ci: publish Anton staging release candidates (ENG-1159) - #298
Conversation
pnewsam
left a comment
There was a problem hiding this comment.
Code Review
Verdict: REQUEST CHANGES
The core producer design is sound, but this head is not merge-ready. It is 36 commits behind staging, GitHub reports it as non-mergeable, and the conflict in .github/workflows/tests.yml overlaps meaningful changes: current staging added uv lock --check and moved development dependencies from --extra dev to --group dev. Rebase and preserve those current test steps while adding workflow_call and removing the standalone push notification.
The PyPI Trusted Publisher for publish-staging.yml with environment pypi also needs to be configured before merge, because the merge push immediately enters the tag-and-publish pipeline.
Non-blocking integration caveat: cowork-server still selects the most recently uploaded Anton version and pins it only when that version is an RC. If ENG-1159 requires strict RC pairing, update the consumer to select the latest RC specifically rather than deferring that change.
Validation
- Inspected all three workflow patches at head
1a9301band the currentstagingversions. - Confirmed the PR CI run passed unit and stub E2E tests.
- Confirmed the branch is 36 commits behind
stagingand conflicts intests.yml. - Did not exercise PyPI OIDC publishing or verify the Trusted Publisher configuration.
Add a staging rc pre-release stream mirroring cowork-server's publish-staging.yml, so cowork-server staging consumes an immutable, versioned anton-agent artifact instead of a mutable git branch or a hand-pinned commit. - publish-staging.yml (new): push to staging -> validate -> PEP 440 rc release (2.YY.M.DD.SEQrcN) -> PyPI trusted publish -> notify. - release.yml: gate the stable stream on the same reusable tests and scope permissions/notify per job, matching cowork-server's publish.yml. - tests.yml: becomes a reusable (workflow_call) called by both publishers and drops the standalone branch-push run + notify, so a push validates once inside its release run tree. Unlike cowork-server, anton has nothing to self-pin (it does not depend on itself), so this needs no anton-agent pin step or version-pretend override: hatch-vcs derives the rc version from the tag on a clean checkout. Requires a one-time PyPI Trusted Publisher registration for publish-staging.yml (environment: pypi) on the anton-agent project before the publish job can succeed. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1a9301b to
549cf3e
Compare
|
Thanks for the review. Addressed the blocking item and confirmed the rest. Rebased onto current
All three workflows pass YAML validation, and PyPI Trusted Publisher for Strict RC pairing (non-blocking): agreed this is a cowork-server consumer change ("latest by upload time, pin only if RC" → "latest RC specifically"). Tracking it as a follow-up rather than folding it into this producer PR. |
The staging CI notify job previously passed
freeze-scoped: ${{ github.ref_name == 'staging' }} (true on staging), so
release-blocking escalation was reserved for the freeze window. Moving the
alert into publish-staging.yml dropped that flag, which would page the eng
channel on every midweek staging failure. This publisher is staging-only, so
hardcode freeze-scoped: true.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Addressed the freeze-scoped comment in
|
lucas-koontz
left a comment
There was a problem hiding this comment.
Read the whole diff at 4cdd4f6, plus calver-release.yml and notify-main-failure.yml against it. The permission grants match the called workflows' declarations exactly, the notify aggregation handles skipped needs correctly, and the recovered lookup keys on this file's path, so the first green run stays quiet. That all checks out.
Nothing blocks. Two notes inline: a version-skew edge when one commit carries two rc tags (with a one-env-var fix), and the two release docs that still describe main as the only publish stream.
The PyPI side is ready: I registered publish-staging.yml (environment pypi) on anton-agent, so the pre-merge gate in the body is cleared. Workflow naming across repos (release.yml here vs publish.yml in cowork-server) is a follow-up I'll take; the rename has to move the trusted-publisher registration in lockstep anyway.
| - name: Build package | ||
| run: uv build |
There was a problem hiding this comment.
issue: If someone clicks 'Re-run all jobs', or dispatches this workflow on an unchanged staging head, calver-release mints a second rc tag on a commit that already has one. The build step asks git which version to build, and git describe answers with the older tag: the run that minted rc2 builds rc1, and PyPI rejects the upload as a duplicate. Pinning the build to the version the release job just minted removes the guesswork (the lines 15-18 header comment would need updating to match). Same edge on release.yml re-runs.
| - name: Build package | |
| run: uv build | |
| - name: Build package | |
| env: | |
| # A re-run or re-dispatch on an already-tagged head leaves two CalVer | |
| # tags on one commit and `git describe` resolves the older one; build | |
| # exactly the version the release job minted. | |
| SETUPTOOLS_SCM_PRETEND_VERSION: ${{ needs.release.outputs.version }} | |
| run: uv build |
| # consumes an immutable, versioned Anton artifact instead of a mutable git branch | ||
| # or a hand-pinned commit (ENG-1159). Resolvers ignore pre-releases unless a | ||
| # specifier names one, and PyPI's `info.version` (which the prod desktop updater | ||
| # reads) excludes them, so prod installs can never pick these up. |
There was a problem hiding this comment.
issue: README's 'Releasing' section and docs/docs/developer/release-and-versioning.md still describe push-to-main as the only publish path and say only the release workflow creates v* tags. The first staging push after merge contradicts both: rc tag, GitHub pre-release, PyPI upload. Worth a short staging-rc paragraph in both while this is fresh.

Problem
cowork-server staging needs Anton changes before they reach
anton/main, but Anton only publishes stable packages frommain(release.yml). Downstream staging has had to install Anton from a mutable git branch or a hand-pinned commit, so tests, images, wheels, and lockfiles can observe different Anton snapshots. This is the producer half of ENG-1159.The consumer half already shipped in cowork-server #225 (ENG-958): its
publish-staging.yml("Pin anton-agent to its latest staging rc" step) pinsanton-agent==<latest rc>and falls back to a loose constraint otherwise. It hits that fallback on every run today because Anton publishes no rc's. This PR closes the loop.Design — mirrors the cowork-server pattern
publish-staging.yml(new) — staging only: push tostaging→ validate → cut a PEP 440 pre-release (2.YY.M.DD.SEQrcN) via the sharedcalver-release.ymlreusable → publish to PyPI via Trusted Publishing → notify.release.yml— same intent, restructured to match cowork-server'spublish.yml: gate on the reusable tests, scopepermissionsper job (only the release job getscontents: write), guard onrefs/heads/main, and fold the test job intonotify.tests.yml— now aworkflow_callreusable invoked by both publishers; drops its standalonepush: [main, staging]run and push-onlynotify. Branch-push validation now happens inside the release run. PR gating is unchanged.Simpler than cowork-server's: Anton doesn't depend on itself, so there's nothing to self-pin —
publish-staging.ymlbuilds from a clean checkout andhatch-vcsderives the rc version directly. No pin step, noSETUPTOOLS_SCM_PRETEND_VERSIONoverride, and nobuild-deployjob (Anton ships no image).Prod safety: resolvers ignore pre-releases unless a specifier names one, and PyPI's
info.version(read by the prod desktop updater) excludes them — so prod can never pick up an rc.Register a second Trusted Publisher on the
anton-agentproject:publish-staging.ymlpypiUntil this is added, the
publishjob fails OIDC auth. The tag/release still cut, so it's safe to add the publisher and re-run.Notes
main, so only this producer workflow is needed.🤖 Generated with Claude Code