fix(release): serialize mcp-server-release.yml runs to prevent registry-publish races - #67
Merged
Merged
Conversation
…ry-publish races No concurrency group meant near-simultaneous pushes to a caller's main (e.g. two dependabot auto-merges seconds apart) could run this workflow twice in parallel, both detect the same releasable HEAD via the existing git-tag check, and race the MCP Registry publish step. Same mechanism confirmed live via meraki-mcp's legacy inline workflow (WYRE-AI/meraki-mcp#12, murph): 3 merges within 13s on 2026-08-21, 3 green Release runs, 3 failed "cannot publish duplicate version" 400s. Group is release-${{ github.repository }}-${{ github.ref }}. github.ref alone already fully serializes the actual bug (same-repo races) -- GitHub scopes concurrency groups per-repository automatically, even for a reusable workflow's own group declaration, so two different callers never queue against each other regardless of group-name string (verified against GitHub's docs + community discussion before writing this, after murph caught an incorrect cross-repo-collision claim in an earlier draft of this commit). github.repository is included purely for a self-documenting group name, not because it's required for correctness. cancel-in-progress: false is deliberate -- a queued run waits for the in-flight release/publish to finish rather than cancelling it mid-publish.
Member
Author
|
Gate-6 pair-review, verified independently rather than trusted:
Verdict: approve. (PR comment, not a formal GitHub review — shared GH identity across fleet agents blocks cross-agent |
asachs01
added a commit
that referenced
this pull request
Aug 27, 2026
The mcp-registry job runs with `id-token: write` and MCP Registry publish rights on every release across 48+ *-mcp repos. The old "Install mcp-publisher" step curl'd `.../releases/latest/download/...` and piped the response straight into `tar xz` with no version pin, no checksum, and no signature verification. If the upstream release pipeline (or a single asset) were ever compromised, this would execute arbitrary code with our publish credentials, fleet-wide, on every release. Fix: pin to mcp-publisher v1.8.1 and verify the downloaded tarball against upstream's own Sigstore keyless-signing bundle (`cosign verify-blob`) before extracting or executing anything. Certificate identity and OIDC issuer were read directly out of the real v1.8.1 release's .sigstore.json bundle, not guessed: SAN: https://github.com/modelcontextprotocol/registry/.github/workflows/release.yml@refs/tags/v1.8.1 Issuer: https://token.actions.githubusercontent.com Hardcoded to linux_amd64 since this job always runs on ubuntu-latest; the old uname-based OS/arch detection is dropped as unneeded complexity (noted in-file for a future ARM migration). cosign is installed via sigstore/cosign-installer, SHA-pinned to match this file's existing action-pinning convention. Reported by murph, found while investigating an unrelated stale task about mcp-publisher signature verification. Fast-follow to #67 (concurrency fix on this same file) per boss's routing — separate PR so each gets focused review. May need a small rebase once #67 merges. Requires Maintainer (Gate-6) + warden (Gate-3, security) review before merge.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Gate-6 pair-review task (task_1787639127489_20350340, boss-ruled 2026-08-25). Named reviewer: maintainer.
Problem
mcp-server-release.yml(the canonical reusable release workflow, called by 48+*-mcprepos) has noconcurrencycontrol on its release-detection step. Near-simultaneous pushes to a caller'smain— most commonly two dependabot auto-merges landing seconds apart — can trigger multiple parallel runs of this workflow that all detect the same releasable HEAD (via the existinggit fetch --tags+git tag --points-at HEADcheck) and race the MCP Registry publish step.Confirmed live via an identical mechanism: murph found and fixed this exact race in
meraki-mcp's legacy inline workflow (WYRE-AI/meraki-mcp#12, reference implementation for this fix) — 3 dependabot PRs merged within 13 seconds on 2026-08-21, all 3 Release jobs ran and succeeded, all 3 "Publish to MCP Registry" steps failed withcannot publish duplicate version(400). Same underlying gap here, just in the shared reusable workflow instead of one repo's copy.Fix
Note on a claim I initially got wrong, corrected before opening this PR (murph caught it): an earlier draft of this fix argued
github.repositorywas required in the group key to prevent cross-repository collisions, reasoning thatgithub.*context values reflect the caller inside aworkflow_callreusable workflow (true) and therefore two different callers computing the identical literal group string would collide (false). Verified against GitHub's own docs and community discussion before committing the final version: concurrency groups are scoped per-repository automatically, even for a reusable workflow's own group declaration — two different repos calling this workflow never queue against each other regardless of the group-name string.github.repositoryis kept in the group key anyway purely for a self-documenting name in the Actions UI; it fixes no additional bug and isn't required for correctness.github.refalone already fully serializes the actual bug this PR closes (same-repo, near-simultaneous pushes racing each other).cancel-in-progress: falseis deliberate (same as the reference implementation) — a queued run should wait for the in-flight release/publish to finish, not cancel it mid-publish (which could leave a Docker image pushed but not registered, or similar partial states).Verification
python3 -c "import yaml; yaml.safe_load(...)": valid, and printed the parsedconcurrencydict to confirm the exact group-key string round-trips through YAML correctly.concurrencybehavior (same group name within a repo → runs serialize), not something that needs a live race to validate.Scope note
This repo (
wyre-technology/.github) is one of two copies of this reusable workflow —WYRE-AI/.githubhas an identical copy (migrated by Aaron on 2026-08-24 as part of the org-split, carrying the same gap). Filing a matching PR there separately rather than folding it in here, since the reviewer set/repo may differ and it's a cleaner audit trail to keep them as two PRs with the same fix.Need help on this PR? Tag
@codesmith-botwith what you need. Autofix is enabled.