Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions .github/workflows/dependency-policy-gate.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
name: Dependency Policy Gate

on:
pull_request:
push:

jobs:
dependency-policy-gate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.10"
- name: Install policy check dependencies
run: python3 -m pip install packaging 'tomli; python_version<"3.11"'
- name: Check runtime dependency policy
run: python3 scripts/check_dependency_e2e.py policy
14 changes: 11 additions & 3 deletions .github/workflows/publish-tag-to-pypi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,13 @@ jobs:
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.x"
- name: Install build tool
run: python3 -m pip install build --user --upgrade
python-version: "3.10"
- name: Install build tools
run: python3 -m pip install build packaging 'tomli; python_version<"3.11"' --user --upgrade
- name: Build wheel and source distribution
run: python3 -m build
- name: Check runtime dependency policy
run: python3 scripts/check_dependency_e2e.py policy
- name: Verify native AgentKit CLI archives stay outside Python distributions
run: |
python3 - <<'PY'
Expand Down Expand Up @@ -67,6 +69,12 @@ jobs:
echo "Distribution exceeds PyPI's 100 MiB limit: ${oversized_file} (${bytes} bytes)"
exit 1
fi
- name: Verify clean wheel install
if: startsWith(github.ref, 'refs/tags/') || github.event_name == 'workflow_dispatch'
run: python3 scripts/check_dependency_e2e.py clean-install
- name: Verify high-risk dependency upgrades against runtime pins
if: startsWith(github.ref, 'refs/tags/') || github.event_name == 'workflow_dispatch'
run: python3 scripts/check_dependency_e2e.py declared-range
- name: Upload distributables as artifact
uses: actions/upload-artifact@v4
with:
Expand Down
6 changes: 6 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@ repos:
- id: gitleaks
- repo: local
hooks:
- id: dependency-policy-gate
name: Check runtime dependency policy
entry: python3 scripts/check_dependency_e2e.py policy
language: system
pass_filenames: false
files: ^pyproject\.toml$
- id: yaml-secret-scan
name: Detect concrete secrets in YAML
entry: scripts/scan_yaml_secrets.py
Expand Down
10 changes: 10 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,16 @@ Pure documentation, translation, or formatting changes that preserve runtime beh
- Keep real credentials, signed URLs, personal data, and unredacted production logs out of source, design documents, reports, test fixtures, and generated artifacts.
- Isolate tests from real user state and external services by default. Real cloud/provider operations need explicit authorization and an isolated smoke/E2E procedure; report simulated and live verification separately.

## Dependency metadata guidelines

- Do not treat `uv.lock` success as proof that published package metadata is installable. PyPI users resolve from `pyproject.toml` metadata, not this repository's lock file.
- When changing runtime dependencies in `pyproject.toml`, consider both direct use and shared transitive constraints from key upstream packages such as `google-adk`, `agentkit-sdk-python`, OpenTelemetry, `mcp`, `fastmcp`, `litellm`, and storage/cloud SDKs.
- High-risk upstream dependencies must declare an explicit tested upper bound. Do not leave them open-ended when an unadapted future version could break installation or runtime behavior.
- Avoid exact pins for shared base dependencies unless there is a concrete compatibility reason. If an exact pin is necessary, document the reason in the dependency comment or the change PRD.
- Before widening a high-risk dependency range, check whether the highest allowed version still resolves with the current runtime pins. Use `scripts/check_dependency_e2e.py declared-range` or the release workflow's manual dispatch when the check is too heavy for ordinary PR iteration.
- If a dependency upgrade only works by relying on another package's current transitive dependency, declare the required direct dependency explicitly in `pyproject.toml` rather than depending on accidental resolver behavior.
- If a heavy dependency E2E check is intentionally not run before merge, record the risk and the expected tag/manual workflow coverage in the PRD or PR description.

## Verification gates

Use the repository-local Python environment and current `pyproject.toml`, `pytest.ini`, package scripts, and CI workflows as command references. Do not invent `make` targets, copy another repository's coverage thresholds, or treat an unavailable check as passing.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
# Dependency E2E Release Gate

