diff --git a/.github/workflows/close-pulumi-bot-prs.yml b/.github/workflows/close-pulumi-bot-prs.yml new file mode 100644 index 0000000000..ca931dfbb1 --- /dev/null +++ b/.github/workflows/close-pulumi-bot-prs.yml @@ -0,0 +1,17 @@ +# This calls close_outdated_bridge_prs.py as part of a daily cron. +# +name: Close pulumi-bot PRs +on: + schedule: + # 4 AM UTC ~ 9 PM PDT - specifically selected to avoid putting load on the CI system during working hours. + - cron: 0 4 * * * + workflow_dispatch: + +env: + GITHUB_TOKEN: ${{ secrets.PULUMI_BOT_TOKEN }} +jobs: + close-outdated-prs: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - run: ./scripts/close_outdated_bridge_prs.py diff --git a/scripts/close_outdated_bridge_prs.py b/scripts/close_outdated_bridge_prs.py new file mode 100755 index 0000000000..e502d21c70 --- /dev/null +++ b/scripts/close_outdated_bridge_prs.py @@ -0,0 +1,77 @@ +#!/usr/bin/env python3 + +# This script closes pulumi-bot PRs that we will not merge. +# +# pulumi-bot opens lots of PRs. Because of the number of repos we maintain, all PRs that +# pulumi-bot opens that we want to merge are set to auto-merge. Other PRs are opened for +# test purposes, but we don't intend to merge these. We want to close these PRs after a +# couple of days to avoid them cluttering up the repo. +# +# This script accomplishes that. + +import subprocess +import json +from datetime import datetime, timedelta + + +def cmd(*args: str, prefix="Exec: ") -> str: + print(prefix + " ".join(args)) + return subprocess.run(args, + capture_output=True, + check=True, + encoding="utf-8").stdout + +# PRs that would otherwise be closed are allowed to live for PR_LIFETIME_DAYS after their +# creation date. +PR_LIFETIME_DAYS=3 + +def close_outdated(repo: str): + cutoff_date = (datetime.now() - timedelta(days=PR_LIFETIME_DAYS)).date().isoformat() + issues = cmd("gh", "pr", "list", + "--repo", repo, + "--json", "author,title,autoMergeRequest,number", + "--search", f"author:pulumi-bot created:<{cutoff_date}", + ) + # Issues have this shape: + # + # { + # "author": { + # "id": "MDQ6VXNlcjMwMzUxOTU1", + # "is_bot": false, + # "login": "pulumi-bot", + # "name": "Pulumi Bot" + # }, + # "autoMergeRequest": { + # "authorEmail": null, + # "commitBody": null, + # "commitHeadline": null, + # "mergeMethod": "SQUASH", + # "enabledAt": "2023-11-22T00:23:34Z", + # "enabledBy": { + # "id": "MDQ6VXNlcjMwMzUxOTU1", + # "is_bot": false, + # "login": "pulumi-bot", + # "name": "Pulumi Bot" + # } + # }, + # "number": 2204, + # "title": "Upgrade pulumi-terraform-bridge to v3.66.0" + # } + # + # "autoMergeRequest" may be `null` + for issue in json.loads(issues): + if issue["autoMergeRequest"] or issue["author"]["login"] != "pulumi-bot": + # auto-merge: we don't need to close + # non-bot author: not our concern + continue + issue_title = issue["title"] + issue_number = issue["number"] + cmd("gh", "pr", "close", str(issue_number), "--repo", repo, prefix=f"Closing \"{issue_title}\":\n\t") + +def all_repos() -> [str]: + with open("provider-ci/providers.json", "r") as f: + return [f"pulumi/pulumi-{p}" for p in json.loads(f.read())] + +if __name__ == "__main__": + for repo in all_repos(): + close_outdated(repo)