From b6a98c049d3f32ce50b5573e812695c03edc0fc4 Mon Sep 17 00:00:00 2001 From: Peter Evans Date: Wed, 13 Nov 2019 19:10:38 +0900 Subject: [PATCH 1/3] Add logging and handling for pr events from forks --- dist/src/create-pull-request.py | 68 +++++++++++++++++++++++---------- src/create-pull-request.py | 68 +++++++++++++++++++++++---------- 2 files changed, 96 insertions(+), 40 deletions(-) diff --git a/dist/src/create-pull-request.py b/dist/src/create-pull-request.py index 80067d0..f8c2ea5 100755 --- a/dist/src/create-pull-request.py +++ b/dist/src/create-pull-request.py @@ -7,8 +7,7 @@ import string import sys import time from git import Repo -from github import Github -from github import GithubException +from github import Github, GithubException def get_github_event(github_event_path): @@ -46,6 +45,7 @@ def get_author_default(event_name, event_data): def set_git_config(git, email, name): + print("Configuring git user as '%s <%s>'" % (name, email)) git.config('--global', 'user.email', '"%s"' % email) git.config('--global', 'user.name', '"%s"' % name) @@ -58,6 +58,7 @@ def set_git_remote_url(git, token, github_repository): def checkout_branch(git, remote_exists, branch): if remote_exists: + print("Checking out branch '%s'" % branch) git.stash('--include-untracked') git.checkout(branch) try: @@ -66,6 +67,7 @@ def checkout_branch(git, remote_exists, branch): git.checkout('--theirs', '.') git.reset() else: + print("Creating new branch '%s'" % branch) git.checkout('HEAD', b=branch) @@ -101,7 +103,7 @@ def process_event(github_token, github_repository, repo, branch, base): pull_request_team_reviewers = os.environ.get('PULL_REQUEST_TEAM_REVIEWERS') # Push the local changes to the remote branch - print("Pushing changes.") + print("Pushing changes to 'origin/%s'" % branch) push_result = push_changes(repo.git, branch, commit_message) print(push_result) @@ -114,18 +116,18 @@ def process_event(github_token, github_repository, repo, branch, base): base=base, head=branch) print("Created pull request #%d (%s => %s)" % - (pull_request.number, branch, base)) + (pull_request.number, branch, base)) except GithubException as e: if e.status == 422: # Format the branch name head_branch = "%s:%s" % (github_repository.split("/")[0], branch) # Get the pull request pull_request = github_repo.get_pulls( - state='open', + state='open', base=base, head=head_branch)[0] print("Updated pull request #%d (%s => %s)" % - (pull_request.number, branch, base)) + (pull_request.number, branch, base)) else: print(str(e)) sys.exit(1) @@ -140,30 +142,31 @@ def process_event(github_token, github_repository, repo, branch, base): # Set labels, assignees and milestone if pull_request_labels is not None: - print("Applying labels") + print("Applying labels '%s'" % pull_request_labels) pull_request.as_issue().edit(labels=cs_string_to_list(pull_request_labels)) if pull_request_assignees is not None: - print("Applying assignees") + print("Applying assignees '%s'" % pull_request_assignees) pull_request.as_issue().edit(assignees=cs_string_to_list(pull_request_assignees)) if pull_request_milestone is not None: - print("Applying milestone") + print("Applying milestone '%s'" % pull_request_milestone) milestone = github_repo.get_milestone(int(pull_request_milestone)) pull_request.as_issue().edit(milestone=milestone) # Set pull request reviewers if pull_request_reviewers is not None: - print("Requesting reviewers") + print("Requesting reviewers '%s'" % pull_request_reviewers) try: pull_request.create_review_request( reviewers=cs_string_to_list(pull_request_reviewers)) except GithubException as e: - # Likely caused by "Review cannot be requested from pull request author." + # Likely caused by "Review cannot be requested from pull request + # author." if e.status == 422: print("Requesting reviewers failed - %s" % e.data["message"]) # Set pull request team reviewers if pull_request_team_reviewers is not None: - print("Requesting team reviewers") + print("Requesting team reviewers '%s'" % pull_request_team_reviewers) pull_request.create_review_request( team_reviewers=cs_string_to_list(pull_request_team_reviewers)) @@ -198,16 +201,29 @@ base_override = os.environ.get('PULL_REQUEST_BASE') # Set the base branch if base_override is not None: base = base_override + print("Overriding the base with branch '%s'" % base) checkout_branch(repo.git, True, base) elif github_ref.startswith('refs/pull/'): + # Check the PR is not raised from a fork of the repository + head_repo = "{pull_request[head][repo][full_name]}".format(**event_data) + if head_repo != github_repository: + print("::warning::Pull request was raised from a fork of the repository. " + + "Limitations on forked repositories have been imposed by GitHub Actions. " + + "Unable to continue. Exiting.") + sys.exit() # Switch to the merging branch instead of the merge commit base = os.environ['GITHUB_HEAD_REF'] - repo.git.checkout(base) + print( + "Removing the merge commit by switching to the pull request head branch '%s'" % + base) + checkout_branch(repo.git, True, base) else: base = github_ref[11:] + print("Currently checked out base assumed to be branch '%s'" % base) # Skip if the current branch is a PR branch created by this action. -# This may occur when using a PAT instead of GITHUB_TOKEN. +# This may occur when using a PAT instead of GITHUB_TOKEN because +# a PAT allows workflow actions to trigger further events. if base.startswith(branch_prefix): print("Branch '%s' was created by this action. Skipping." % base) sys.exit() @@ -232,10 +248,15 @@ else: branch_suffix) sys.exit(1) -# Check if the remote branch exists -remote_exists = remote_branch_exists(repo, branch) +# Output head branch +print("Pull request branch to create/update set to '%s'" % branch) +# Check if the determined head branch exists as a remote +remote_exists = remote_branch_exists(repo, branch) if remote_exists: + print( + "Pull request branch '%s' already exists as remote branch 'origin/%s'" % + (branch, branch)) if branch_suffix == 'short-commit-hash': # A remote branch already exists for the HEAD commit print( @@ -243,9 +264,9 @@ if remote_exists: branch) sys.exit() elif branch_suffix in ['timestamp', 'random']: - # Generated branch name clash with an existing branch + # Generated branch name collision with an existing branch print( - "Pull request branch '%s' already exists. Please re-run." % + "Pull request branch '%s' collided with a branch of the same name. Please re-run." % branch) sys.exit(1) @@ -253,8 +274,15 @@ if remote_exists: checkout_branch(repo.git, remote_exists, branch) # Check if there are changes to pull request +if remote_exists: + print("Checking for local working copy changes indicating a " + + "diff with existing pull request branch 'origin/%s'" % branch) +else: + print("Checking for local working copy changes indicating a " + + "diff with base 'origin/%s'" % base) + if repo.is_dirty() or len(repo.untracked_files) > 0: - print("Repository has modified or untracked files.") + print("Modified or untracked files detected.") process_event( github_token, github_repository, @@ -262,4 +290,4 @@ if repo.is_dirty() or len(repo.untracked_files) > 0: branch, base) else: - print("Repository has no modified or untracked files. Skipping.") + print("No modified or untracked files detected. Skipping.") diff --git a/src/create-pull-request.py b/src/create-pull-request.py index 80067d0..f8c2ea5 100755 --- a/src/create-pull-request.py +++ b/src/create-pull-request.py @@ -7,8 +7,7 @@ import string import sys import time from git import Repo -from github import Github -from github import GithubException +from github import Github, GithubException def get_github_event(github_event_path): @@ -46,6 +45,7 @@ def get_author_default(event_name, event_data): def set_git_config(git, email, name): + print("Configuring git user as '%s <%s>'" % (name, email)) git.config('--global', 'user.email', '"%s"' % email) git.config('--global', 'user.name', '"%s"' % name) @@ -58,6 +58,7 @@ def set_git_remote_url(git, token, github_repository): def checkout_branch(git, remote_exists, branch): if remote_exists: + print("Checking out branch '%s'" % branch) git.stash('--include-untracked') git.checkout(branch) try: @@ -66,6 +67,7 @@ def checkout_branch(git, remote_exists, branch): git.checkout('--theirs', '.') git.reset() else: + print("Creating new branch '%s'" % branch) git.checkout('HEAD', b=branch) @@ -101,7 +103,7 @@ def process_event(github_token, github_repository, repo, branch, base): pull_request_team_reviewers = os.environ.get('PULL_REQUEST_TEAM_REVIEWERS') # Push the local changes to the remote branch - print("Pushing changes.") + print("Pushing changes to 'origin/%s'" % branch) push_result = push_changes(repo.git, branch, commit_message) print(push_result) @@ -114,18 +116,18 @@ def process_event(github_token, github_repository, repo, branch, base): base=base, head=branch) print("Created pull request #%d (%s => %s)" % - (pull_request.number, branch, base)) + (pull_request.number, branch, base)) except GithubException as e: if e.status == 422: # Format the branch name head_branch = "%s:%s" % (github_repository.split("/")[0], branch) # Get the pull request pull_request = github_repo.get_pulls( - state='open', + state='open', base=base, head=head_branch)[0] print("Updated pull request #%d (%s => %s)" % - (pull_request.number, branch, base)) + (pull_request.number, branch, base)) else: print(str(e)) sys.exit(1) @@ -140,30 +142,31 @@ def process_event(github_token, github_repository, repo, branch, base): # Set labels, assignees and milestone if pull_request_labels is not None: - print("Applying labels") + print("Applying labels '%s'" % pull_request_labels) pull_request.as_issue().edit(labels=cs_string_to_list(pull_request_labels)) if pull_request_assignees is not None: - print("Applying assignees") + print("Applying assignees '%s'" % pull_request_assignees) pull_request.as_issue().edit(assignees=cs_string_to_list(pull_request_assignees)) if pull_request_milestone is not None: - print("Applying milestone") + print("Applying milestone '%s'" % pull_request_milestone) milestone = github_repo.get_milestone(int(pull_request_milestone)) pull_request.as_issue().edit(milestone=milestone) # Set pull request reviewers if pull_request_reviewers is not None: - print("Requesting reviewers") + print("Requesting reviewers '%s'" % pull_request_reviewers) try: pull_request.create_review_request( reviewers=cs_string_to_list(pull_request_reviewers)) except GithubException as e: - # Likely caused by "Review cannot be requested from pull request author." + # Likely caused by "Review cannot be requested from pull request + # author." if e.status == 422: print("Requesting reviewers failed - %s" % e.data["message"]) # Set pull request team reviewers if pull_request_team_reviewers is not None: - print("Requesting team reviewers") + print("Requesting team reviewers '%s'" % pull_request_team_reviewers) pull_request.create_review_request( team_reviewers=cs_string_to_list(pull_request_team_reviewers)) @@ -198,16 +201,29 @@ base_override = os.environ.get('PULL_REQUEST_BASE') # Set the base branch if base_override is not None: base = base_override + print("Overriding the base with branch '%s'" % base) checkout_branch(repo.git, True, base) elif github_ref.startswith('refs/pull/'): + # Check the PR is not raised from a fork of the repository + head_repo = "{pull_request[head][repo][full_name]}".format(**event_data) + if head_repo != github_repository: + print("::warning::Pull request was raised from a fork of the repository. " + + "Limitations on forked repositories have been imposed by GitHub Actions. " + + "Unable to continue. Exiting.") + sys.exit() # Switch to the merging branch instead of the merge commit base = os.environ['GITHUB_HEAD_REF'] - repo.git.checkout(base) + print( + "Removing the merge commit by switching to the pull request head branch '%s'" % + base) + checkout_branch(repo.git, True, base) else: base = github_ref[11:] + print("Currently checked out base assumed to be branch '%s'" % base) # Skip if the current branch is a PR branch created by this action. -# This may occur when using a PAT instead of GITHUB_TOKEN. +# This may occur when using a PAT instead of GITHUB_TOKEN because +# a PAT allows workflow actions to trigger further events. if base.startswith(branch_prefix): print("Branch '%s' was created by this action. Skipping." % base) sys.exit() @@ -232,10 +248,15 @@ else: branch_suffix) sys.exit(1) -# Check if the remote branch exists -remote_exists = remote_branch_exists(repo, branch) +# Output head branch +print("Pull request branch to create/update set to '%s'" % branch) +# Check if the determined head branch exists as a remote +remote_exists = remote_branch_exists(repo, branch) if remote_exists: + print( + "Pull request branch '%s' already exists as remote branch 'origin/%s'" % + (branch, branch)) if branch_suffix == 'short-commit-hash': # A remote branch already exists for the HEAD commit print( @@ -243,9 +264,9 @@ if remote_exists: branch) sys.exit() elif branch_suffix in ['timestamp', 'random']: - # Generated branch name clash with an existing branch + # Generated branch name collision with an existing branch print( - "Pull request branch '%s' already exists. Please re-run." % + "Pull request branch '%s' collided with a branch of the same name. Please re-run." % branch) sys.exit(1) @@ -253,8 +274,15 @@ if remote_exists: checkout_branch(repo.git, remote_exists, branch) # Check if there are changes to pull request +if remote_exists: + print("Checking for local working copy changes indicating a " + + "diff with existing pull request branch 'origin/%s'" % branch) +else: + print("Checking for local working copy changes indicating a " + + "diff with base 'origin/%s'" % base) + if repo.is_dirty() or len(repo.untracked_files) > 0: - print("Repository has modified or untracked files.") + print("Modified or untracked files detected.") process_event( github_token, github_repository, @@ -262,4 +290,4 @@ if repo.is_dirty() or len(repo.untracked_files) > 0: branch, base) else: - print("Repository has no modified or untracked files. Skipping.") + print("No modified or untracked files detected. Skipping.") From 2dd62d446e2433685f77039441f7778e8b1c7752 Mon Sep 17 00:00:00 2001 From: Peter Evans Date: Wed, 13 Nov 2019 19:10:53 +0900 Subject: [PATCH 2/3] Bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 5670e1b..5779cc6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "create-pull-request", - "version": "1.7.2", + "version": "1.7.3", "description": "Creates a pull request for changes to your repository in the actions workspace", "main": "index.js", "scripts": { From 74b6a9337d4714aba6b517fec2fece7f7eaaad61 Mon Sep 17 00:00:00 2001 From: Peter Evans Date: Wed, 13 Nov 2019 23:52:28 +0900 Subject: [PATCH 3/3] Update documentation --- README.md | 8 +++++--- examples.md | 14 +++++++------- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 2bacf9c..be83eb7 100644 --- a/README.md +++ b/README.md @@ -19,11 +19,13 @@ See [examples](examples.md) for detailed use cases. ```yml - name: Create Pull Request - uses: peter-evans/create-pull-request@v1.7.2 + uses: peter-evans/create-pull-request@v1 with: token: ${{ secrets.GITHUB_TOKEN }} ``` +You can also pin to a [specific release](https://github.com/peter-evans/create-pull-request/releases) version in the format `@v1.x.x` + **Note**: If you want pull requests created by this action to trigger an `on: pull_request` workflow then you must use a [Personal Access Token](https://help.github.com/en/articles/creating-a-personal-access-token-for-the-command-line) instead of the default `GITHUB_TOKEN`. See [this issue](https://github.com/peter-evans/create-pull-request/issues/48) for further details. @@ -55,7 +57,7 @@ Note that in order to read the step output the action step must have an id. ```yml - name: Create Pull Request id: cpr - uses: peter-evans/create-pull-request@v1.7.2 + uses: peter-evans/create-pull-request@v1 with: token: ${{ secrets.GITHUB_TOKEN }} - name: Check outputs @@ -105,7 +107,7 @@ jobs: run: date +%s > report.txt - name: Create Pull Request id: cpr - uses: peter-evans/create-pull-request@v1.7.2 + uses: peter-evans/create-pull-request@v1 with: token: ${{ secrets.GITHUB_TOKEN }} commit-message: Add report file diff --git a/examples.md b/examples.md index 118da99..2648d85 100644 --- a/examples.md +++ b/examples.md @@ -42,7 +42,7 @@ jobs: ncu -u npm install - name: Create Pull Request - uses: peter-evans/create-pull-request@v1.7.2 + uses: peter-evans/create-pull-request@v1 with: token: ${{ secrets.GITHUB_TOKEN }} commit-message: update dependencies @@ -76,7 +76,7 @@ jobs: - run: echo "##[set-output name=pr_title;]update to latest Go release ${{ steps.ensure_go.outputs.go_version}}" id: pr_title_maker - name: Create pull request - uses: peter-evans/create-pull-request@v1.7.2 + uses: peter-evans/create-pull-request@v1 with: token: ${{ secrets.GITHUB_TOKEN }} title: ${{ steps.pr_title_maker.outputs.pr_title }} @@ -128,7 +128,7 @@ jobs: # Update current release echo ${{ steps.swagger-ui.outputs.release_tag }} > swagger-ui.version - name: Create Pull Request - uses: peter-evans/create-pull-request@v1.7.2 + uses: peter-evans/create-pull-request@v1 with: token: ${{ secrets.GITHUB_TOKEN }} commit-message: Update swagger-ui to ${{ steps.swagger-ui.outputs.release_tag }} @@ -172,7 +172,7 @@ jobs: --domains quotes.toscrape.com \ http://quotes.toscrape.com/ - name: Create Pull Request - uses: peter-evans/create-pull-request@v1.7.2 + uses: peter-evans/create-pull-request@v1 with: token: ${{ secrets.GITHUB_TOKEN }} commit-message: update local website copy @@ -264,7 +264,7 @@ jobs: run: echo ::set-output name=branch-name::"autopep8-patches/$GITHUB_HEAD_REF" - name: Create Pull Request if: steps.autopep8.outputs.exit-code == 2 - uses: peter-evans/create-pull-request@v1.7.2 + uses: peter-evans/create-pull-request@v1 with: token: ${{ secrets.GITHUB_TOKEN }} commit-message: autopep8 action fixes @@ -311,7 +311,7 @@ The recommended method is to use [`set-output`](https://help.github.com/en/githu echo ::set-output name=pr_body::"This PR was auto-generated on $(date +%d-%m-%Y) \ by [create-pull-request](https://github.com/peter-evans/create-pull-request)." - name: Create Pull Request - uses: peter-evans/create-pull-request@v1.7.2 + uses: peter-evans/create-pull-request@v1 with: token: ${{ secrets.GITHUB_TOKEN }} title: ${{ steps.vars.outputs.pr_title }} @@ -327,7 +327,7 @@ Alternatively, [`set-env`](https://help.github.com/en/github/automating-your-wor echo ::set-env name=PULL_REQUEST_BODY::"This PR was auto-generated on $(date +%d-%m-%Y) \ by [create-pull-request](https://github.com/peter-evans/create-pull-request)." - name: Create Pull Request - uses: peter-evans/create-pull-request@v1.7.2 + uses: peter-evans/create-pull-request@v1 with: token: ${{ secrets.GITHUB_TOKEN }} title: ${{ env.PULL_REQUEST_TITLE }}