Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .changelog/unreleased/61-label-skip-concurrency.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
### Fixed

- Isolated skipped non-`ci:full` label events into a separate required-gate
caller concurrency group so they cannot replace pending real gate runs. (#61)
9 changes: 5 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,10 +76,11 @@ The required-gate caller listens for `labeled` and `unlabeled` events only to
preserve the `ci:full` escape hatch. The caller job and concurrency expression
are guarded so `ci:full` is the only label change that can run or cancel the
required branch-protection gate. Other labels such as `no-changelog` may create
a skipped caller run, but they do not start `repo-required-gate / decision` and
do not cancel an in-flight required-gate run. If a repo overrides the
`force-full-ci-label` input, update the caller guard and concurrency label name
to match.
a skipped caller run in a separate `label-skip-*` concurrency group, but they do
not start `repo-required-gate / decision`, cancel an in-flight required-gate
run, or replace a pending gate run in the real required-gate concurrency group.
If a repo overrides the `force-full-ci-label` input, update the caller guard and
concurrency label name to match.

For agent closeout, use the shared local preflight instead of direct promotion:

Expand Down
12 changes: 10 additions & 2 deletions examples/repo-required-gate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,14 @@ permissions:
pull-requests: write

concurrency:
group: repo-required-gate-${{ github.event.pull_request.number || github.ref }}
group: >-
repo-required-gate-${{ github.event.pull_request.number || github.ref }}-${{
github.event_name == 'pull_request' &&
(github.event.action == 'labeled' || github.event.action == 'unlabeled') &&
github.event.label.name != 'ci:full' &&
format('label-skip-{0}', github.event.label.name) ||
'gate'
}}
cancel-in-progress: >-
${{
github.event_name == 'pull_request' &&
Expand Down Expand Up @@ -120,7 +127,8 @@ jobs:
# routing and run the full language CI surface — e.g. a workflow-only
# change that semantically affects CI. The caller guard above allows
# only this label to run/cancel the gate on label changes; other labels
# such as `no-changelog` skip without replacing the required decision.
# such as `no-changelog` skip in a separate concurrency group without
# replacing or canceling the required decision.
# If you override the label input, update the guard/concurrency label
# name above to match.
#
Expand Down
3 changes: 3 additions & 0 deletions scripts/workflow-structure.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,9 @@ describe('repo-required-gate caller example', () => {
expect(jobBlock).toContain("github.event.label.name == 'ci:full'");

const concurrencyBlock = body.slice(body.indexOf('concurrency:'), body.indexOf('jobs:'));
expect(concurrencyBlock).toContain('group: >-');
expect(concurrencyBlock).toContain("format('label-skip-{0}', github.event.label.name)");
expect(concurrencyBlock).toContain("'gate'");
expect(concurrencyBlock).toContain('cancel-in-progress: >-');
expect(concurrencyBlock).toContain("github.event.label.name == 'ci:full'");
});
Expand Down
Loading