diff --git a/.github/actions/README.md b/.github/actions/README.md index b5df344..339e4a3 100644 --- a/.github/actions/README.md +++ b/.github/actions/README.md @@ -402,11 +402,16 @@ Requirements in the consumer repo: jobs' `if:` on it, so a hand-made `chore/release-*` branch merged by a collaborator never reaches the app-token steps. - A `concurrency` group on every release workflow, always with - `cancel-in-progress: false` (the templates carry them): one candidate at a - time, one settle per version, one publish per release branch, one - `on-release` run per tag. Each of them ends in a push, a tag or an upload - that must never be cancelled half-way, and none of them is atomic with its - own guard, so a second run queues rather than overlaps. + `cancel-in-progress: false` (the templates carry them): one proposal at a + time, one cut at a time, one settle per version, one publish per release + branch, one `on-release` run per tag. Each of them ends in a push, a tag or + an upload that must never be cancelled half-way, and none of them is + atomic with its own guard, so a second run queues rather than overlaps. + The groups on the `pull_request`-triggered jobs (`cut`, `publish`) are + job-level, not workflow-level: every PR closing on that branch starts the + workflow, and GitHub keeps one pending run per group, so a workflow-wide + group would let an unrelated closure evict a queued release run. A + skipped job holds no slot in a job-level group. - Every workflow the release depends on must be on the tagged commit, not just on the default branch: `release-publish.yml` runs from the settle PR's merge into the release branch, and `on-release.yml` from the tag's diff --git a/workflow-templates/release-candidate.yml b/workflow-templates/release-candidate.yml index 01385c8..f048a6d 100644 --- a/workflow-templates/release-candidate.yml +++ b/workflow-templates/release-candidate.yml @@ -17,18 +17,20 @@ on: types: [closed] branches: [$default-branch] -# One candidate at a time: two dispatches would race on the candidate branch, -# and a cut runs on its own merge and must never be cancelled half-way. -concurrency: - group: ${{ github.workflow }} - cancel-in-progress: false - permissions: contents: read jobs: propose: if: github.event_name == 'workflow_dispatch' + # One proposal at a time: two dispatches would race on the candidate + # branch. Job-level, not workflow-level: every PR closing on the default + # branch also starts this workflow, and a workflow-wide group would let + # that noise evict a queued release run (one pending run per group). + # A skipped job holds no slot in a job-level group. + concurrency: + group: ${{ github.workflow }}-propose + cancel-in-progress: false runs-on: ubuntu-latest steps: - uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3 @@ -60,6 +62,11 @@ jobs: github.event.pull_request.merged == true && startsWith(github.event.pull_request.head.ref, 'chore/release-candidate-') && github.event.pull_request.user.login == 'mega-maxwell[bot]' + # One cut at a time, never cancelled: pull_request.closed does not refire. + # Job-level for the reason given on `propose`. + concurrency: + group: ${{ github.workflow }}-cut + cancel-in-progress: false runs-on: ubuntu-latest steps: - uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3 diff --git a/workflow-templates/release-publish.yml b/workflow-templates/release-publish.yml index 89a9615..7d08f1c 100644 --- a/workflow-templates/release-publish.yml +++ b/workflow-templates/release-publish.yml @@ -14,12 +14,6 @@ on: types: [closed] branches: ["release-v*"] -# One publish per release branch: the tag guard and the tag push are two -# steps, so a re-run must queue behind a run in flight, never overlap it. -concurrency: - group: ${{ github.workflow }}-${{ github.event.pull_request.base.ref }} - cancel-in-progress: false - permissions: contents: read @@ -29,6 +23,13 @@ jobs: github.event.pull_request.merged == true && startsWith(github.event.pull_request.head.ref, 'chore/release-settle-') && github.event.pull_request.user.login == 'mega-maxwell[bot]' + # One publish per release branch: the tag guard and the tag push are two + # steps, so a re-run must queue behind a run in flight, never overlap it. + # Job-level: other PRs closing on the release branch also start this + # workflow, and must not evict a queued publish (one pending per group). + concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.base.ref }} + cancel-in-progress: false runs-on: ubuntu-latest steps: - uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3