[中文版](2026-10-13-dependency-e2e-release-gate.zh.md)

| Field | Value |
| --- | --- |
| Change ID | `dependency-e2e-release-gate` |
| Created | 2026-10-13 |
| Status | approved |
| Component specs | No component contract impact; this change only adds release-time validation for package metadata and dependency resolution. |

## Background

The dependency incident showed that local `uv.lock` success is not enough for PyPI users. VeADK can publish metadata that lets pip resolve an unsupported upstream version, or pin a shared base dependency so tightly that a compatible upstream stack cannot be installed.

## Goals and non-goals

| Type | Scope |
| --- | --- |
| Goal | Block PyPI publication when the built wheel cannot be installed in a clean environment. |
| Goal | Expose high-risk direct dependency upgrades that conflict with current runtime pins or other package metadata. |
| Goal | Reject high-risk unbounded runtime dependencies and exact pins for shared OpenTelemetry packages. |
| Non-goal | Exhaustively test every optional extra, Studio/docker/sandbox runtime, or real cloud service. |

## Requirements

| ID | Requirement |
| --- | --- |
| FR-1 | The tag publish workflow must install the built wheel in a clean venv, run `pip check`, and run a minimal import smoke before uploading to PyPI. |
| FR-2 | The tag publish workflow must test selected high-risk base runtime direct dependencies at their highest currently available versions inside VeADK's declared ranges, together with the built wheel's current runtime pins. |
| FR-3 | The policy check must fail for unbounded `google-adk` and exact `opentelemetry-*` runtime pins. |
| FR-4 | The checks must run on the release artifact, not the developer `uv.lock` environment. |

## Design

| Part | Decision |
| --- | --- |
| Script | Add `scripts/check_dependency_e2e.py` with `policy`, `clean-install`, and `declared-range` commands. |
| Workflow | Expose a standalone `dependency-policy-gate` on PRs and pushes; run clean install and declared-range checks only for tag publication or manual workflow dispatch. |
| Dependency scope | Check selected high-risk base dependencies: `google-adk`, `agentkit-sdk-python`, OpenTelemetry packages, `litellm`, `mcp`, `fastmcp`, `tos`, and `jsonschema`; each candidate upgrade is resolved against the full wheel metadata, including exact runtime pins. Optional extras are deferred. |
| Developer guidance | Add repository-level dependency metadata guidelines so developers and coding agents avoid unbounded high-risk dependencies, unjustified shared base pins, and `uv.lock`-only validation before the heavier release E2E runs. |
| Runtime smoke | Import `veadk`, `google.adk`, and `opentelemetry` only; no credentials or external service calls. |

## Tasks

| ID | Work | Requirement |
| --- | --- | --- |
| T-1 | Add the dependency E2E script. | FR-1, FR-2, FR-3, FR-4 |
| T-2 | Add unit tests for dependency policy and highest-version selection. | FR-2, FR-3 |
| T-3 | Wire the script into the tag publish workflow and PR-visible dependency policy gate. | FR-1, FR-2, FR-4 |
| T-5 | Run the policy check from pre-commit for dependency metadata edits. | FR-3 |
| T-6 | Document repository-level dependency metadata guidelines for developers and coding agents. | FR-2, FR-3 |
| T-4 | Adjust runtime dependency metadata if the new checks expose an existing conflict. | FR-2, FR-3 |

## Acceptance

