Skip to content

Commit 9cb9077

Browse files
committed
fix(deps): add release dependency e2e gate
1 parent a7569a0 commit 9cb9077

11 files changed

Lines changed: 921 additions & 15 deletions
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
name: Dependency Policy Gate
2+
3+
on:
4+
pull_request:
5+
push:
6+
7+
jobs:
8+
dependency-policy-gate:
9+
runs-on: ubuntu-latest
10+
steps:
11+
- uses: actions/checkout@v4
12+
with:
13+
persist-credentials: false
14+
- name: Set up Python
15+
uses: actions/setup-python@v5
16+
with:
17+
python-version: "3.10"
18+
- name: Install policy check dependencies
19+
run: python3 -m pip install packaging 'tomli; python_version<"3.11"'
20+
- name: Check runtime dependency policy
21+
run: python3 scripts/check_dependency_e2e.py policy

‎.github/workflows/publish-tag-to-pypi.yaml‎

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,13 @@ jobs:
2222
- name: Set up Python
2323
uses: actions/setup-python@v5
2424
with:
25-
python-version: "3.x"
26-
- name: Install build tool
27-
run: python3 -m pip install build --user --upgrade
25+
python-version: "3.10"
26+
- name: Install build tools
27+
run: python3 -m pip install build packaging 'tomli; python_version<"3.11"' --user --upgrade
2828
- name: Build wheel and source distribution
2929
run: python3 -m build
30+
- name: Check runtime dependency policy
31+
run: python3 scripts/check_dependency_e2e.py policy
3032
- name: Verify native AgentKit CLI archives stay outside Python distributions
3133
run: |
3234
python3 - <<'PY'
@@ -67,6 +69,12 @@ jobs:
6769
echo "Distribution exceeds PyPI's 100 MiB limit: ${oversized_file} (${bytes} bytes)"
6870
exit 1
6971
fi
72+
- name: Verify clean wheel install
73+
if: startsWith(github.ref, 'refs/tags/') || github.event_name == 'workflow_dispatch'
74+
run: python3 scripts/check_dependency_e2e.py clean-install
75+
- name: Verify high-risk dependency upgrades against runtime pins
76+
if: startsWith(github.ref, 'refs/tags/') || github.event_name == 'workflow_dispatch'
77+
run: python3 scripts/check_dependency_e2e.py declared-range
7078
- name: Upload distributables as artifact
7179
uses: actions/upload-artifact@v4
7280
with:

‎.pre-commit-config.yaml‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,12 @@ repos:
1616
- id: gitleaks
1717
- repo: local
1818
hooks:
19+
- id: dependency-policy-gate
20+
name: Check runtime dependency policy
21+
entry: python3 scripts/check_dependency_e2e.py policy
22+
language: system
23+
pass_filenames: false
24+
files: ^pyproject\.toml$
1925
- id: yaml-secret-scan
2026
name: Detect concrete secrets in YAML
2127
entry: scripts/scan_yaml_secrets.py

‎AGENTS.md‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,16 @@ Pure documentation, translation, or formatting changes that preserve runtime beh
3232
- Keep real credentials, signed URLs, personal data, and unredacted production logs out of source, design documents, reports, test fixtures, and generated artifacts.
3333
- 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.
3434

35+
## Dependency metadata guidelines
36+
37+
- 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.
38+
- 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.
39+
- 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.
40+
- 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.
41+
- 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.
42+
- 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.
43+
- 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.
44+
3545
## Verification gates
3646

