From b32dea480cc8b032cbd697f8a66935da7a593066 Mon Sep 17 00:00:00 2001 From: Jean-Marie Lemetayer Date: Wed, 2 Nov 2022 14:20:27 +0100 Subject: [PATCH 1/6] Add pull request branch configuration --- README.md | 1 + action.yaml | 4 ++++ src/config.py | 2 ++ src/main.py | 20 ++++++++++++-------- src/run_git.py | 2 +- src/utils.py | 9 ++++++++- 6 files changed, 28 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 4e93fae..140c545 100644 --- a/README.md +++ b/README.md @@ -86,6 +86,7 @@ These are the inputs that can be provided on the workflow. | `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` | No | The pull request branch name. | "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" | diff --git a/action.yaml b/action.yaml index cd3f8f9..6dfdfb9 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' + required: false + default: 'gh-actions-update-' 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..cfaa1c0 100644 --- a/src/config.py +++ b/src/config.py @@ -47,6 +47,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 @@ -81,6 +82,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"), diff --git a/src/main.py b/src/main.py index 7539c1f..05ab01c 100644 --- a/src/main.py +++ b/src/main.py @@ -77,7 +77,10 @@ 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())}" + new_branch_name = self.user_config.pull_request_branch + if new_branch_name is None: + new_branch_name = f"gh-actions-update-{int(time.time())}" + create_new_git_branch(self.env.base_branch, new_branch_name) git_commit_changes( self.user_config.commit_message, @@ -92,13 +95,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..77b3d31 100644 --- a/src/run_git.py +++ b/src/run_git.py @@ -47,7 +47,7 @@ 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]) + run_subprocess_command(["git", "push", "-fu", "origin", commit_branch_name]) def git_has_changes() -> bool: diff --git a/src/utils.py b/src/utils.py index d9db48d..d47ecf8 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.warning("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()}" From 6863d260d953ebb3d107c2f46b083a17c42097ec Mon Sep 17 00:00:00 2001 From: Maksudul Haque Date: Sun, 8 Jan 2023 18:58:40 +0600 Subject: [PATCH 2/6] Small Tweaks --- README.md | 2 +- action.yaml | 4 ++-- src/config.py | 16 ++++++++++++++++ src/main.py | 9 +++++---- src/run_git.py | 16 ++++++++++++++-- src/utils.py | 2 +- 6 files changed, 39 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 140c545..0487c7d 100644 --- a/README.md +++ b/README.md @@ -86,7 +86,7 @@ These are the inputs that can be provided on the workflow. | `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` | No | The pull request branch name. | "gh-actions-update-" | "github/actions-update" | +| `pull_request_branch` | 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" | diff --git a/action.yaml b/action.yaml index 6dfdfb9..ee94932 100644 --- a/action.yaml +++ b/action.yaml @@ -22,9 +22,9 @@ inputs: required: false default: 'Update GitHub Action Versions' pull_request_branch: - description: 'The pull request branch name' + description: 'The pull request branch name (If provided, the action will force push to the branch)' required: false - default: 'gh-actions-update-' + 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 cfaa1c0..0d124c1 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 @@ -56,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""" @@ -198,3 +208,9 @@ 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): + return value + return None diff --git a/src/main.py b/src/main.py index 05ab01c..f44e7e8 100644 --- a/src/main.py +++ b/src/main.py @@ -77,15 +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 = self.user_config.pull_request_branch - if new_branch_name is None: - 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, diff --git a/src/run_git.py b/src/run_git.py index 77b3d31..1819877 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,16 @@ def git_commit_changes( run_subprocess_command( ["git", "commit", f"--author={commit_author}", "-m", commit_message] ) - run_subprocess_command(["git", "push", "-fu", "origin", commit_branch_name]) + run_subprocess_command( + [ + "git", + "push", + "-u", + "-f" if force_push else "", + "origin", + commit_branch_name, + ] + ) def git_has_changes() -> bool: diff --git a/src/utils.py b/src/utils.py index d47ecf8..674cc39 100644 --- a/src/utils.py +++ b/src/utils.py @@ -50,7 +50,7 @@ def create_pull_request( response.status_code == 422 and "A pull request already exists for" in response.text ): - gha_utils.warning("A pull request already exists") + gha_utils.notice("A pull request already exists") return None gha_utils.error( From aba39c88ed789dba139a97faa1b249b47f100fe2 Mon Sep 17 00:00:00 2001 From: Maksudul Haque Date: Sun, 8 Jan 2023 19:23:16 +0600 Subject: [PATCH 3/6] Small Tweaks --- src/config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config.py b/src/config.py index 0d124c1..268e70c 100644 --- a/src/config.py +++ b/src/config.py @@ -211,6 +211,6 @@ def clean_extra_workflow_paths(value: Any) -> set[str] | None: @staticmethod def clean_pull_request_branch(value: Any) -> str | None: - if value and isinstance(value, str): + if value and isinstance(value, str) and value.lower() not in ["main", "master"]: return value return None From b8e455c881df7f77abb62f188c9c7a5aec81d5c0 Mon Sep 17 00:00:00 2001 From: Maksudul Haque Date: Sun, 8 Jan 2023 19:26:50 +0600 Subject: [PATCH 4/6] Docs update --- README.md | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 0487c7d..12f45fb 100644 --- a/README.md +++ b/README.md @@ -79,21 +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" | -| `pull_request_branch` | 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" | +| 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 @@ -134,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 From bdd1a62cdfb8b541541ff5d137108be4b53ce59c Mon Sep 17 00:00:00 2001 From: Maksudul Haque Date: Sun, 8 Jan 2023 19:33:02 +0600 Subject: [PATCH 5/6] Fix --- src/run_git.py | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/src/run_git.py b/src/run_git.py index 1819877..bd54413 100644 --- a/src/run_git.py +++ b/src/run_git.py @@ -50,16 +50,13 @@ def git_commit_changes( run_subprocess_command( ["git", "commit", f"--author={commit_author}", "-m", commit_message] ) - run_subprocess_command( - [ - "git", - "push", - "-u", - "-f" if force_push else "", - "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: From 7bac55aa880d84e6c3d23b12e07d37349703dd4c Mon Sep 17 00:00:00 2001 From: Maksudul Haque Date: Sun, 8 Jan 2023 19:41:57 +0600 Subject: [PATCH 6/6] Fix --- src/config.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/config.py b/src/config.py index 268e70c..6351655 100644 --- a/src/config.py +++ b/src/config.py @@ -211,6 +211,12 @@ def clean_extra_workflow_paths(value: Any) -> set[str] | None: @staticmethod def clean_pull_request_branch(value: Any) -> str | None: - if value and isinstance(value, str) and value.lower() not in ["main", "master"]: + 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