| Requirement | Task | Criterion | Verification | Result |
| --- | --- | --- | --- | --- |
| FR-1 | T-1, T-3 | Clean wheel install gate exists before PyPI upload. | `uv run --extra dev pytest tests/test_dependency_e2e.py` | pass |
| FR-2 | T-1, T-2, T-3 | Selected high-risk direct dependency highest-version checks are implemented and unit-tested against current runtime pins. | `uv run --extra dev pytest tests/test_dependency_e2e.py`; `uv run python scripts/check_dependency_e2e.py --dist-dir <tmp-dist> declared-range` | pass |
| FR-3 | T-1, T-2, T-4 | `google-adk` requires an upper bound and `opentelemetry-*` exact pins are rejected. | `uv run python scripts/check_dependency_e2e.py policy` | pass |
| FR-4 | T-3 | Workflow checks `dist/*.whl` instead of `uv.lock`. | workflow diff review | pass |
| FR-3 | T-5 | Local pre-commit runs the same runtime dependency policy check when `pyproject.toml` changes. | `uv run --extra dev pre-commit run --all-files` | pass |
| FR-2, FR-3 | T-6 | `AGENTS.md` documents dependency metadata rules for high-risk upper bounds, shared base pins, direct dependency declarations, and heavyweight E2E deferral. | guideline diff review | pass |

## Risks

| Risk | Mitigation |
| --- | --- |
| PyPI state can change after release. | Keep the gate on tag publish and optionally run it manually before tagging. |
| Highest-version installs may be slower than normal unit tests. | Check only selected high-risk dependencies and run the heavy checks only on tag publication or manual workflow dispatch. |

## Review record

User approved implementation after reviewing the E2E plan in chat. Direct review found no component-spec change because public SDK behavior and runtime contracts are unchanged.

## Verification record

| Date | Command | Result | Notes |
| --- | --- | --- | --- |
| 2026-10-13 | `uv lock` | pass | Refreshed lock metadata after dependency bound updates. |
| 2026-10-13 | `uv run --extra dev pytest tests/test_dependency_e2e.py` | pass | 11 tests passed. |
| 2026-10-13 | `uv run python scripts/check_dependency_e2e.py policy` | pass | Runtime dependency policy passed. |
| 2026-10-13 | `uv run python scripts/check_dependency_e2e.py --dist-dir <tmp-dist> declared-range` | pass | Validated selected high-risk dependency pins including `google-adk==2.7.1`, `opentelemetry-exporter-otlp==1.42.1`, `opentelemetry-instrumentation-logging==0.63b1`, `fastmcp==3.4.8`, `tos==2.8.7`, and `jsonschema==4.23.0`. |
| 2026-10-13 | `uv run python scripts/check_dependency_e2e.py --dist-dir <tmp-dist> clean-install` | blocked | Local PyPI download for `google-adk` was too slow and was interrupted; the workflow gate remains wired to run this in GitHub Actions before upload. |

## Dependency metadata adjustments found by the E2E check

| Dependency | Before | After | Reason |
| --- | --- | --- | --- |
| `opentelemetry-exporter-otlp` | `>=1.37.0` | `>=1.37.0,<1.43` | `google-adk 2.7.x` caps OpenTelemetry SDK/API at the 1.42 line. |
| `opentelemetry-instrumentation-logging` | `>=0.56b0` | `>=0.56b0,<0.64` | The 0.64+ instrumentation line resolves with newer OpenTelemetry packages than `google-adk 2.7.x` allows. |
| `fastmcp` | `>=2.12.3` | `>=2.12.3,<4` | `fastmcp 4.x` requires `mcp>=2`, while VeADK currently declares `mcp==1.26.0`. |
| `tos` | `>=2.8.4` | `>=2.8.4,<2.9` | `tos 2.9.x` pins `wrapt==1.16.0`, conflicting with VeADK's `wrapt==1.17.2`. |
| `jsonschema` | `>=4.23,<5` | `>=4.23,<4.24` | `litellm 1.83.x` pins `jsonschema==4.23.0`; the previous range overstated installable versions. |
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
# 依赖 E2E 发布门禁

[English](2026-10-13-dependency-e2e-release-gate.md)

| 字段 | 值 |
| --- | --- |
| 变更 ID | `dependency-e2e-release-gate` |
| 创建日期 | 2026-10-13 |
| 状态 | approved |
| 组件规格 | 无组件契约影响;本变更只增加发布期的包元数据和依赖解析校验。 |