3747
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.
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
# Dependency E2E Release Gate
2+
3+
[中文版](2026-10-13-dependency-e2e-release-gate.zh.md)
4+
5+
| Field | Value |
6+
| --- | --- |
7+
| Change ID | `dependency-e2e-release-gate` |
8+
| Created | 2026-10-13 |
9+
| Status | approved |
10+
| Component specs | No component contract impact; this change only adds release-time validation for package metadata and dependency resolution. |
11+
12+
## Background
13+
14+
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.
15+
16+
## Goals and non-goals
17+
18+
| Type | Scope |
19+
| --- | --- |
20+
| Goal | Block PyPI publication when the built wheel cannot be installed in a clean environment. |
21+
| Goal | Expose high-risk direct dependency upgrades that conflict with current runtime pins or other package metadata. |
22+
| Goal | Reject high-risk unbounded runtime dependencies and exact pins for shared OpenTelemetry packages. |
23+
| Non-goal | Exhaustively test every optional extra, Studio/docker/sandbox runtime, or real cloud service. |
24+
25+
## Requirements
26+
27+
| ID | Requirement |
28+
| --- | --- |
29+
| 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. |
30+
| 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. |
31+
| FR-3 | The policy check must fail for unbounded `google-adk` and exact `opentelemetry-*` runtime pins. |
32+
| FR-4 | The checks must run on the release artifact, not the developer `uv.lock` environment. |
33+
34+
## Design
35+
36+
| Part | Decision |
37+
| --- | --- |
38+
| Script | Add `scripts/check_dependency_e2e.py` with `policy`, `clean-install`, and `declared-range` commands. |
39+
| 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. |
40+
| 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. |
41+
| 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. |
42+
| Runtime smoke | Import `veadk`, `google.adk`, and `opentelemetry` only; no credentials or external service calls. |
43+
44+
## Tasks
45+
46+
| ID | Work | Requirement |
47+
| --- | --- | --- |
48+
| T-1 | Add the dependency E2E script. | FR-1, FR-2, FR-3, FR-4 |
49+
| T-2 | Add unit tests for dependency policy and highest-version selection. | FR-2, FR-3 |
50+
| T-3 | Wire the script into the tag publish workflow and PR-visible dependency policy gate. | FR-1, FR-2, FR-4 |
51+
| T-5 | Run the policy check from pre-commit for dependency metadata edits. | FR-3 |
52+
| T-6 | Document repository-level dependency metadata guidelines for developers and coding agents. | FR-2, FR-3 |
53+
| T-4 | Adjust runtime dependency metadata if the new checks expose an existing conflict. | FR-2, FR-3 |
54+
55+
## Acceptance
56+
57+
| Requirement | Task | Criterion | Verification | Result |
58+
| --- | --- | --- | --- | --- |
59+
| 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 |
60+
| 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 |
61+
| 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 |
62+
| FR-4 | T-3 | Workflow checks `dist/*.whl` instead of `uv.lock`. | workflow diff review | pass |
63+
| 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 |
64+
| 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 |
65+
66+
## Risks
67+
68+
| Risk | Mitigation |
69+
| --- | --- |
70+
| PyPI state can change after release. | Keep the gate on tag publish and optionally run it manually before tagging. |
71+
| 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. |
72+
73+
## Review record
74+
75+
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.
76+
77+
## Verification record
78+
79+
| Date | Command | Result | Notes |
80+
| --- | --- | --- | --- |
81+
| 2026-10-13 | `uv lock` | pass | Refreshed lock metadata after dependency bound updates. |
82+
| 2026-10-13 | `uv run --extra dev pytest tests/test_dependency_e2e.py` | pass | 11 tests passed. |
83+
| 2026-10-13 | `uv run python scripts/check_dependency_e2e.py policy` | pass | Runtime dependency policy passed. |
84+
| 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`. |
85+
| 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. |
86+
87+
## Dependency metadata adjustments found by the E2E check
88+
89+
| Dependency | Before | After | Reason |
90+
| --- | --- | --- | --- |
91+
| `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. |
92+
| `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. |
93+
| `fastmcp` | `>=2.12.3` | `>=2.12.3,<4` | `fastmcp 4.x` requires `mcp>=2`, while VeADK currently declares `mcp==1.26.0`. |
94+
| `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`. |
95+
| `jsonschema` | `>=4.23,<5` | `>=4.23,<4.24` | `litellm 1.83.x` pins `jsonschema==4.23.0`; the previous range overstated installable versions. |
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
# 依赖 E2E 发布门禁
2+
3+
[English](2026-10-13-dependency-e2e-release-gate.md)
4+
5+
| 字段 | 值 |
6+
| --- | --- |
7+
| 变更 ID | `dependency-e2e-release-gate` |
8+
| 创建日期 | 2026-10-13 |
9+
| 状态 | approved |
10+
| 组件规格 | 无组件契约影响;本变更只增加发布期的包元数据和依赖解析校验。 |
11+
12+
## 背景
13+
14+
本次依赖问题说明:本地 `uv.lock` 可用不代表 PyPI 用户可用。VeADK 可能发布出允许 pip 解析到未适配上游版本的元数据,也可能把共享基础依赖 pin 得过死,导致原本兼容的上游依赖栈无法安装。
15+
16+
## 目标和非目标
17+
18+
| 类型 | 范围 |
19+
| --- | --- |
20+
| 目标 | 发布到 PyPI 前,如果构建出的 wheel 无法在干净环境安装,则阻塞发布。 |
21+
| 目标 | 暴露高风险 direct dependency 升级后与当前 runtime pin 或其它包元数据冲突的问题。 |
22+
| 目标 | 拒绝高风险无上限 runtime 依赖,以及 OpenTelemetry 共享依赖 exact pin。 |
23+
| 非目标 | 不穷举所有 optional extra、Studio/docker/sandbox runtime,且不调用真实云服务。 |
24+
25+
## 需求
26+
27+
| ID | 需求 |
28+
| --- | --- |
29+
| FR-1 | tag 发布 workflow 必须在上传 PyPI 前,将构建出的 wheel 安装到干净 venv,执行 `pip check` 和最小 import smoke。 |
30+
| FR-2 | tag 发布 workflow 必须验证选定高风险 base runtime direct dependency 在 VeADK 声明范围内当前可用最高版本,能否和构建出的 wheel 当前 runtime pin 一起解析。 |
31+
| FR-3 | 策略检查必须拦截无上限 `google-adk` 和 exact pin 的 `opentelemetry-*` runtime 依赖。 |
32+
| FR-4 | 检查必须基于发布产物,而不是开发者的 `uv.lock` 环境。 |
33+
34+
## 设计
35+
36+
| 部分 | 决策 |
37+
| --- | --- |
38+
| 脚本 | 新增 `scripts/check_dependency_e2e.py`,提供 `policy`、`clean-install`、`declared-range` 命令。 |
39+
| Workflow | 在 PR 和 push 上暴露独立的 `dependency-policy-gate`;clean install 和 declared-range 重检查只在 tag 发布或手动 workflow dispatch 时运行。 |
40+
| 依赖范围 | 只检查选定高风险 base 依赖:`google-adk`、`agentkit-sdk-python`、OpenTelemetry packages、`litellm`、`mcp`、`fastmcp`、`tos` 和 `jsonschema`;每个候选升级都会和完整 wheel metadata 一起解析,包括精确 runtime pin。optional extras 后续再扩展。 |
41+
| 开发规范 | 增加仓库级 dependency metadata 规范,要求开发者和 coding agent 在重 E2E 发布门禁前先规避高风险依赖无上限、无理由 shared base pin、只依赖 `uv.lock` 验证等问题。 |
42+
| Runtime smoke | 只 import `veadk`、`google.adk`、`opentelemetry`;不需要凭据,也不访问外部服务。 |
43+
44+
## 任务
45+
46+
| ID | 工作 | 需求 |
47+
| --- | --- | --- |
48+
| T-1 | 新增依赖 E2E 脚本。 | FR-1, FR-2, FR-3, FR-4 |
49+
| T-2 | 增加依赖策略和最高版本选择的单测。 | FR-2, FR-3 |
50+
| T-3 | 将脚本接入 tag 发布 workflow 和 PR 可见的 dependency policy gate。 | FR-1, FR-2, FR-4 |
51+
| T-5 | 在 pre-commit 中对依赖元数据改动执行 policy 检查。 | FR-3 |
52+
| T-6 | 为开发者和 coding agent 记录仓库级 dependency metadata 规范。 | FR-2, FR-3 |
53+
| T-4 | 如果新检查暴露已有依赖冲突,则修正 runtime dependency metadata。 | FR-2, FR-3 |
54+
55+
## 验收
56+
57+
| 需求 | 任务 | 标准 | 验证 | 结果 |
58+
| --- | --- | --- | --- | --- |
59+
| FR-1 | T-1, T-3 | PyPI 上传前存在 clean wheel install 门禁。 | `uv run --extra dev pytest tests/test_dependency_e2e.py` | pass |
60+
| 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 |
61+
| FR-3 | T-1, T-2, T-4 | `google-adk` 必须有上限,`opentelemetry-*` 不允许 exact pin。 | `uv run python scripts/check_dependency_e2e.py policy` | pass |
62+
| FR-4 | T-3 | Workflow 检查 `dist/*.whl`,不依赖 `uv.lock`。 | workflow diff review | pass |
63+
| FR-3 | T-5 | 修改 `pyproject.toml` 时,本地 pre-commit 会执行同一 runtime dependency policy 检查。 | `uv run --extra dev pre-commit run --all-files` | pass |
64+
| FR-2, FR-3 | T-6 | `AGENTS.md` 已记录高风险依赖上限、shared base pin、直接依赖声明和重 E2E 延后执行等 dependency metadata 规则。 | guideline diff review | pass |
65+
66+
## 风险
67+
68+
| 风险 | 缓解 |
69+
| --- | --- |
70+
| PyPI 状态在发布后仍可能变化。 | tag 发布时运行门禁,必要时也可手动预检。 |
71+
| 最高版本安装检查比普通单测慢。 | 只检查选定高风险依赖,且重检查只在 tag 发布或手动 workflow dispatch 时运行。 |
72+
73+
## 评审记录
74+
75+
用户在会话中审阅 E2E 方案后批准开始实现。直接评审结论:本变更不改变公开 SDK 行为和运行时契约,因此无需更新组件 spec。
76+
77+
## 验证记录
78+
79+
| 日期 | 命令 | 结果 | 说明 |
80+
| --- | --- | --- | --- |
81+
| 2026-10-13 | `uv lock` | pass | 依赖边界调整后刷新 lock metadata。 |
82+
| 2026-10-13 | `uv run --extra dev pytest tests/test_dependency_e2e.py` | pass | 11 个测试通过。 |
83+
| 2026-10-13 | `uv run python scripts/check_dependency_e2e.py policy` | pass | Runtime dependency policy 通过。 |
84+
| 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`。 |
85+
| 2026-10-13 | `uv run python scripts/check_dependency_e2e.py --dist-dir <tmp-dist> clean-install` | blocked | 本地下载 `google-adk` 过慢后中断;workflow 已接入该门禁,会在 GitHub Actions 上传前执行。 |
86+
87+
## E2E 暴露出的依赖元数据调整
88+
89+
| 依赖 | 调整前 | 调整后 | 原因 |
90+
| --- | --- | --- | --- |
91+
| `opentelemetry-exporter-otlp` | `>=1.37.0` | `>=1.37.0,<1.43` | `google-adk 2.7.x` 将 OpenTelemetry SDK/API 限制在 1.42 线。 |
92+
| `opentelemetry-instrumentation-logging` | `>=0.56b0` | `>=0.56b0,<0.64` | 0.64+ instrumentation 会解析到 `google-adk 2.7.x` 不允许的新版 OpenTelemetry 包。 |
93+
| `fastmcp` | `>=2.12.3` | `>=2.12.3,<4` | `fastmcp 4.x` 要求 `mcp>=2`,但 VeADK 当前声明 `mcp==1.26.0`。 |
94+
| `tos` | `>=2.8.4` | `>=2.8.4,<2.9` | `tos 2.9.x` pin 了 `wrapt==1.16.0`,与 VeADK 的 `wrapt==1.17.2` 冲突。 |
95+
| `jsonschema` | `>=4.23,<5` | `>=4.23,<4.24` | `litellm 1.83.x` pin 了 `jsonschema==4.23.0`;原范围夸大了可安装版本。 |

0 commit comments

Comments
 (0)