fix(ci): scope release runs to per-sha concurrency group

The workflow-wide concurrency group was `${{ github.workflow }}-${{ github.ref }}`
with `cancel-in-progress: true`, so the release-script's atomic
`refs/heads/main + v* tag` push shared the `CI-refs/heads/main` group with every
later main push. The newer run cancelled the older release run before
`release_binary` / `release_github` / `release_npm` could execute, and no
future run carried the tag at HEAD, so the tag stayed published-as-a-ref but
unreleased on GitHub and npm (v15.12.6 in the wild).

Release runs are now routed to a per-sha group with `cancel-in-progress: false`
when either:
  * the push subject starts with `chore: bump version to ` (the release-script
    commit convention from scripts/release.ts), or
  * `github.ref` is a `v*` tag (workflow_dispatch recovery from a tag ref).

Other events keep the cheap branch-wide cancel-in-progress for PR/main churn.
release.ts's retry hint now uses the same release commit subject so manual
retries also land in the per-sha group.

Added scripts/ci-concurrency.test.ts: a regression test with a minimal GHA
expression evaluator that asserts the resolved group / cancel-in-progress for
auto-release pushes, retry pushes, tag-ref dispatches, plain main pushes, PRs,
distinct release shas, and a benign `revert: chore: bump version to ...`
follow-up.

Fixes #2564
This commit is contained in:
roboomp
2026-06-14 12:23:23 +00:00
parent cd00003829
commit 9acc24329f
4 changed files with 317 additions and 3 deletions
+10 -2
View File
@@ -12,9 +12,17 @@ on:
type: boolean
default: false
# Release runs publish a `v*` tag pushed atomically with main HEAD; sharing
# the cheap branch-wide `CI-refs/heads/main` group meant a later main push
# silently cancelled the in-flight release and left the tag without a GitHub
# Release or npm publish (#2564). Detect release runs at workflow-scheduling
# time via the release-script commit subject (`chore: bump version to vX.Y.Z`,
# see scripts/release.ts) and via `v*` tag-ref dispatches, then scope them to
# a per-sha group with no cancellation. Every other event keeps the
# branch-wide cancel-in-progress for PR/main churn.
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
group: "${{ github.workflow }}-${{ (startsWith(github.event.head_commit.message, 'chore: bump version to ') || startsWith(github.ref, 'refs/tags/v')) && format('release-{0}', github.sha) || github.ref }}"
cancel-in-progress: "${{ !(startsWith(github.event.head_commit.message, 'chore: bump version to ') || startsWith(github.ref, 'refs/tags/v')) }}"
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true