## 背景

本次依赖问题说明:本地 `uv.lock` 可用不代表 PyPI 用户可用。VeADK 可能发布出允许 pip 解析到未适配上游版本的元数据,也可能把共享基础依赖 pin 得过死,导致原本兼容的上游依赖栈无法安装。

## 目标和非目标

| 类型 | 范围 |
| --- | --- |
| 目标 | 发布到 PyPI 前,如果构建出的 wheel 无法在干净环境安装,则阻塞发布。 |
| 目标 | 暴露高风险 direct dependency 升级后与当前 runtime pin 或其它包元数据冲突的问题。 |
| 目标 | 拒绝高风险无上限 runtime 依赖,以及 OpenTelemetry 共享依赖 exact pin。 |
| 非目标 | 不穷举所有 optional extra、Studio/docker/sandbox runtime,且不调用真实云服务。 |

## 需求

| ID | 需求 |
| --- | --- |
| FR-1 | tag 发布 workflow 必须在上传 PyPI 前,将构建出的 wheel 安装到干净 venv,执行 `pip check` 和最小 import smoke。 |
| FR-2 | tag 发布 workflow 必须验证选定高风险 base runtime direct dependency 在 VeADK 声明范围内当前可用最高版本,能否和构建出的 wheel 当前 runtime pin 一起解析。 |
| FR-3 | 策略检查必须拦截无上限 `google-adk` 和 exact pin 的 `opentelemetry-*` runtime 依赖。 |
| FR-4 | 检查必须基于发布产物,而不是开发者的 `uv.lock` 环境。 |

## 设计

| 部分 | 决策 |
| --- | --- |
| 脚本 | 新增 `scripts/check_dependency_e2e.py`,提供 `policy`、`clean-install`、`declared-range` 命令。 |
| Workflow | 在 PR 和 push 上暴露独立的 `dependency-policy-gate`;clean install 和 declared-range 重检查只在 tag 发布或手动 workflow dispatch 时运行。 |
| 依赖范围 | 只检查选定高风险 base 依赖:`google-adk`、`agentkit-sdk-python`、OpenTelemetry packages、`litellm`、`mcp`、`fastmcp`、`tos` 和 `jsonschema`;每个候选升级都会和完整 wheel metadata 一起解析,包括精确 runtime pin。optional extras 后续再扩展。 |
| 开发规范 | 增加仓库级 dependency metadata 规范,要求开发者和 coding agent 在重 E2E 发布门禁前先规避高风险依赖无上限、无理由 shared base pin、只依赖 `uv.lock` 验证等问题。 |
| Runtime smoke | 只 import `veadk`、`google.adk`、`opentelemetry`;不需要凭据,也不访问外部服务。 |

## 任务

| ID | 工作 | 需求 |
| --- | --- | --- |
| T-1 | 新增依赖 E2E 脚本。 | FR-1, FR-2, FR-3, FR-4 |
| T-2 | 增加依赖策略和最高版本选择的单测。 | FR-2, FR-3 |
| T-3 | 将脚本接入 tag 发布 workflow 和 PR 可见的 dependency policy gate。 | FR-1, FR-2, FR-4 |
| T-5 | 在 pre-commit 中对依赖元数据改动执行 policy 检查。 | FR-3 |
| T-6 | 为开发者和 coding agent 记录仓库级 dependency metadata 规范。 | FR-2, FR-3 |
| T-4 | 如果新检查暴露已有依赖冲突,则修正 runtime dependency metadata。 | FR-2, FR-3 |

## 验收

