Skip to content

Commit 1c865f1

Browse files
manusaclaude
andauthored
chore(ci): cancel PR workflows when prerequisite checks fail
Style checks and model generation are prerequisites that must pass before a PR is mergeable. When they fail, this cancels all other in-progress and queued workflow runs for the same commit to avoid wasting CI resources on builds and e2e tests that aren't needed. Co-Authored-By: Claude Opus 4.6 <[email protected]> Signed-off-by: Marc Nuri <[email protected]>
1 parent e18bf76 commit 1c865f1

3 files changed

Lines changed: 106 additions & 0 deletions

File tree

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
#
2+
# Copyright (C) 2015 Red Hat, Inc.
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
#
16+
17+
name: Cancel PR Workflows
18+
description: Cancels all other in-progress and queued workflow runs for the same PR commit
19+
20+
# Purpose:
21+
# Prerequisite workflows (style-checks, generate-model) are fast checks that must pass before
22+
# any PR is mergeable. When a prereq fails, there's no point running expensive workflows
23+
# (build, e2e tests, etc.) since the PR will need changes regardless. This action cancels all
24+
# other in-progress and queued workflow runs for the same PR commit to save CI resources.
25+
#
26+
# Usage:
27+
# Add a "cancel-on-failure" job to any prerequisite workflow:
28+
#
29+
# cancel-on-failure:
30+
# name: Cancel PR workflows on failure
31+
# runs-on: ubuntu-latest
32+
# if: ${{ failure() && github.event_name == 'pull_request' }}
33+
# needs: [<prereq-job>]
34+
# steps:
35+
# - uses: actions/checkout@v6
36+
# - uses: ./.github/actions/cancel-pr-workflows
37+
#
38+
# The calling workflow needs `actions: write` permission.
39+
#
40+
# Design notes:
41+
# - Only prereq workflows use this action. Other workflows should NOT use it — only checks that
42+
# are true prerequisites (i.e., the PR can never merge if they fail) should cancel other work.
43+
# - Race condition between prereqs is benign: if both style-checks and generate-model fail at
44+
# the same time, each cancel-on-failure job may try to cancel the other. The try/catch ensures
45+
# this doesn't cause errors (the run may have already completed or been cancelled).
46+
# - There is a small race window where heavy workflows may run briefly before being cancelled,
47+
# since all PR workflows start in parallel. This is acceptable given the prereqs are fast.
48+
# - The job-level `if` should include `github.event_name == 'pull_request'` to avoid spinning up
49+
# a runner on push-to-main failures (where cancellation is not needed).
50+
51+
runs:
52+
using: composite
53+
steps:
54+
- name: Cancel other workflow runs
55+
if: ${{ github.event_name == 'pull_request' }}
56+
uses: actions/github-script@v7
57+
with:
58+
script: |
59+
const owner = context.repo.owner;
60+
const repo = context.repo.repo;
61+
const headSha = context.payload.pull_request.head.sha;
62+
const currentRunId = context.runId;
63+
64+
for (const status of ['in_progress', 'queued']) {
65+
const { data: { workflow_runs } } = await github.rest.actions.listWorkflowRunsForRepo({
66+
owner,
67+
repo,
68+
head_sha: headSha,
69+
status,
70+
});
71+
72+
for (const run of workflow_runs) {
73+
if (run.id !== currentRunId) {
74+
console.log(`Cancelling ${status} run ${run.id} (${run.name})`);
75+
try {
76+
await github.rest.actions.cancelWorkflowRun({
77+
owner,
78+
repo,
79+
run_id: run.id,
80+
});
81+
} catch (e) {
82+
console.log(`Failed to cancel run ${run.id}: ${e.message}`);
83+
}
84+
}
85+
}
86+
}

.github/workflows/generate-model.yml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ concurrency:
3333

3434
permissions:
3535
contents: read
36+
actions: write
3637

3738
jobs:
3839
generate-model:
@@ -65,3 +66,12 @@ jobs:
6566
echo "Schema files modified. Build Failure";
6667
exit 1;
6768
fi;
69+
70+
cancel-on-failure:
71+
name: Cancel PR workflows on failure
72+
runs-on: ubuntu-latest
73+
if: ${{ failure() && github.event_name == 'pull_request' }}
74+
needs: [generate-model]
75+
steps:
76+
- uses: actions/checkout@v6
77+
- uses: ./.github/actions/cancel-pr-workflows

.github/workflows/style-checks.yml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ concurrency:
3232

3333
permissions:
3434
contents: read
35+
actions: write
3536

3637
jobs:
3738
style-check:
@@ -51,3 +52,12 @@ jobs:
5152
run: ./mvnw ${MAVEN_ARGS} -Pitests -N license:check
5253
- name: Check Format (only on touched files)
5354
run: ./mvnw ${MAVEN_ARGS} -Pitests spotless:check
55+
56+
cancel-on-failure:
57+
name: Cancel PR workflows on failure
58+
runs-on: ubuntu-latest
59+
if: ${{ failure() && github.event_name == 'pull_request' }}
60+
needs: [style-check]
61+
steps:
62+
- uses: actions/checkout@v6
63+
- uses: ./.github/actions/cancel-pr-workflows

0 commit comments

Comments
 (0)