Skip to content

Fix the auto-tag push auth, and stop the two publishes racing - #12

Merged
jamiewest merged 1 commit into
mainfrom
ci/serialize-release-tags
Sep 21, 2026
Merged

jamiewest merged 1 commit into
mainfrom
ci/serialize-release-tags

Conversation

@jamiewest

Copy link
Copy Markdown
Owner

Two problems, both surfaced by the first real run of this workflow now that RELEASE_PAT exists.

1. The push authenticated as the bot, not the PAT — this is what's blocking the release

remote: Permission to jamiewest/extensions.git denied to github-actions[bot].
fatal: unable to access 'https://github.com/jamiewest/extensions.git/': The requested URL returned error: 403
Error: Process completed with exit code 128.

The secret is set correctly — the RELEASE_PAT-is-empty guard would have exited before git push otherwise. The problem is actions/checkout: with the default persist-credentials: true it writes GITHUB_TOKEN into an http.https://github.com/.extraheader git config, and that header takes precedence over credentials embedded in a push URL. So

git push "https://x-access-token:${RELEASE_PAT}@github.com/${GITHUB_REPOSITORY}.git" "$TAG"

never used the PAT at all — it pushed as github-actions[bot], which has contents: read, hence the 403.

Fixed by setting persist-credentials: false on the checkout, leaving the URL credentials as the only ones in play. The job keeps contents: read deliberately: GITHUB_TOKEN should have no write access here, because only a PAT-pushed tag triggers publish.yml. Both facts are now in the workflow header so this doesn't get "helpfully" reverted later.

2. The two publishes raced

Both tags were pushed back to back, kicking off two concurrent publish.yml runs. extensions_flutter depends on extensions: ^0.8.0, so whenever the flutter publish won the race it failed resolving a dependency that wasn't on pub.dev yet. The old header acknowledged this and told the reader to re-run by hand.

The dependent tag now waits for the dependency to actually appear on pub.dev before being pushed — polling the versions API every 15s, 20 minutes by default, overridable with a PUBLISH_WAIT_SECONDS repo variable. The wait also covers the case where the core tag already existed with its publish still in flight, not just the case where this run pushed it. A wait that times out fails the job and names the publish run to go look at.

Worth recording why not needs:, since it was the obvious option: two jobs with needs: would wait for the tag push to finish, not for the publish that push triggers. The race would survive it. The poll is the part that actually does the work.

Also sets timeout-minutes: 45, since a run can now legitimately sit and wait.

Verification

Extracted the step's script and ran it the way CI does (bash -e, same env), with a stubbed git for the push paths:

Scenario Result
No PAT, both unpublished exit 1, both reported — unchanged from #10
Both already on pub.dev exit 0, no wait, no summary
Core pending, dependency wait core tagged → wait engages → times out at the deadline → exit 1 + summary naming the publish run
Core already published, dependent pending no wait, dependent tagged immediately (0.6s)

YAML parses; the script passes bash -n.

After this merges

Re-run Auto-tag releases and the release should go end to end: extensions-v0.8.1 pushed → published → extensions_flutter-v0.6.0 pushed once 0.8.1 is live → published. pub.dev is still serving 0.7.1 / 0.5.2 as of this PR.

🤖 Generated with Claude Code

https://claude.ai/code/session_01P889JtFvr4ypqnHYQZCPag


Generated by Claude Code

Two problems, both on the first real run of this workflow.

1. The tag push authenticated as github-actions[bot], not as the PAT:

     remote: Permission to jamiewest/extensions.git denied to github-actions[bot]
     fatal: ... The requested URL returned error: 403

   actions/checkout persists GITHUB_TOKEN into an
   `http.https://github.com/.extraheader` git config, and that header takes
   precedence over credentials embedded in a push URL -- so
   `git push https://x-access-token:${RELEASE_PAT}@github.com/...` never used
   the PAT at all. Fixed with persist-credentials: false on the checkout,
   which leaves the URL credentials as the only ones in play. The job keeps
   contents: read deliberately: GITHUB_TOKEN should have no write access
   here, since only a PAT-pushed tag triggers publish.yml.

2. Both tags were pushed back to back, so the two publish.yml runs raced.
   extensions_flutter depends on extensions, so when the flutter publish won
   the race it failed resolving a dependency that was not on pub.dev yet --
   the workflow header acknowledged this and told the reader to re-run.
   Now the dependent tag waits for the dependency to actually appear on
   pub.dev (polling the versions API every 15s, default 20 minutes, override
   with the PUBLISH_WAIT_SECONDS repo variable) before being pushed. The
   wait also covers the case where the core tag already existed with its
   publish still in flight, not just the case where this run pushed it.
   A wait that times out fails the job and says which publish to check.

   Note `needs:` between two jobs would not have fixed this: it waits for
   the tag push to finish, not for the publish that the push triggers.

Also sets timeout-minutes: 45, since a run can now legitimately wait.

Verified by extracting the step's script and running it as CI does, with a
stubbed git for the push paths:

  - no PAT, both unpublished          -> exit 1, both reported (unchanged)
  - both already on pub.dev           -> exit 0, no wait, no summary
  - core pending, dependency wait     -> core tagged, wait engages, times out
                                         at the deadline, exit 1 + summary
  - core already published            -> no wait, dependent tagged at once

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P889JtFvr4ypqnHYQZCPag
@jamiewest
jamiewest merged commit adbe895 into main Sep 21, 2026
3 checks passed
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