Skip to content

Commit 6252541

Browse files
andy31415andreilitvincecille
authored
Add script and workflow that is able to "cancel all CI runs for a specific PR/commit" (#35975)
* Add a script and workflow for 'cancel running workflows'. The intent for this is to allow failing workflows to trigger a "cancel all the rest of the runs" for a current PR. * Restyle * Rename parameter to make it clear it is a github token * Update scripts/tools/cancel_workflows_for_pr.py Co-authored-by: C Freeman <cecille@google.com> --------- Co-authored-by: Andrei Litvin <andreilitvin@google.com> Co-authored-by: C Freeman <cecille@google.com>
1 parent 2a19f57 commit 6252541

File tree

2 files changed

+142
-0
lines changed

2 files changed

+142
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
# Copyright (c) 2024 Project CHIP Authors
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
name: Cancel workflow on PR
16+
on:
17+
workflow_dispatch:
18+
inputs:
19+
pull_request_id:
20+
description: 'PR number to consider'
21+
required: true
22+
type: number
23+
commit_sha:
24+
description: 'Cancel runs for this specific SHA'
25+
required: true
26+
type: string
27+
28+
jobs:
29+
cancel_workflow:
30+
name: Report on pull requests
31+
32+
runs-on: ubuntu-latest
33+
34+
# Don't run on forked repos
35+
if: github.repository_owner == 'project-chip'
36+
37+
steps:
38+
- name: Checkout
39+
uses: actions/checkout@v4
40+
- uses: actions/setup-python@v5
41+
with:
42+
python-version: '3.12'
43+
- name: Setup pip modules we use
44+
run: |
45+
pip install \
46+
click \
47+
coloredlogs \
48+
pygithub \
49+
&& echo "DONE installint python prerequisites"
50+
- name: Cancel runs
51+
run: |
52+
scripts/tools/cancel_workflows_for_pr.py \
53+
--pull-request ${{ inputs.pull_request_id }} \
54+
--commit-sha "${{ inputs.commit_sha }}" \
55+
--gh-api-token "${{ secrets.GITHUB_TOKEN }}"
+87
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
#!/usr/bin/env python3
2+
#
3+
# Copyright (c) 2024 Project CHIP Authors
4+
#
5+
# Licensed under the Apache License, Version 2.0 (the "License");
6+
# you may not use this file except in compliance with the License.
7+
# You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS,
13+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
# See the License for the specific language governing permissions and
15+
# limitations under the License.
16+
#
17+
18+
import logging
19+
20+
import click
21+
import coloredlogs
22+
from github import Github
23+
24+
__LOG_LEVELS__ = {
25+
"debug": logging.DEBUG,
26+
"info": logging.INFO,
27+
"warn": logging.WARN,
28+
"fatal": logging.FATAL,
29+
}
30+
31+
REPOSITORY = "project-chip/connectedhomeip"
32+
33+
34+
class Canceller:
35+
def __init__(self, token):
36+
self.api = Github(token)
37+
self.repo = self.api.get_repo(REPOSITORY)
38+
39+
def cancel_all_runs(self, pr_number, commit_sha, dry_run):
40+
pr = self.repo.get_pull(pr_number)
41+
logging.info("Examining PR '%s'", pr.title)
42+
for commit in pr.get_commits():
43+
if commit.sha != commit_sha:
44+
logging.info("Skipping SHA '%s' as it was not selected", commit.sha)
45+
continue
46+
47+
for check_suite in commit.get_check_suites():
48+
for run in check_suite.get_check_runs():
49+
if run.status in {"in_progress", "queued"}:
50+
if dry_run:
51+
logging.warning("DRY RUN: Will not stop run %s", run.name)
52+
else:
53+
logging.warning("Stopping run %s", run.name)
54+
self.repo.get_workflow_run(run.id).cancel()
55+
else:
56+
logging.info("Skip over run %s (%s)", run.name, run.status)
57+
58+
59+
@click.command()
60+
@click.option(
61+
"--log-level",
62+
default="INFO",
63+
type=click.Choice(list(__LOG_LEVELS__.keys()), case_sensitive=False),
64+
help="Determines the verbosity of script output.",
65+
)
66+
@click.option("--pull-request", type=int, help="Pull request number to consider")
67+
@click.option("--commit-sha", help="Commit to look at when cancelling pull requests")
68+
@click.option("--gh-api-token", help="Github token to use")
69+
@click.option("--token-file", help="Read github token from the given file")
70+
@click.option("--dry-run", default=False, is_flag=True, help="Actually cancel or not")
71+
def main(log_level, pull_request, commit_sha, gh_api_token, token_file, dry_run):
72+
coloredlogs.install(
73+
level=__LOG_LEVELS__[log_level], fmt="%(asctime)s %(levelname)-7s %(message)s"
74+
)
75+
76+
if gh_api_token:
77+
gh_token = gh_api_token
78+
elif token_file:
79+
gh_token = open(token_file, "rt").read().strip()
80+
else:
81+
raise Exception("Require a --gh-api-token or --token-file to access github")
82+
83+
Canceller(gh_token).cancel_all_runs(pull_request, commit_sha, dry_run)
84+
85+
86+
if __name__ == "__main__":
87+
main()

0 commit comments

Comments
 (0)