| 需求 | 任务 | 标准 | 验证 | 结果 |
| --- | --- | --- | --- | --- |
| FR-1 | T-1, T-3 | PyPI 上传前存在 clean wheel install 门禁。 | `uv run --extra dev pytest tests/test_dependency_e2e.py` | pass |
| FR-2 | T-1, T-2, T-3 | 已实现并单测覆盖选定高风险 direct dependency 最高声明版本与当前 runtime pin 的兼容检查。 | `uv run --extra dev pytest tests/test_dependency_e2e.py`;`uv run python scripts/check_dependency_e2e.py --dist-dir <tmp-dist> declared-range` | pass |
| FR-3 | T-1, T-2, T-4 | `google-adk` 必须有上限,`opentelemetry-*` 不允许 exact pin。 | `uv run python scripts/check_dependency_e2e.py policy` | pass |
| FR-4 | T-3 | Workflow 检查 `dist/*.whl`,不依赖 `uv.lock`。 | workflow diff review | pass |
| FR-3 | T-5 | 修改 `pyproject.toml` 时,本地 pre-commit 会执行同一 runtime dependency policy 检查。 | `uv run --extra dev pre-commit run --all-files` | pass |
| FR-2, FR-3 | T-6 | `AGENTS.md` 已记录高风险依赖上限、shared base pin、直接依赖声明和重 E2E 延后执行等 dependency metadata 规则。 | guideline diff review | pass |

## 风险

| 风险 | 缓解 |
| --- | --- |
| PyPI 状态在发布后仍可能变化。 | tag 发布时运行门禁,必要时也可手动预检。 |
| 最高版本安装检查比普通单测慢。 | 只检查选定高风险依赖,且重检查只在 tag 发布或手动 workflow dispatch 时运行。 |

## 评审记录

用户在会话中审阅 E2E 方案后批准开始实现。直接评审结论:本变更不改变公开 SDK 行为和运行时契约,因此无需更新组件 spec。

## 验证记录

| 日期 | 命令 | 结果 | 说明 |
| --- | --- | --- | --- |
| 2026-10-13 | `uv lock` | pass | 依赖边界调整后刷新 lock metadata。 |
| 2026-10-13 | `uv run --extra dev pytest tests/test_dependency_e2e.py` | pass | 11 个测试通过。 |
| 2026-10-13 | `uv run python scripts/check_dependency_e2e.py policy` | pass | Runtime dependency policy 通过。 |
| 2026-10-13 | `uv run python scripts/check_dependency_e2e.py --dist-dir <tmp-dist> declared-range` | pass | 已验证选定高风险依赖的声明范围最高版本,包括 `google-adk==2.7.1`、`opentelemetry-exporter-otlp==1.42.1`、`opentelemetry-instrumentation-logging==0.63b1`、`fastmcp==3.4.8`、`tos==2.8.7` 和 `jsonschema==4.23.0`。 |
| 2026-10-13 | `uv run python scripts/check_dependency_e2e.py --dist-dir <tmp-dist> clean-install` | blocked | 本地下载 `google-adk` 过慢后中断;workflow 已接入该门禁,会在 GitHub Actions 上传前执行。 |

## E2E 暴露出的依赖元数据调整

| 依赖 | 调整前 | 调整后 | 原因 |
| --- | --- | --- | --- |
| `opentelemetry-exporter-otlp` | `>=1.37.0` | `>=1.37.0,<1.43` | `google-adk 2.7.x` 将 OpenTelemetry SDK/API 限制在 1.42 线。 |
| `opentelemetry-instrumentation-logging` | `>=0.56b0` | `>=0.56b0,<0.64` | 0.64+ instrumentation 会解析到 `google-adk 2.7.x` 不允许的新版 OpenTelemetry 包。 |
| `fastmcp` | `>=2.12.3` | `>=2.12.3,<4` | `fastmcp 4.x` 要求 `mcp>=2`,但 VeADK 当前声明 `mcp==1.26.0`。 |
| `tos` | `>=2.8.4` | `>=2.8.4,<2.9` | `tos 2.9.x` pin 了 `wrapt==1.16.0`,与 VeADK 的 `wrapt==1.17.2` 冲突。 |
| `jsonschema` | `>=4.23,<5` | `>=4.23,<4.24` | `litellm 1.83.x` pin 了 `jsonschema==4.23.0`;原范围夸大了可安装版本。 |
Loading
Loading