This is an automated email from the ASF dual-hosted git repository. yjc pushed a commit to branch manage-github-workflow in repository https://gitbox.apache.org/repos/asf/incubator-superset.git
commit 9d7ce7b19f8259cc8b4a24cd2488b43d34d79e5e Author: Jesse Yang <[email protected]> AuthorDate: Fri Dec 4 19:16:05 2020 -0800 chore: add a script to cancel Github workflows --- scripts/cancel_github_workflows.py | 159 +++++++++++++++++++++++++++++++++++++ 1 file changed, 159 insertions(+) diff --git a/scripts/cancel_github_workflows.py b/scripts/cancel_github_workflows.py new file mode 100755 index 0000000..e46d6ca --- /dev/null +++ b/scripts/cancel_github_workflows.py @@ -0,0 +1,159 @@ +#!/usr/bin/env python3 +""" +Manually GitHub workflow runs +""" +import os +from typing import List, Iterable, Union, Optional + +import requests +import click +from click.exceptions import ClickException +from typing_extensions import Literal + + +github_token = os.environ.get("GITHUB_TOKEN") + + +def request(method: Literal["GET", "POST", "DELETE", "PUT"], endpoint: str, **kwargs): + resp = requests.request( + method, + f"https://api.github.com/{endpoint.lstrip('/')}", + headers={"Authorization": f"Bearer {github_token}"}, + **kwargs, + ).json() + if "message" in resp: + raise ClickException(f"{method} {endpoint} >> {resp['message']} <<") + return resp + + +def list_runs(repo: str, params=None): + return request("GET", f"/repos/{repo}/actions/runs", params=params) + + +def cancel_run(repo: str, run_id: Union[str, int]): + return request("POST", f"/repos/{repo}/actions/runs/{run_id}/cancel") + + +def get_pull_request(repo: str, pull_number: Union[str, int]): + return request("GET", f"/repos/{repo}/pulls/{pull_number}") + + +def get_runs_by_branch( + repo: str, + branch: str, + user: Optional[str] = None, + statuses: Iterable[str] = ("queued", "in_progress"), + events: Iterable[str] = ("pull_request",), +): + """Get workflow runs associated with the given branch""" + return [ + item + for event in events + for status in statuses + for item in list_runs(repo, {"event": event, "status": status})["workflow_runs"] + if item["head_branch"] == branch + and (user is None or user == item["head_repository"]["owner"]["login"]) + ] + + +def print_commit(commit): + """Print out commit message for verification""" + indented_message = " \n".join(commit["message"].split("\n")) + print( + f""" +HEAD {commit["id"]} +Author: {commit["author"]["name"]} <{commit["author"]["email"]}> +Date: {commit["timestamp"]} + + {indented_message} +""" + ) + + [email protected]() [email protected]( + "--repo", + default="apache/incubator-superset", + help="Default is apache/incubator-superset", +) [email protected]( + "--event", + type=click.Choice(["pull_request", "push", "issue"]), + default=["pull_request"], + multiple=True, + help="One of more pull_request, push or issue", +) [email protected]( + "--keep-last/--no-keep-last", default=True, help="Don't cancel the lastest runs" +) [email protected]( + "--keep-running/--no-keep-running", + default=False, + help="Whether to skip cancelling running workflows", +) [email protected]("branch_or_pull") +def cancel_github_workflows( + branch_or_pull: str, repo, event: List[str], keep_last: bool, keep_running: bool +): + """Cancel running or queued GitHub workflows by branch or pull request ID.""" + if not github_token: + raise ClickException("Please provide GITHUB_TOKEN as an env variable") + + statuses = ("queued",) if keep_running else ("queued", "in_progress") + pr = None + + if branch_or_pull.isdigit(): + pr = get_pull_request(repo, pull_number=branch_or_pull) + target_type = "pull request" + title = f"#{pr['number']} - {pr['title']}" + else: + target_type = "branch" + title = branch_or_pull + + print(f"\nCancelling workflow runs for {target_type}\n\n {title}\n") + + if pr: + # full branch name + runs = get_runs_by_branch( + repo, + statuses=statuses, + events=event, + branch=pr["head"]["ref"], + user=pr["user"]["login"], + ) + else: + user = None + branch = branch_or_pull + if ":" in branch: + [user, branch] = branch.split(":", 2) + runs = get_runs_by_branch( + repo, statuses=statuses, events=event, branch=branch_or_pull, user=user + ) + + runs = sorted(runs, key=lambda x: x["created_at"]) + if not runs: + print(f"No {' or '.join(statuses)} workflow runs found.\n") + return + + last_sha = runs[-1]["head_commit"]["id"] + if keep_last: + # Find the head commit SHA of the last created workflow run + runs = [x for x in runs if x["head_commit"]["id"] != last_sha] + if not runs: + print("Only the latest runs are in queue. Use --no-keep-last to force cancelling them.\n") + return + + last_sha = None + + for entry in runs: + head_commit = entry["head_commit"] + if head_commit["id"] != last_sha: + last_sha = head_commit["id"] + print_commit(head_commit) + cancel_run(repo, entry["id"]) + print(f"[Cancled] {entry['name']}") + print("") + +if __name__ == "__main__": + # pylint: disable=no-value-for-parameter + cancel_github_workflows()
