diff --git a/README.md b/README.md index a3ab6d3..36cfae0 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,7 @@ A branch must meet all the following criteria to be deemed abandoned and safe to * Must NOT be the base of an open pull request of another branch. The base of a pull request is the branch you told GitHub you want to merge your pull request into. * Must NOT be in an optional list of branches to ignore +* Must match one of the given branch prefixes (optional) * Must be older than a given amount of days ## Inputs @@ -27,6 +28,7 @@ A branch must meet all the following criteria to be deemed abandoned and safe to | ------------- | ------------- | ------------- | | `ignore_branches` | Comma-separated list of branches to ignore and never delete. You don't need to add your protected branches here. | `foo,bar` | `last_commit_age_days` | How old in days must be the last commit into the branch for the branch to be deleted. Default: `60` | `90` +| `prefixes` | Comma-separated list of prefixes a branch must match to be deleted. Default: `null` | `feature/,bugfix/` | `dry_run`* | Whether we're actually deleting branches at all. Possible values: `yes, no` (case sensitive). Default: `yes` | `no` | `github_token`* | The github token to use on requests to the github api. You can use the one github actions provide | `${{ github.token }}` | `github_base_url` | The github API's base url. You only need to override this when using Github Enterprise on a different domain. Default: `https://api.github.com` | `https://github.mycompany.com/api/v3` diff --git a/action.yml b/action.yml index a7fae31..2b88b97 100644 --- a/action.yml +++ b/action.yml @@ -16,6 +16,10 @@ inputs: description: "How old in days must be the last commit into the branch for the branch to be deleted." required: false default: "60" + prefixes: + description: "Comma-separated list of prefixes a branch must match to be deleted." + required: false + default: "" dry_run: description: "Whether we're actually deleting branches at all. Defaults to 'yes'. Possible values: yes, no (case sensitive)" required: true @@ -37,6 +41,7 @@ runs: args: - ${{ inputs.ignore_branches }} - ${{ inputs.last_commit_age_days }} + - ${{ inputs.prefixes }} - ${{ inputs.dry_run }} - ${{ inputs.github_token }} - ${{ inputs.github_base_url }} diff --git a/main.py b/main.py index e20146b..3bce38a 100644 --- a/main.py +++ b/main.py @@ -1,11 +1,12 @@ from src import actions, io if __name__ == '__main__': - ignore_branches, last_commit_age_days, dry_run, github_token, github_repo, github_base_url = io.parse_input() + ignore_branches, last_commit_age_days, prefixes, dry_run, github_token, github_repo, github_base_url = io.parse_input() deleted_branches = actions.run_action( ignore_branches=ignore_branches, last_commit_age_days=last_commit_age_days, + prefixes=prefixes, dry_run=dry_run, github_repo=github_repo, github_token=github_token, diff --git a/src/actions.py b/src/actions.py index 1facfd0..7d9484f 100644 --- a/src/actions.py +++ b/src/actions.py @@ -5,6 +5,7 @@ def run_action( github_repo: str, ignore_branches: list, last_commit_age_days: int, + prefixes: list, github_token: str, github_base_url: str, dry_run: bool = True @@ -13,6 +14,7 @@ def run_action( 'github_repo': github_repo, 'ignore_branches': ignore_branches, 'last_commit_age_days': last_commit_age_days, + 'prefixes': prefixes, 'dry_run': dry_run, 'github_base_url': github_base_url } @@ -21,7 +23,7 @@ def run_action( github = Github(github_repo=github_repo, github_token=github_token, github_base_url=github_base_url) - branches = github.get_deletable_branches(last_commit_age_days=last_commit_age_days, ignore_branches=ignore_branches) + branches = github.get_deletable_branches(last_commit_age_days=last_commit_age_days, ignore_branches=ignore_branches, prefixes=prefixes) print(f"Branches queued for deletion: {branches}") if dry_run is False: diff --git a/src/github.py b/src/github.py index 0ec3517..4710253 100644 --- a/src/github.py +++ b/src/github.py @@ -17,7 +17,7 @@ def make_headers(self) -> dict: def get_paginated_branches_url(self, page: int = 0) -> str: return f'{self.github_base_url}/repos/{self.github_repo}/branches?protected=false&per_page=30&page={page}' - def get_deletable_branches(self, last_commit_age_days: int, ignore_branches: list) -> list: + def get_deletable_branches(self, last_commit_age_days: int, ignore_branches: list, prefixes: list) -> list: # Default branch might not be protected default_branch = self.get_default_branch() @@ -42,7 +42,7 @@ def get_deletable_branches(self, last_commit_age_days: int, ignore_branches: lis print(f'Analyzing branch `{branch_name}`...') - # Immediately discard protected branches, default branch and ignored branches + # Immediately discard protected branches, default branch, ignored branches and branches not matching prefix if branch_name == default_branch: print(f'Ignoring `{branch_name}` because it is the default branch') continue @@ -57,6 +57,16 @@ def get_deletable_branches(self, last_commit_age_days: int, ignore_branches: lis print(f'Ignoring `{branch_name}` because it is on the list of ignored branches') continue + # If prefixes are provided, only consider branches that match one of the prefixes + if len(prefixes) > 0: + found_prefix = False + for prefix in prefixes: + if branch_name.startswith(prefix): + found_prefix = True + if found_prefix is False: + print(f'Ignoring `{branch_name}` because it does not match any provided prefix') + continue + # Move on if commit is in an open pull request if self.has_open_pulls(commit_hash=commit_hash): print(f'Ignoring `{branch_name}` because it has open pulls') diff --git a/src/io.py b/src/io.py index 3cf9ff1..60a8103 100644 --- a/src/io.py +++ b/src/io.py @@ -3,14 +3,14 @@ from typing import List -def parse_input() -> (list, int, bool, str, str, str): +def parse_input() -> (list, int, list, bool, str, str, str): args: List[str] = sys.argv num_args = len(args) - if num_args < 4 or num_args > 6: + if num_args < 4 or num_args > 7: input_string = ' '.join(args) - expected_string = f'{args[0]} ignore_branches last_commit_age_days dry_run_yes_no github_token github_repo github_base_url' + expected_string = f'{args[0]} ignore_branches last_commit_age_days prefixes dry_run_yes_no github_token github_repo github_base_url' raise RuntimeError(f'Incorrect input: {input_string}. Expected: {expected_string}') branches_raw: str = args[1] @@ -20,16 +20,21 @@ def parse_input() -> (list, int, bool, str, str, str): last_commit_age_days = int(args[2]) + prefixes_raw: str = args[3] + prefixes = prefixes_raw.split(',') + if prefixes == ['']: + prefixes = [] + # Dry run can only be either `true` or `false`, as strings due to github actions input limitations - dry_run = False if args[3] == 'no' else True + dry_run = False if args[4] == 'no' else True - github_token = args[4] + github_token = args[5] github_repo = getenv('GITHUB_REPOSITORY') - github_base_url = args[5] if num_args >= 6 else 'https://api.github.com' + github_base_url = args[6] if num_args >= 7 else 'https://api.github.com' - return ignore_branches, last_commit_age_days, dry_run, github_token, github_repo, github_base_url + return ignore_branches, last_commit_age_days, prefixes, dry_run, github_token, github_repo, github_base_url def format_output(output_strings: dict) -> None: