Skip to content

Harden the release pipeline: dry-run dispatch, tested artifacts, strict smoke, TestPyPI check - #13

Merged
DABH merged 2 commits into
mainfrom
release/pipeline-hardening
Sep 10, 2026
Merged

DABH merged 2 commits into
mainfrom
release/pipeline-hardening

Conversation

@DABH

@DABH DABH commented Sep 10, 2026 •

Copy link
Copy Markdown
Collaborator

Addresses the actionable items of #12 before the first tag.

Merged at fbde27c with the first review round's fixes. The second round's fixes (no draft after a failed PyPI publish, newest-published re-run warning, two-way and yanked-aware verify-index-files, published-release guard, lane-qualified artifacts with overwrite: true) landed separately in #15.

Changes

  • Dry run. workflow_dispatch gains tag and skip-publish. Dispatching on main with -f tag=python/<name>/v<version> runs tag parsing, the manifest check, the version policy, the full test matrix and the artifact build; nothing is uploaded, nothing is drafted, no tag is consumed. A dispatch from a branch can never publish (github.ref_type != 'tag'); skip-publish additionally turns a dispatch on a tag ref into a dry run, and only the literal false publishes. prepare rejects a dispatch on a tag ref whose tag input names a different tag, and a branch dispatch without a tag input.
  • Published bytes are the tested bytes. The build job is removed; publish, smoke and release jobs use dist-<plugin>-locked, the artifact that _python-plugin.yml's ubuntu dist cell built, verified with check_wheel.py and smoke-tested in isolation. Artifact names carry the dependency lane.
  • The index serves those bytes. Both smoke jobs run verify-index-files before installing: every local file must be served with an identical sha256. uv_build is reproducible, so a legitimate re-run passes.
  • Recovery stays possible. A version already staged on TestPyPI is a warning behind --check-testpypi, not a failure; "Re-run failed jobs" keeps prepare's outputs and the tested artifact.
  • Strict clean-project smoke. allow_final is a prepare output and both smoke jobs set ALLOW_OVERLAP_WITH_CORE from it: overlap with the SDK is tolerated only while allow-final = false, on TestPyPI as well as PyPI.
  • draft-release refuses to modify an already published release and uploads assets with gh release upload --clobber; the release job works from prepare's parsed tag rather than github.ref_name.
  • Fail closed, small hardening. transition_markers() raises on any git grep failure and on a missing git; the release checkout sets persist-credentials: false; first-release notes link tree/refs/tags/<tag>/<dir>; the SDK-overlap warning in pre-release notes appears only for plugins the SDK still bundles; per-tag concurrency group; !cancelled() on the release job.
  • AGENTS.md runbook updated: version bump, dry run, tag, pipeline, hand-publish, recovery.

Not in this PR (from #12): repo visibility (decision), PyPI project owners (after the first upload), the 1.0.0rc2 release PR (after #6), adapter test coverage (#6).

Testing

  • uv run --project scripts --locked pytest scripts/tests -q: 79 passed at the merged head; the policy tests no longer touch the network.
  • scripts/ci/check_conventions.py: OK
  • First real exercise once merged: gh workflow run release-python.yml --ref main -f tag=python/openai_agents/v1.0.0rc1.

…ifacts, strict smoke, TestPyPI check

release-python.yml gains a dry run: dispatch on a branch with the tag input naming the intended tag and skip-publish on, and the tag validation, version policy, full test matrix and artifact build run without uploading anything or consuming a tag. Uploads and the draft release now require a real tag ref.

The separate build job is gone: the test job's ubuntu dist cell already builds, verifies and smoke-tests the wheel and sdist, so the publish and release jobs download that dist-<plugin> artifact and the published bytes are the tested bytes.

The clean-project smoke installs are strict unless plugin.toml allow-final is false (the only case where the SDK still ships the same files), on TestPyPI as well as PyPI; allow_final is a prepare output.

check-version-policy also refuses a version that already exists on TestPyPI, because uploads are immutable and skip-existing would otherwise hide the failure while the smoke test validated stale bytes. transition_markers() fails closed on git errors instead of reporting no markers. The release checkout no longer persists the write token, assets are uploaded with gh release upload --clobber, first-release notes link tree/refs/tags/<tag>/<dir>, and the SDK-overlap warning appears in pre-release notes only for plugins the SDK still bundles.
@DABH
DABH requested a review from a team as a code owner September 10, 2026 16:52
@DABH
DABH requested a review from brianstrauch September 10, 2026 16:53
…spatch inputs

Self-review of the hardening PR found that a hard failure on a version already staged on TestPyPI removed the only recovery path for a failed final release (fix-forward cannot produce a new 1.0.0, and a fresh dispatch or re-run of all jobs would trip the check). The staged check is now a warning behind --check-testpypi, and the real guarantee moves to where it belongs: both smoke jobs run verify-index-files, which fails unless the index serves exactly the sha256 of the artifacts this run built (uv_build is reproducible, so a legitimate re-run passes and only foreign bytes fail).

A dispatch on a tag ref with a different tag input could publish plugin A while rewriting plugin B's release: prepare now rejects that, requires the tag input for branch dispatches, and exports the parsed tag so the release job never reads github.ref_name. The publish gate compares skip-publish against the literal false, so a malformed value is a dry run rather than an upload. Artifacts are named per lane (dist-<plugin>-<deps>) and the release consumes -locked. draft-release refuses to modify a release that is already published. transition_markers fails closed when git is missing. github-release uses !cancelled(), and the concurrency group is per tag so dry runs do not queue behind each other.

The policy tests no longer reach the network, and the workflow tests pin the smoke strictness expressions, the dist-cell ordering and the dispatch guard. The runbook now puts the dry run between the version bump and the tag push.
@DABH
DABH merged commit f6936fa into main Sep 10, 2026
13 checks passed
@DABH
DABH deleted the release/pipeline-hardening branch September 10, 2026 17:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants