diff --git a/.github/workflows/cleanup.yaml b/.github/workflows/cleanup.yaml index d0aad80..1d90a77 100644 --- a/.github/workflows/cleanup.yaml +++ b/.github/workflows/cleanup.yaml @@ -34,4 +34,5 @@ jobs: last_commit_age_days: 0 dry_run: yes ignore_branches: test_prefix/one,test_prefix/two,test_prefix_ignored/one - branch_limit: 1 + branch_limit: 10 + only_closed_prs: yes diff --git a/action.yml b/action.yml index c4e514a..e7175c0 100644 --- a/action.yml +++ b/action.yml @@ -34,6 +34,10 @@ inputs: description: "The max number of branches that can be deleted" required: false default: "100" + only_closed_prs: + description: "Whether we're only deleting branches that belong to closed PRs. Defaults to 'no'. Possible values: yes, no (case sensitive)" + required: false + default: "no" outputs: deleted_branches: # id of output @@ -54,5 +58,6 @@ runs: python3 main.py --ignore-branches=${{ inputs.ignore_branches }} \ --last-commit-age-days=${{ inputs.last_commit_age_days }} --allowed-prefixes=${{ inputs.allowed_prefixes }} \ --dry-run=${{ inputs.dry_run }} --github-token=${{ inputs.github_token }} \ - --github-base-url=${{ inputs.github_base_url }} --branch-limit=${{ inputs.branch_limit }} + --github-base-url=${{ inputs.github_base_url }} --branch-limit=${{ inputs.branch_limit }} \ + --only-closed-prs=${{ inputs.only_closed_prs }} shell: bash diff --git a/src/actions.py b/src/actions.py index 0967cbe..4062da2 100644 --- a/src/actions.py +++ b/src/actions.py @@ -11,7 +11,8 @@ def run_action(options: Options) -> list: last_commit_age_days=options.last_commit_age_days, ignore_branches=options.ignore_branches, allowed_prefixes=options.allowed_prefixes, - branch_limit=options.branch_limit + branch_limit=options.branch_limit, + only_closed_prs=options.only_closed_prs, ) print(f"Branches queued for deletion: {branches}") diff --git a/src/github.py b/src/github.py index 5ade934..d985e7b 100644 --- a/src/github.py +++ b/src/github.py @@ -23,7 +23,8 @@ def get_deletable_branches( last_commit_age_days: int, ignore_branches: list[str], allowed_prefixes: list[str], - branch_limit: int + branch_limit: int, + only_closed_prs: bool, ) -> list[str]: if branch_limit < 1: return [] @@ -76,6 +77,11 @@ def get_deletable_branches( if found_prefix is False: print(f'Ignoring `{branch_name}` because it does not match any provided allowed_prefixes') continue + + # If only_closed_prs is True, move on if branch is not base for a closed pull request + if only_closed_prs is True and self.is_closed_pull_request_base(branch=branch_name) is False: + print(f'Ignoring `{branch_name}` because only_closed_prs is True and it is not base for a closed pull request') + continue # Move on if commit is in an open pull request if self.has_open_pulls(commit_hash=commit_hash): @@ -152,6 +158,20 @@ def has_open_pulls(self, commit_hash: str) -> bool: return False + def is_closed_pull_request_base(self, branch: str) -> bool: + """ + Returns true if the given branch is base for a closed pull request. + """ + url = f'{self.base_url}/repos/{self.repo}/pulls?base={branch}&state=closed' + headers = self.make_headers() + headers['accept'] = 'application/vnd.github.groot-preview+json' + + response = requests.get(url=url, headers=headers) + if response.status_code != 200: + raise RuntimeError(f'Failed to make request to {url}. {response} {response.json()}') + + return len(response.json()) > 0 + def is_pull_request_base(self, branch: str) -> bool: """ Returns true if the given branch is base for another pull request. diff --git a/src/io.py b/src/io.py index 3c3977e..89b1d6f 100644 --- a/src/io.py +++ b/src/io.py @@ -15,6 +15,7 @@ def __init__( branch_limit: int, dry_run: bool = True, github_base_url: str = DEFAULT_GITHUB_API_URL, + only_closed_prs: bool = False, ): self.ignore_branches = ignore_branches self.last_commit_age_days = last_commit_age_days @@ -24,6 +25,7 @@ def __init__( self.dry_run = dry_run self.github_base_url = github_base_url self.branch_limit = branch_limit + self.only_closed_prs = only_closed_prs class InputParser: @@ -67,6 +69,13 @@ def get_args() -> argparse.Namespace: type=int, ) + parser.add_argument( + "--only-closed-prs", + choices=["yes", "no"], + default="no", + help="Whether we're only deleting branches that belong to closed PRs. Defaults to 'no'. Possible values: yes, no (case sensitive)" + ) + return parser.parse_args() def parse_input(self) -> Options: @@ -84,6 +93,7 @@ def parse_input(self) -> Options: # Dry run can only be either `true` or `false`, as strings due to github actions input limitations dry_run = False if args.dry_run == 'no' else True + only_closed_prs = False if args.only_closed_prs == 'no' else True return Options( ignore_branches=ignore_branches, @@ -93,7 +103,8 @@ def parse_input(self) -> Options: github_token=args.github_token, github_repo=getenv('GITHUB_REPOSITORY'), github_base_url=args.github_base_url, - branch_limit=args.branch_limit + branch_limit=args.branch_limit, + only_closed_prs=only_closed_prs, )