diff --git a/README.md b/README.md index 4e93fae..12f45fb 100644 --- a/README.md +++ b/README.md @@ -79,20 +79,21 @@ jobs: These are the inputs that can be provided on the workflow. -| Name | Required | Description | Default | Example | -|-------------------------------|----------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------|--------------------------------------------| -| `token` | Yes | GitHub Access Token with `workflow` scope (The Token needs to be added to the actions secrets) | `null` | `${{ secrets.WORKFLOW_SECRET }}` | -| `committer_username` | No | Name of the user who will commit the changes to GitHub | "github-actions[bot]" | "Test User" | -| `committer_email` | No | Email Address of the user who will commit the changes to GitHub | "github-actions[bot]@users.noreply.github.com" | "test@test.com" | -| `commit_message` | No | Commit message for the commits created by the action | "Update GitHub Action Versions" | "Custom Commit Message" | -| `pull_request_title` | No | Title of the pull requests generated by the action | "Update GitHub Action Versions" | "Custom PR Title" | -| `ignore` | No | A comma separated string of GitHub Actions to ignore updates for | `null` | "actions/checkout@v2, actions/cache@v2" | -| `skip_pull_request` | No | If **"true"**, the action will only check for updates and if any update is found the job will fail and update the build summary with the diff (**Options:** "true", "false") | "false" | "true" | -| `update_version_with` | No | Use The Latest Release Tag/Commit SHA or Default Branch Commit SHA to update the actions (**options:** "release-tag", "release-commit-sha", "default-branch-sha"') | "release-tag" | "release-commit-sha" | -| `release_types` | No | A comma separated string of release types to use when updating the actions. By default, all release types are used to update the actions. Only Applicable for **"release-tag", "release-commit-sha"** (**Options:** "major", "minor", "patch" **[one or many seperated by comma]**) | "all" | "minor, patch" | -| `pull_request_user_reviewers` | No | A comma separated string (usernames) which denotes the users that should be added as reviewers to the pull request | `null` | "octocat, hubot, other_user" | -| `pull_request_team_reviewers` | No | A comma separated string (team slugs) which denotes the teams that should be added as reviewers to the pull request | `null` | "justice-league, other_team" | -| `extra_workflow_locations` | No | A comma separated string of file or directory paths to look for workflows. By default, only the workflow files in the `.github/workflows` directory are checked updates | `null` | "path/to/directory, path/to/workflow.yaml" | +| Name | Required | Description | Default | Example | +|--------------------------------------|----------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------|--------------------------------------------| +| `token` | Yes | GitHub Access Token with `workflow` scope (The Token needs to be added to the actions secrets) | `null` | `${{ secrets.WORKFLOW_SECRET }}` | +| `committer_username` | No | Name of the user who will commit the changes to GitHub | "github-actions[bot]" | "Test User" | +| `committer_email` | No | Email Address of the user who will commit the changes to GitHub | "github-actions[bot]@users.noreply.github.com" | "test@test.com" | +| `commit_message` | No | Commit message for the commits created by the action | "Update GitHub Action Versions" | "Custom Commit Message" | +| `pull_request_title` | No | Title of the pull requests generated by the action | "Update GitHub Action Versions" | "Custom PR Title" | +| `pull_request_branch` (Experimental) | No | The pull request branch name. (If provided, the action will force push to the branch) | "gh-actions-update-" | "github/actions-update" | +| `ignore` | No | A comma separated string of GitHub Actions to ignore updates for | `null` | "actions/checkout@v2, actions/cache@v2" | +| `skip_pull_request` | No | If **"true"**, the action will only check for updates and if any update is found the job will fail and update the build summary with the diff (**Options:** "true", "false") | "false" | "true" | +| `update_version_with` | No | Use The Latest Release Tag/Commit SHA or Default Branch Commit SHA to update the actions (**options:** "release-tag", "release-commit-sha", "default-branch-sha"') | "release-tag" | "release-commit-sha" | +| `release_types` | No | A comma separated string of release types to use when updating the actions. By default, all release types are used to update the actions. Only Applicable for **"release-tag", "release-commit-sha"** (**Options:** "major", "minor", "patch" **[one or many seperated by comma]**) | "all" | "minor, patch" | +| `pull_request_user_reviewers` | No | A comma separated string (usernames) which denotes the users that should be added as reviewers to the pull request | `null` | "octocat, hubot, other_user" | +| `pull_request_team_reviewers` | No | A comma separated string (team slugs) which denotes the teams that should be added as reviewers to the pull request | `null` | "justice-league, other_team" | +| `extra_workflow_locations` | No | A comma separated string of file or directory paths to look for workflows. By default, only the workflow files in the `.github/workflows` directory are checked updates | `null` | "path/to/directory, path/to/workflow.yaml" | #### Workflow with all options @@ -133,6 +134,8 @@ jobs: pull_request_user_reviewers: "octocat, hubot, other_user" pull_request_team_reviewers: "justice-league, other_team" extra_workflow_locations: "path/to/directory, path/to/workflow.yaml" + # [Experimental] + pull_request_branch: "actions-update" ``` ### Important Note diff --git a/action.yaml b/action.yaml index cd3f8f9..ee94932 100644 --- a/action.yaml +++ b/action.yaml @@ -21,6 +21,10 @@ inputs: description: 'Title of the pull requests generated by the action' required: false default: 'Update GitHub Action Versions' + pull_request_branch: + description: 'The pull request branch name (If provided, the action will force push to the branch)' + required: false + default: '' ignore: description: 'A comma separated string of GitHub Actions to ignore updates for' required: false diff --git a/src/config.py b/src/config.py index 7dd6417..6351655 100644 --- a/src/config.py +++ b/src/config.py @@ -1,5 +1,6 @@ import json import os +import time from collections.abc import Mapping from pathlib import Path from typing import Any, NamedTuple @@ -47,6 +48,7 @@ class Configuration(NamedTuple): git_committer_username: str = "github-actions[bot]" git_committer_email: str = "github-actions[bot]@users.noreply.github.com" pull_request_title: str = "Update GitHub Action Versions" + pull_request_branch: str | None = None commit_message: str = "Update GitHub Action Versions" ignore_actions: set[str] = set() update_version_with: str = LATEST_RELEASE_TAG @@ -55,6 +57,15 @@ class Configuration(NamedTuple): release_types: list[str] = ALL_RELEASE_TYPES extra_workflow_paths: set[str] = set() + def get_pull_request_branch_name(self) -> tuple[bool, str]: + """ + Get the pull request branch name. + If the branch name is provided by the user set the force push flag to True + """ + if self.pull_request_branch is None: + return (False, f"gh-actions-update-{int(time.time())}") + return (True, self.pull_request_branch) + @property def git_commit_author(self) -> str: """git_commit_author option""" @@ -81,6 +92,7 @@ def get_user_config(cls, env: Mapping[str, str | None]) -> dict[str, str | None] "git_committer_username": env.get("INPUT_COMMITTER_USERNAME"), "git_committer_email": env.get("INPUT_COMMITTER_EMAIL"), "pull_request_title": env.get("INPUT_PULL_REQUEST_TITLE"), + "pull_request_branch": env.get("INPUT_PULL_REQUEST_BRANCH"), "commit_message": env.get("INPUT_COMMIT_MESSAGE"), "ignore_actions": env.get("INPUT_IGNORE"), "update_version_with": env.get("INPUT_UPDATE_VERSION_WITH"), @@ -196,3 +208,15 @@ def clean_extra_workflow_paths(value: Any) -> set[str] | None: ) return workflow_file_paths + + @staticmethod + def clean_pull_request_branch(value: Any) -> str | None: + if value and isinstance(value, str): + if value.lower() in ["main", "master"]: + gha_utils.error( + "Invalid input for `pull_request_branch` field, " + "the action does not support `main` or `master` branches" + ) + raise SystemExit(1) + return value + return None diff --git a/src/main.py b/src/main.py index 7539c1f..f44e7e8 100644 --- a/src/main.py +++ b/src/main.py @@ -77,12 +77,16 @@ def run(self) -> None: gha_utils.append_job_summary(pull_request_body) if not self.user_config.skip_pull_request: - new_branch_name = f"gh-actions-update-{int(time.time())}" + ( + force_push, + new_branch_name, + ) = self.user_config.get_pull_request_branch_name() create_new_git_branch(self.env.base_branch, new_branch_name) git_commit_changes( self.user_config.commit_message, self.user_config.git_commit_author, new_branch_name, + force_push, ) pull_request_number = create_pull_request( self.user_config.pull_request_title, @@ -92,13 +96,14 @@ def run(self) -> None: pull_request_body, self.user_config.github_token, ) - add_pull_request_reviewers( - self.env.repository, - pull_request_number, - self.user_config.pull_request_user_reviewers, - self.user_config.pull_request_team_reviewers, - self.user_config.github_token, - ) + if pull_request_number is not None: + add_pull_request_reviewers( + self.env.repository, + pull_request_number, + self.user_config.pull_request_user_reviewers, + self.user_config.pull_request_team_reviewers, + self.user_config.github_token, + ) else: add_git_diff_to_job_summary() gha_utils.error( diff --git a/src/run_git.py b/src/run_git.py index 9f7002c..bd54413 100644 --- a/src/run_git.py +++ b/src/run_git.py @@ -37,7 +37,10 @@ def create_new_git_branch(base_branch_name: str, new_branch_name: str) -> None: def git_commit_changes( - commit_message: str, commit_author: str, commit_branch_name: str + commit_message: str, + commit_author: str, + commit_branch_name: str, + force_push: bool = False, ) -> None: """ Commit the changed files. @@ -47,7 +50,13 @@ def git_commit_changes( run_subprocess_command( ["git", "commit", f"--author={commit_author}", "-m", commit_message] ) - run_subprocess_command(["git", "push", "-u", "origin", commit_branch_name]) + push_command = ["git", "push", "-u"] + + if force_push: + push_command.append("-f") + + push_command.extend(["origin", commit_branch_name]) + run_subprocess_command(push_command) def git_has_changes() -> bool: diff --git a/src/utils.py b/src/utils.py index d9db48d..674cc39 100644 --- a/src/utils.py +++ b/src/utils.py @@ -24,7 +24,7 @@ def create_pull_request( head_branch_name: str, body: str, github_token: str | None = None, -) -> int: +) -> int | None: """Create pull request on GitHub""" with gha_utils.group("Create Pull Request"): url = f"https://api.github.com/repos/{repository_name}/pulls" @@ -46,6 +46,13 @@ def create_pull_request( ) return response_data["number"] + elif ( + response.status_code == 422 + and "A pull request already exists for" in response.text + ): + gha_utils.notice("A pull request already exists") + return None + gha_utils.error( f"Could not create a pull request on " f"{repository_name}, GitHub API Response: {response.json()}"