Compare commits

...

27 Commits

Author SHA1 Message Date
484d8bd85d Merge pull request #72 from peter-evans/dev
Logging and handling of PR events from forks
2019-11-13 23:59:38 +09:00
74b6a9337d Update documentation 2019-11-13 23:52:33 +09:00
2dd62d446e Bump version 2019-11-13 19:10:53 +09:00
b6a98c049d Add logging and handling for pr events from forks 2019-11-13 19:10:38 +09:00
67e8822279 Update examples 2019-11-13 11:05:55 +09:00
956a240181 Update examples 2019-11-11 22:49:11 +09:00
179aca65d7 Update examples 2019-11-11 22:45:23 +09:00
e4c8b68e33 Update dockerhub-description workflow 2019-11-10 22:08:30 +09:00
731211fd81 Fix bug with filtering pull requests 2019-11-10 18:14:10 +09:00
006313b45a Fix bug when updating a pull request 2019-11-10 16:56:55 +09:00
3c86e9a421 Add outputs description 2019-11-10 13:07:11 +09:00
e49ebca896 Merge pull request #69 from peter-evans/renovate/pin-dependencies
Pin dependency @zeit/ncc to 0.20.5
2019-11-09 23:36:02 +09:00
d9652cf06a Pin dependency @zeit/ncc to 0.20.5 2019-11-09 14:06:18 +00:00
4517bf7ce3 Merge pull request #68 from peter-evans/dev
Convert to hybrid multi-platfrom
2019-11-09 23:06:00 +09:00
04aa84a2af Update examples 2019-11-09 22:48:30 +09:00
11baa5dc07 Update examples 2019-11-09 22:46:01 +09:00
1281ebd51a Update README 2019-11-09 18:22:57 +09:00
46dc4f23d5 Update workflows 2019-11-09 18:22:03 +09:00
d469aaf0ef Update workflows 2019-11-09 17:59:23 +09:00
5692e041c6 Set a step output 2019-11-09 17:42:11 +09:00
55e7b1ec28 Update action to hybrid multi platform 2019-11-09 17:24:47 +09:00
d99b58f9d5 Merge pull request #67 from peter-evans/renovate/pygithub-1.x
Update dependency PyGithub to v1.44.1
2019-11-08 20:31:06 +09:00
b8ce455e56 Update dependency PyGithub to v1.44.1 2019-11-07 03:57:40 +00:00
c9e477ec05 Update examples 2019-11-05 14:10:23 +09:00
979d417e9d Update examples 2019-11-05 13:23:16 +09:00
b74a77cefc Update examples 2019-11-04 12:19:19 +09:00
2af7d9797a Update documentation 2019-11-04 11:13:18 +09:00
18 changed files with 5476 additions and 174 deletions

View File

@ -1,4 +1,4 @@
name: create-pull-request workflow name: Create Pull Request All Platforms
on: on:
repository_dispatch: repository_dispatch:
types: [create-pull-request-multi] types: [create-pull-request-multi]
@ -18,24 +18,27 @@ jobs:
if: matrix.platform == 'windows-latest' if: matrix.platform == 'windows-latest'
run: echo %DATE% %TIME% > report.txt run: echo %DATE% %TIME% > report.txt
- name: Create Pull Request - name: Create Pull Request
uses: peter-evans/create-pull-request@multi-platform-release id: cpr
env: uses: ./
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with:
COMMIT_MESSAGE: Add report file token: ${{ secrets.GITHUB_TOKEN }}
COMMIT_AUTHOR_EMAIL: peter-evans@users.noreply.github.com commit-message: Add report file
COMMIT_AUTHOR_NAME: Peter Evans author-email: peter-evans@users.noreply.github.com
PULL_REQUEST_TITLE: '[Example] Add report file' author-name: Peter Evans
PULL_REQUEST_BODY: | title: '[Example] Add report file'
body: |
New report New report
- Contains *today's* date - Contains *today's* date
- Auto-generated by [create-pull-request][1] - Auto-generated by [create-pull-request][1]
[1]: https://github.com/peter-evans/create-pull-request [1]: https://github.com/peter-evans/create-pull-request
PULL_REQUEST_LABELS: report, automated pr labels: report, automated pr
PULL_REQUEST_ASSIGNEES: peter-evans assignees: peter-evans
PULL_REQUEST_REVIEWERS: peter-evans reviewers: peter-evans
PULL_REQUEST_MILESTONE: 1 milestone: 1
PULL_REQUEST_BRANCH: example-patches branch: example-patches
BRANCH_SUFFIX: 'random' branch-suffix: random
- name: Check output environment variable - name: Check outputs
run: echo "Pull Request Number - ${{ env.PULL_REQUEST_NUMBER }}" run: |
echo "Pull Request Number - ${{ env.PULL_REQUEST_NUMBER }}"
echo "Pull Request Number - ${{ steps.cpr.outputs.pr_number }}"

View File

@ -1,4 +1,4 @@
name: create-pull-request workflow name: Create Pull Request
on: on:
repository_dispatch: repository_dispatch:
types: [create-pull-request] types: [create-pull-request]
@ -10,24 +10,27 @@ jobs:
- name: Create report file - name: Create report file
run: date +%s > report.txt run: date +%s > report.txt
- name: Create Pull Request - name: Create Pull Request
id: cpr
uses: ./ uses: ./
env: with:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} token: ${{ secrets.GITHUB_TOKEN }}
COMMIT_MESSAGE: Add report file commit-message: Add report file
COMMIT_AUTHOR_EMAIL: peter-evans@users.noreply.github.com author-email: peter-evans@users.noreply.github.com
COMMIT_AUTHOR_NAME: Peter Evans author-name: Peter Evans
PULL_REQUEST_TITLE: '[Example] Add report file' title: '[Example] Add report file'
PULL_REQUEST_BODY: | body: |
New report New report
- Contains *today's* date - Contains *today's* date
- Auto-generated by [create-pull-request][1] - Auto-generated by [create-pull-request][1]
[1]: https://github.com/peter-evans/create-pull-request [1]: https://github.com/peter-evans/create-pull-request
PULL_REQUEST_LABELS: report, automated pr labels: report, automated pr
PULL_REQUEST_ASSIGNEES: peter-evans assignees: peter-evans
PULL_REQUEST_REVIEWERS: peter-evans reviewers: peter-evans
PULL_REQUEST_MILESTONE: 1 milestone: 1
PULL_REQUEST_BRANCH: example-patches branch: example-patches
BRANCH_SUFFIX: short-commit-hash branch-suffix: short-commit-hash
- name: Check output environment variable - name: Check outputs
run: echo "Pull Request Number - ${{ env.PULL_REQUEST_NUMBER }}" run: |
echo "Pull Request Number - ${{ env.PULL_REQUEST_NUMBER }}"
echo "Pull Request Number - ${{ steps.cpr.outputs.pr_number }}"

View File

@ -2,7 +2,10 @@ name: Update Docker Hub Description
on: on:
push: push:
branches: branches:
- master - master
paths:
- README.md
- .github/workflows/dockerhub-description.yml
jobs: jobs:
dockerHubDescription: dockerHubDescription:
runs-on: ubuntu-latest runs-on: ubuntu-latest

1
.gitignore vendored
View File

@ -1 +1,2 @@
node_modules
.DS_Store .DS_Store

View File

@ -1,20 +0,0 @@
FROM alpine:3.10.3
LABEL maintainer="Peter Evans <mail@peterevans.dev>"
LABEL repository="https://github.com/peter-evans/create-pull-request"
LABEL homepage="https://github.com/peter-evans/create-pull-request"
LABEL com.github.actions.name="Create Pull Request"
LABEL com.github.actions.description="Creates a pull request for changes to your repository in the actions workspace"
LABEL com.github.actions.icon="git-pull-request"
LABEL com.github.actions.color="gray-dark"
COPY LICENSE README.md /
RUN apk add python3-dev git git-lfs
COPY requirements.txt /tmp/
RUN pip3 install --requirement /tmp/requirements.txt
COPY create-pull-request.py /create-pull-request.py
ENTRYPOINT [ "/create-pull-request.py" ]

107
README.md
View File

@ -17,54 +17,54 @@ Create Pull Request action will:
See [examples](examples.md) for detailed use cases. See [examples](examples.md) for detailed use cases.
Linux
```yml ```yml
- name: Create Pull Request - name: Create Pull Request
uses: peter-evans/create-pull-request@v1.6.0 uses: peter-evans/create-pull-request@v1
env: with:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} token: ${{ secrets.GITHUB_TOKEN }}
``` ```
Multi platform - Linux, MacOS, Windows (beta) You can also pin to a [specific release](https://github.com/peter-evans/create-pull-request/releases) version in the format `@v1.x.x`
```yml
- name: Create Pull Request
uses: peter-evans/create-pull-request@v1.6.0-multi
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
```
**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`. **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. See [this issue](https://github.com/peter-evans/create-pull-request/issues/48) for further details.
### Environment variables ### Action inputs
These variables are *all optional*. If not set, sensible default values will be used. These inputs are *all optional*. If not set, sensible default values will be used.
| Name | Description | Default | | Name | Description | Default |
| --- | --- | --- | | --- | --- | --- |
| `COMMIT_MESSAGE` | The message to use when committing changes. | `Auto-committed changes by create-pull-request action` | | `commit-message` | The message to use when committing changes. | `Auto-committed changes by create-pull-request action` |
| `COMMIT_AUTHOR_EMAIL` | The email address of the commit author. | For `push` events, the HEAD commit author. Otherwise, <GITHUB_ACTOR>@users.noreply.github.com, where `GITHUB_ACTOR` is the GitHub user that initiated the event. | | `author-email` | The email address of the commit author. | For `push` events, the HEAD commit author. Otherwise, <GITHUB_ACTOR>@users.noreply.github.com, where `GITHUB_ACTOR` is the GitHub user that initiated the event. |
| `COMMIT_AUTHOR_NAME` | The name of the commit author. | For `push` events, the HEAD commit author. Otherwise, <GITHUB_ACTOR>, the GitHub user that initiated the event. | | `author-name` | The name of the commit author. | For `push` events, the HEAD commit author. Otherwise, <GITHUB_ACTOR>, the GitHub user that initiated the event. |
| `PULL_REQUEST_TITLE` | The title of the pull request. | `Auto-generated by create-pull-request action` | | `title` | The title of the pull request. | `Auto-generated by create-pull-request action` |
| `PULL_REQUEST_BODY` | The body of the pull request. | `Auto-generated pull request by [create-pull-request](https://github.com/peter-evans/create-pull-request) GitHub Action` | | `body` | The body of the pull request. | `Auto-generated pull request by [create-pull-request](https://github.com/peter-evans/create-pull-request) GitHub Action` |
| `PULL_REQUEST_LABELS` | A comma separated list of labels. | none | | `labels` | A comma separated list of labels. | none |
| `PULL_REQUEST_ASSIGNEES` | A comma separated list of assignees (GitHub usernames). | none | | `assignees` | A comma separated list of assignees (GitHub usernames). | none |
| `PULL_REQUEST_REVIEWERS` | A comma separated list of reviewers (GitHub usernames) to request a review from. | none | | `reviewers` | A comma separated list of reviewers (GitHub usernames) to request a review from. | none |
| `PULL_REQUEST_TEAM_REVIEWERS` | A comma separated list of GitHub teams to request a review from. | none | | `team-reviewers` | A comma separated list of GitHub teams to request a review from. | none |
| `PULL_REQUEST_MILESTONE` | The number of the milestone to associate this pull request with. | none | | `milestone` | The number of the milestone to associate this pull request with. | none |
| `PULL_REQUEST_BRANCH` | The branch name. See **Branch naming** below for details. | `create-pull-request/patch` | | `branch` | The branch name. See **Branch naming** below for details. | `create-pull-request/patch` |
| `PULL_REQUEST_BASE` | Overrides the base branch. **Use with caution!** | Defaults to the currently checked out branch. | | `base` | Overrides the base branch. **Use with caution!** | Defaults to the currently checked out branch. |
| `BRANCH_SUFFIX` | The branch suffix type. Valid values are `short-commit-hash`, `timestamp`, `random` and `none`. See **Branch naming** below for details. | `short-commit-hash` | | `branch-suffix` | The branch suffix type. Valid values are `short-commit-hash`, `timestamp`, `random` and `none`. See **Branch naming** below for details. | `short-commit-hash` |
**Output environment variables** **Outputs**
- `PULL_REQUEST_NUMBER` - The number of the pull request created. The pull request number is output as both an environment variable and a step output.
Note that in order to read the step output the action step must have an id.
**Debug environment variables** ```yml
- name: Create Pull Request
The following parameter is available for debugging and troubleshooting. id: cpr
uses: peter-evans/create-pull-request@v1
- `DEBUG_EVENT` - If present, outputs the event data that triggered the workflow. with:
token: ${{ secrets.GITHUB_TOKEN }}
- name: Check outputs
run: |
echo "Pull Request Number - ${{ env.PULL_REQUEST_NUMBER }}"
echo "Pull Request Number - ${{ steps.cpr.outputs.pr_number }}"
```
### Branch naming ### Branch naming
@ -73,7 +73,7 @@ For branch naming there are two strategies. Always create a new branch each time
#### Strategy A - Always create a new pull request branch (default) #### Strategy A - Always create a new pull request branch (default)
For this strategy there are three options to suffix the branch name. For this strategy there are three options to suffix the branch name.
The branch name is defined by the variable `PULL_REQUEST_BRANCH` and defaults to `create-pull-request/patch`. The following options are values for `BRANCH_SUFFIX`. The branch name is defined by the input `branch` and defaults to `create-pull-request/patch`. The following options are values for `branch-suffix`.
- `short-commit-hash` (default) - Commits will be made to a branch suffixed with the short SHA1 commit hash. e.g. `create-pull-request/patch-fcdfb59`, `create-pull-request/patch-394710b` - `short-commit-hash` (default) - Commits will be made to a branch suffixed with the short SHA1 commit hash. e.g. `create-pull-request/patch-fcdfb59`, `create-pull-request/patch-394710b`
@ -83,7 +83,7 @@ The branch name is defined by the variable `PULL_REQUEST_BRANCH` and defaults to
#### Strategy B - Create and update a pull request branch #### Strategy B - Create and update a pull request branch
To use this strategy, set `BRANCH_SUFFIX` to the value `none`. The variable `PULL_REQUEST_BRANCH` defaults to `create-pull-request/patch`. Commits will be made to this branch and a pull request created. Any subsequent changes will be committed to the *same* branch and reflected in the existing pull request. To use this strategy, set `branch-suffix` to the value `none`. The input `branch` defaults to `create-pull-request/patch`. Commits will be made to this branch and a pull request created. Any subsequent changes will be committed to the *same* branch and reflected in the existing pull request.
### Ignoring files ### Ignoring files
@ -91,7 +91,7 @@ If there are files or directories you want to ignore you can simply add them to
## Reference Example ## Reference Example
The following workflow is a reference example that sets all the main environment variables. The following workflow is a reference example that sets all the main inputs.
See [examples](examples.md) for more realistic use cases. See [examples](examples.md) for more realistic use cases.
@ -106,27 +106,30 @@ jobs:
- name: Create report file - name: Create report file
run: date +%s > report.txt run: date +%s > report.txt
- name: Create Pull Request - name: Create Pull Request
uses: peter-evans/create-pull-request@v1.6.0 id: cpr
env: uses: peter-evans/create-pull-request@v1
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with:
COMMIT_MESSAGE: Add report file token: ${{ secrets.GITHUB_TOKEN }}
COMMIT_AUTHOR_EMAIL: peter-evans@users.noreply.github.com commit-message: Add report file
COMMIT_AUTHOR_NAME: Peter Evans author-email: peter-evans@users.noreply.github.com
PULL_REQUEST_TITLE: '[Example] Add report file' author-name: Peter Evans
PULL_REQUEST_BODY: | title: '[Example] Add report file'
body: |
New report New report
- Contains *today's* date - Contains *today's* date
- Auto-generated by [create-pull-request][1] - Auto-generated by [create-pull-request][1]
[1]: https://github.com/peter-evans/create-pull-request [1]: https://github.com/peter-evans/create-pull-request
PULL_REQUEST_LABELS: report, automated pr labels: report, automated pr
PULL_REQUEST_ASSIGNEES: peter-evans assignees: peter-evans
PULL_REQUEST_REVIEWERS: peter-evans reviewers: peter-evans
PULL_REQUEST_MILESTONE: 1 milestone: 1
PULL_REQUEST_BRANCH: example-patches branch: example-patches
BRANCH_SUFFIX: short-commit-hash branch-suffix: short-commit-hash
- name: Check output environment variable - name: Check outputs
run: echo "Pull Request Number - ${{ env.PULL_REQUEST_NUMBER }}" run: |
echo "Pull Request Number - ${{ env.PULL_REQUEST_NUMBER }}"
echo "Pull Request Number - ${{ steps.cpr.outputs.pr_number }}"
``` ```
This reference configuration will create pull requests that look like this: This reference configuration will create pull requests that look like this:

View File

@ -1,9 +1,41 @@
name: 'Create Pull Request' name: 'Create Pull Request'
author: 'Peter Evans'
description: 'Creates a pull request for changes to your repository in the actions workspace' description: 'Creates a pull request for changes to your repository in the actions workspace'
inputs:
token:
description: 'The GitHub authentication token'
required: true
commit-message:
description: 'The message to use when committing changes.'
author-email:
description: 'The email address of the commit author.'
author-name:
description: 'The name of the commit author.'
title:
description: 'The title of the pull request.'
body:
description: 'The body of the pull request.'
labels:
description: 'A comma separated list of labels.'
assignees:
description: 'A comma separated list of assignees (GitHub usernames).'
reviewers:
description: 'A comma separated list of reviewers (GitHub usernames) to request a review from.'
team-reviewers:
description: 'A comma separated list of GitHub teams to request a review from.'
milestone:
description: 'The number of the milestone to associate this pull request with.'
branch:
description: 'The pull request branch name.'
base:
description: 'Overrides the base branch.'
branch-suffix:
description: 'The branch suffix type.'
outputs:
pr_number:
description: 'The pull request number'
runs: runs:
using: 'docker' using: 'node12'
image: 'docker://peterevans/create-pull-request:1.6.1' main: 'dist/index.js'
branding: branding:
icon: 'git-pull-request' icon: 'git-pull-request'
color: 'gray-dark' color: 'gray-dark'

4524
dist/index.js vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@ -7,8 +7,7 @@ import string
import sys import sys
import time import time
from git import Repo from git import Repo
from github import Github from github import Github, GithubException
from github import GithubException
def get_github_event(github_event_path): 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): 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.email', '"%s"' % email)
git.config('--global', 'user.name', '"%s"' % name) 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): def checkout_branch(git, remote_exists, branch):
if remote_exists: if remote_exists:
print("Checking out branch '%s'" % branch)
git.stash('--include-untracked') git.stash('--include-untracked')
git.checkout(branch) git.checkout(branch)
try: try:
@ -66,6 +67,7 @@ def checkout_branch(git, remote_exists, branch):
git.checkout('--theirs', '.') git.checkout('--theirs', '.')
git.reset() git.reset()
else: else:
print("Creating new branch '%s'" % branch)
git.checkout('HEAD', b=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') pull_request_team_reviewers = os.environ.get('PULL_REQUEST_TEAM_REVIEWERS')
# Push the local changes to the remote branch # 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) push_result = push_changes(repo.git, branch, commit_message)
print(push_result) print(push_result)
@ -114,50 +116,57 @@ def process_event(github_token, github_repository, repo, branch, base):
base=base, base=base,
head=branch) head=branch)
print("Created pull request #%d (%s => %s)" % print("Created pull request #%d (%s => %s)" %
(pull_request.number, branch, base)) (pull_request.number, branch, base))
except GithubException as e: except GithubException as e:
if e.status == 422: 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( pull_request = github_repo.get_pulls(
state='open', state='open',
base=base, base=base,
head=branch)[1] head=head_branch)[0]
print("Updated pull request #%d (%s => %s)" % print("Updated pull request #%d (%s => %s)" %
(pull_request.number, branch, base)) (pull_request.number, branch, base))
else: else:
print(str(e)) print(str(e))
sys.exit(1) sys.exit(1)
# Set the output variable # Set the output variables
os.system( os.system(
'echo ::set-env name=PULL_REQUEST_NUMBER::%d' % 'echo ::set-env name=PULL_REQUEST_NUMBER::%d' %
pull_request.number) pull_request.number)
os.system(
'echo ::set-output name=pr_number::%d' %
pull_request.number)
# Set labels, assignees and milestone # Set labels, assignees and milestone
if pull_request_labels is not None: 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)) pull_request.as_issue().edit(labels=cs_string_to_list(pull_request_labels))
if pull_request_assignees is not None: 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)) pull_request.as_issue().edit(assignees=cs_string_to_list(pull_request_assignees))
if pull_request_milestone is not None: 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)) milestone = github_repo.get_milestone(int(pull_request_milestone))
pull_request.as_issue().edit(milestone=milestone) pull_request.as_issue().edit(milestone=milestone)
# Set pull request reviewers # Set pull request reviewers
if pull_request_reviewers is not None: if pull_request_reviewers is not None:
print("Requesting reviewers") print("Requesting reviewers '%s'" % pull_request_reviewers)
try: try:
pull_request.create_review_request( pull_request.create_review_request(
reviewers=cs_string_to_list(pull_request_reviewers)) reviewers=cs_string_to_list(pull_request_reviewers))
except GithubException as e: 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: if e.status == 422:
print("Requesting reviewers failed - %s" % e.data["message"]) print("Requesting reviewers failed - %s" % e.data["message"])
# Set pull request team reviewers # Set pull request team reviewers
if pull_request_team_reviewers is not None: 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( pull_request.create_review_request(
team_reviewers=cs_string_to_list(pull_request_team_reviewers)) team_reviewers=cs_string_to_list(pull_request_team_reviewers))
@ -192,16 +201,29 @@ base_override = os.environ.get('PULL_REQUEST_BASE')
# Set the base branch # Set the base branch
if base_override is not None: if base_override is not None:
base = base_override base = base_override
print("Overriding the base with branch '%s'" % base)
checkout_branch(repo.git, True, base) checkout_branch(repo.git, True, base)
elif github_ref.startswith('refs/pull/'): 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 # Switch to the merging branch instead of the merge commit
base = os.environ['GITHUB_HEAD_REF'] 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: else:
base = github_ref[11:] 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. # 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): if base.startswith(branch_prefix):
print("Branch '%s' was created by this action. Skipping." % base) print("Branch '%s' was created by this action. Skipping." % base)
sys.exit() sys.exit()
@ -226,10 +248,15 @@ else:
branch_suffix) branch_suffix)
sys.exit(1) sys.exit(1)
# Check if the remote branch exists # Output head branch
remote_exists = remote_branch_exists(repo, 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: if remote_exists:
print(
"Pull request branch '%s' already exists as remote branch 'origin/%s'" %
(branch, branch))
if branch_suffix == 'short-commit-hash': if branch_suffix == 'short-commit-hash':
# A remote branch already exists for the HEAD commit # A remote branch already exists for the HEAD commit
print( print(
@ -237,9 +264,9 @@ if remote_exists:
branch) branch)
sys.exit() sys.exit()
elif branch_suffix in ['timestamp', 'random']: elif branch_suffix in ['timestamp', 'random']:
# Generated branch name clash with an existing branch # Generated branch name collision with an existing branch
print( 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) branch)
sys.exit(1) sys.exit(1)
@ -247,8 +274,15 @@ if remote_exists:
checkout_branch(repo.git, remote_exists, branch) checkout_branch(repo.git, remote_exists, branch)
# Check if there are changes to pull request # 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: 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( process_event(
github_token, github_token,
github_repository, github_repository,
@ -256,4 +290,4 @@ if repo.is_dirty() or len(repo.untracked_files) > 0:
branch, branch,
base) base)
else: else:
print("Repository has no modified or untracked files. Skipping.") print("No modified or untracked files detected. Skipping.")

View File

@ -1,2 +1,2 @@
GitPython==3.0.4 GitPython==3.0.4
PyGithub==1.44 PyGithub==1.44.1

52
dist/src/setup-python.js vendored Normal file
View File

@ -0,0 +1,52 @@
const core = require("@actions/core");
const tc = require("@actions/tool-cache");
const path = require("path");
const semver = require("semver");
/**
* Setup for Python from the GitHub Actions tool cache
* Converted from https://github.com/actions/setup-python
*
* @param {string} versionSpec version of Python
* @param {string} arch architecture (x64|x32)
*/
let setupPython = function(versionSpec, arch) {
return new Promise((resolve, reject) => {
const IS_WINDOWS = process.platform === "win32";
// Find the version of Python we want in the tool cache
const installDir = tc.find("Python", versionSpec, arch);
core.debug(`installDir: ${installDir}`);
// Set paths
core.exportVariable("pythonLocation", installDir);
core.addPath(installDir);
if (IS_WINDOWS) {
core.addPath(path.join(installDir, "Scripts"));
} else {
core.addPath(path.join(installDir, "bin"));
}
if (IS_WINDOWS) {
// Add --user directory
// `installDir` from tool cache should look like $AGENT_TOOLSDIRECTORY/Python/<semantic version>/x64/
// So if `findLocalTool` succeeded above, we must have a conformant `installDir`
const version = path.basename(path.dirname(installDir));
const major = semver.major(version);
const minor = semver.minor(version);
const userScriptsDir = path.join(
process.env["APPDATA"] || "",
"Python",
`Python${major}${minor}`,
"Scripts"
);
core.addPath(userScriptsDir);
}
// On Linux and macOS, pip will create the --user directory and add it to PATH as needed.
resolve();
});
};
module.exports = setupPython;

View File

@ -3,12 +3,17 @@
- [Use case: Create a pull request to update X periodically](#use-case-create-a-pull-request-to-update-x-periodically) - [Use case: Create a pull request to update X periodically](#use-case-create-a-pull-request-to-update-x-periodically)
- [Update NPM dependencies](#update-npm-dependencies) - [Update NPM dependencies](#update-npm-dependencies)
- [Keep Go up to date](#keep-go-up-to-date) - [Keep Go up to date](#keep-go-up-to-date)
- [Update SwaggerUI for GitHub Pages](#update-swaggerui-for-github-pages)
- [Spider and download a website](#spider-and-download-a-website) - [Spider and download a website](#spider-and-download-a-website)
- [Use case: Create a pull request to update X by calling the GitHub API](#use-case-create-a-pull-request-to-update-x-by-calling-the-github-api)
- [Call the GitHub API from an external service](#call-the-github-api-from-an-external-service)
- [Call the GitHub API from another GitHub Actions workflow](#call-the-github-api-from-another-github-actions-workflow)
- [Use case: Create a pull request to modify/fix pull requests](#use-case-create-a-pull-request-to-modifyfix-pull-requests) - [Use case: Create a pull request to modify/fix pull requests](#use-case-create-a-pull-request-to-modifyfix-pull-requests)
- [autopep8](#autopep8) - [autopep8](#autopep8)
- [Misc workflow tips](#misc-workflow-tips) - [Misc workflow tips](#misc-workflow-tips)
- [Filtering push events](#filtering-push-events) - [Filtering push events](#filtering-push-events)
- [Dynamic configuration using variables](#dynamic-configuration-using-variables) - [Dynamic configuration using variables](#dynamic-configuration-using-variables)
- [Debugging GitHub Actions](#debugging-github-actions)
## Use case: Create a pull request to update X periodically ## Use case: Create a pull request to update X periodically
@ -37,14 +42,14 @@ jobs:
ncu -u ncu -u
npm install npm install
- name: Create Pull Request - name: Create Pull Request
uses: peter-evans/create-pull-request@v1.6.0 uses: peter-evans/create-pull-request@v1
env: with:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} token: ${{ secrets.GITHUB_TOKEN }}
COMMIT_MESSAGE: update dependencies commit-message: update dependencies
PULL_REQUEST_TITLE: Automated Dependency Updates title: Automated Dependency Updates
PULL_REQUEST_BODY: This is an auto-generated PR with dependency updates. body: This is an auto-generated PR with dependency updates.
PULL_REQUEST_BRANCH: dep-updates branch: dep-updates
BRANCH_SUFFIX: none branch-suffix: none
``` ```
### Keep Go up to date ### Keep Go up to date
@ -71,18 +76,79 @@ jobs:
- run: echo "##[set-output name=pr_title;]update to latest Go release ${{ steps.ensure_go.outputs.go_version}}" - run: echo "##[set-output name=pr_title;]update to latest Go release ${{ steps.ensure_go.outputs.go_version}}"
id: pr_title_maker id: pr_title_maker
- name: Create pull request - name: Create pull request
uses: peter-evans/create-pull-request@v1.6.0 uses: peter-evans/create-pull-request@v1
with:
token: ${{ secrets.GITHUB_TOKEN }}
title: ${{ steps.pr_title_maker.outputs.pr_title }}
body: Auto-generated pull request created by the GitHub Actions [create-pull-request](https://github.com/peter-evans/create-pull-request) and [ensure-latest-go](https://github.com/jmhodges/ensure-latest-go).
commit-message: ${{ steps.pr_title_maker.outputs.pr_title }}
branch-suffix: none
branch: ensure-latest-go/patch-${{ steps.ensure_go.outputs.go_version }}
```
### Update SwaggerUI for GitHub Pages
When using [GitHub Pages to host Swagger documentation](https://github.com/peter-evans/swagger-github-pages), this workflow updates the repository with the latest distribution of [SwaggerUI](https://github.com/swagger-api/swagger-ui).
You must create a file called `swagger-ui.version` at the root of your repository before running.
```yml
name: Update Swagger UI
on:
schedule:
- cron: '0 10 * * *'
jobs:
updateSwagger:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: Get Latest Swagger UI Release
id: swagger-ui
run: |
echo ::set-output name=release_tag::$(curl -sL https://api.github.com/repos/swagger-api/swagger-ui/releases/latest | jq -r ".tag_name")
echo ::set-output name=current_tag::$(<swagger-ui.version)
- name: Update Swagger UI
if: steps.swagger-ui.outputs.current_tag != steps.swagger-ui.outputs.release_tag
env: env:
PULL_REQUEST_TITLE: ${{ steps.pr_title_maker.outputs.pr_title }} RELEASE_TAG: ${{ steps.swagger-ui.outputs.release_tag }}
PULL_REQUEST_BODY: Auto-generated pull request created by the GitHub Actions [create-pull-request](https://github.com/peter-evans/create-pull-request) and [ensure-latest-go](https://github.com/jmhodges/ensure-latest-go). SWAGGER_YAML: "swagger.yaml"
COMMIT_MESSAGE: ${{ steps.pr_title_maker.outputs.pr_title }} run: |
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Delete the dist directory and index.html
BRANCH_SUFFIX: none rm -fr dist index.html
PULL_REQUEST_BRANCH: ensure-latest-go/patch-${{ steps.ensure_go.outputs.go_version }} # Download the release
curl -sL -o $RELEASE_TAG https://api.github.com/repos/swagger-api/swagger-ui/tarball/$RELEASE_TAG
# Extract the dist directory
tar -xzf $RELEASE_TAG --strip-components=1 $(tar -tzf $RELEASE_TAG | head -1 | cut -f1 -d"/")/dist
rm $RELEASE_TAG
# Move index.html to the root
mv dist/index.html .
# Fix references in index.html
sed -i "s|https://petstore.swagger.io/v2/swagger.json|$SWAGGER_YAML|g" index.html
sed -i "s|href=\"./|href=\"dist/|g" index.html
sed -i "s|src=\"./|src=\"dist/|g" index.html
# Update current release
echo ${{ steps.swagger-ui.outputs.release_tag }} > swagger-ui.version
- name: Create Pull Request
uses: peter-evans/create-pull-request@v1
with:
token: ${{ secrets.GITHUB_TOKEN }}
commit-message: Update swagger-ui to ${{ steps.swagger-ui.outputs.release_tag }}
title: Update SwaggerUI to ${{ steps.swagger-ui.outputs.release_tag }}
body: |
Updates [swagger-ui][1] to ${{ steps.swagger-ui.outputs.release_tag }}
Auto-generated by [create-pull-request][2]
[1]: https://github.com/swagger-api/swagger-ui
[2]: https://github.com/peter-evans/create-pull-request
labels: dependencies, automated pr
branch: swagger-ui-updates
branch-suffix: none
``` ```
### Spider and download a website ### Spider and download a website
This workflow spiders a website and downloads the content. Any changes to the website will be raised in a pull request.
```yml ```yml
name: Download Website name: Download Website
on: on:
@ -106,20 +172,67 @@ jobs:
--domains quotes.toscrape.com \ --domains quotes.toscrape.com \
http://quotes.toscrape.com/ http://quotes.toscrape.com/
- name: Create Pull Request - name: Create Pull Request
uses: peter-evans/create-pull-request@v1.6.0 uses: peter-evans/create-pull-request@v1
env: with:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} token: ${{ secrets.GITHUB_TOKEN }}
COMMIT_MESSAGE: update local website copy commit-message: update local website copy
PULL_REQUEST_TITLE: Automated Updates to Local Website Copy title: Automated Updates to Local Website Copy
PULL_REQUEST_BODY: This is an auto-generated PR with website updates. body: This is an auto-generated PR with website updates.
PULL_REQUEST_BRANCH: website-updates branch: website-updates
BRANCH_SUFFIX: none branch-suffix: none
```
## Use case: Create a pull request to update X by calling the GitHub API
You can use the GitHub API to trigger a webhook event called [`repository_dispatch`](https://help.github.com/en/github/automating-your-workflow-with-github-actions/events-that-trigger-workflows#external-events-repository_dispatch) when you want to trigger a workflow for activity that happens outside of GitHub.
This pattern will work well for updating any kind of static content from an external source.
You can modify any of the examples in the previous section to work in this fashion.
Set the workflow to execute `on: repository_dispatch`.
```yml
on:
repository_dispatch:
types: [create-pull-request]
```
### Call the GitHub API from an external service
An `on: repository_dispatch` workflow can be triggered by a call to the GitHub API as follows.
- `[username]` is a GitHub username
- `[token]` is a `repo` scoped [Personal Access Token](https://help.github.com/en/articles/creating-a-personal-access-token-for-the-command-line)
- `[repository]` is the name of the repository the workflow resides in.
```
curl -XPOST -u "[username]:[token]" \
-H "Accept: application/vnd.github.everest-preview+json" \
-H "Content-Type: application/json" \
https://api.github.com/repos/[username]/[repository]/dispatches \
--data '{"event_type": "create-pull-request"}'
```
### Call the GitHub API from another GitHub Actions workflow
An `on: repository_dispatch` workflow can be triggered from another workflow with [repository-dispatch](https://github.com/peter-evans/repository-dispatch) action.
```yml
- name: Repository Dispatch
uses: peter-evans/repository-dispatch@v1.0.0
with:
token: ${{ secrets.REPO_ACCESS_TOKEN }}
repository: username/my-repo
event-type: create-pull-request
client-payload: '{"ref": "${{ github.ref }}", "sha": "${{ github.sha }}"}'
``` ```
## Use case: Create a pull request to modify/fix pull requests ## Use case: Create a pull request to modify/fix pull requests
This is a pattern that works well for any automated code linting and fixing. A pull request can be created to fix or modify something during an `on: pull_request` workflow. The pull request containing the fix will be raised with the original pull request as the base. This can be then be merged to update the original pull request and pass any required tests. This is a pattern that works well for any automated code linting and fixing. A pull request can be created to fix or modify something during an `on: pull_request` workflow. The pull request containing the fix will be raised with the original pull request as the base. This can be then be merged to update the original pull request and pass any required tests.
Note that due to [limitations on forked repositories](https://help.github.com/en/actions/automating-your-workflow-with-github-actions/authenticating-with-the-github_token#permissions-for-the-github_token) workflows for this use case do not work for pull requests raised from forks.
### autopep8 ### autopep8
The following is an example workflow for a use case where [autopep8 action](https://github.com/peter-evans/autopep8) runs as both a check on pull requests and raises a further pull request to apply code fixes. The following is an example workflow for a use case where [autopep8 action](https://github.com/peter-evans/autopep8) runs as both a check on pull requests and raises a further pull request to apply code fixes.
@ -136,7 +249,8 @@ name: autopep8
on: pull_request on: pull_request
jobs: jobs:
autopep8: autopep8:
if: startsWith(github.head_ref, 'autopep8-patches') == false # Check if the PR is not raised by this workflow and is not from a fork
if: startsWith(github.head_ref, 'autopep8-patches') == false && github.event.pull_request.head.repo.full_name == github.repository
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v1 - uses: actions/checkout@v1
@ -150,15 +264,15 @@ jobs:
run: echo ::set-output name=branch-name::"autopep8-patches/$GITHUB_HEAD_REF" run: echo ::set-output name=branch-name::"autopep8-patches/$GITHUB_HEAD_REF"
- name: Create Pull Request - name: Create Pull Request
if: steps.autopep8.outputs.exit-code == 2 if: steps.autopep8.outputs.exit-code == 2
uses: peter-evans/create-pull-request@v1.6.0 uses: peter-evans/create-pull-request@v1
env: with:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} token: ${{ secrets.GITHUB_TOKEN }}
COMMIT_MESSAGE: autopep8 action fixes commit-message: autopep8 action fixes
PULL_REQUEST_TITLE: Fixes by autopep8 action title: Fixes by autopep8 action
PULL_REQUEST_BODY: This is an auto-generated PR with fixes by autopep8. body: This is an auto-generated PR with fixes by autopep8.
PULL_REQUEST_LABELS: autopep8, automated pr labels: autopep8, automated pr
PULL_REQUEST_BRANCH: ${{ steps.vars.outputs.branch-name }} branch: ${{ steps.vars.outputs.branch-name }}
BRANCH_SUFFIX: none branch-suffix: none
- name: Fail if autopep8 made changes - name: Fail if autopep8 made changes
if: steps.autopep8.outputs.exit-code == 2 if: steps.autopep8.outputs.exit-code == 2
run: exit 1 run: exit 1
@ -197,14 +311,14 @@ 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) \ 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)." by [create-pull-request](https://github.com/peter-evans/create-pull-request)."
- name: Create Pull Request - name: Create Pull Request
uses: peter-evans/create-pull-request@v1.6.0 uses: peter-evans/create-pull-request@v1
env: with:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} token: ${{ secrets.GITHUB_TOKEN }}
PULL_REQUEST_TITLE: ${{ steps.vars.outputs.pr_title }} title: ${{ steps.vars.outputs.pr_title }}
PULL_REQUEST_BODY: ${{ steps.vars.outputs.pr_body }} body: ${{ steps.vars.outputs.pr_body }}
``` ```
Since the action reads environment variables from the system, it's technically not necessary to explicitly pass them as long as they exist in the environment. So the following method using `set-env` *also* works, but explicitly passing the configuration parameters using the previous method is perferred for its clarity. Alternatively, [`set-env`](https://help.github.com/en/github/automating-your-workflow-with-github-actions/development-tools-for-github-actions#set-an-environment-variable-set-env) can be used to create environment variables.
```yml ```yml
- name: Set environment variables - name: Set environment variables
@ -213,7 +327,48 @@ Since the action reads environment variables from the system, it's technically n
echo ::set-env name=PULL_REQUEST_BODY::"This PR was auto-generated on $(date +%d-%m-%Y) \ 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)." by [create-pull-request](https://github.com/peter-evans/create-pull-request)."
- name: Create Pull Request - name: Create Pull Request
uses: peter-evans/create-pull-request@v1.6.0 uses: peter-evans/create-pull-request@v1
env: with:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} token: ${{ secrets.GITHUB_TOKEN }}
title: ${{ env.PULL_REQUEST_TITLE }}
body: ${{ env.PULL_REQUEST_BODY }}
```
### Debugging GitHub Actions
#### Step Debug Logging
To enable [step debug logging](https://help.github.com/en/actions/automating-your-workflow-with-github-actions/managing-a-workflow-run#enabling-step-debug-logging) set the secret `ACTIONS_STEP_DEBUG` to `true` in the repository that contains the workflow.
#### Output Various Contexts
```yml
- name: Dump event JSON
env:
EVENT_JSON_FILENAME: ${{ github.event_path }}
run: cat "$EVENT_JSON_FILENAME"
- name: Dump GitHub context
env:
GITHUB_CONTEXT: ${{ toJson(github) }}
run: echo "$GITHUB_CONTEXT"
- name: Dump job context
env:
JOB_CONTEXT: ${{ toJson(job) }}
run: echo "$JOB_CONTEXT"
- name: Dump steps context
env:
STEPS_CONTEXT: ${{ toJson(steps) }}
run: echo "$STEPS_CONTEXT"
- name: Dump runner context
env:
RUNNER_CONTEXT: ${{ toJson(runner) }}
run: echo "$RUNNER_CONTEXT"
- name: Dump strategy context
env:
STRATEGY_CONTEXT: ${{ toJson(strategy) }}
run: echo "$STRATEGY_CONTEXT"
- name: Dump matrix context
env:
MATRIX_CONTEXT: ${{ toJson(matrix) }}
run: echo "$MATRIX_CONTEXT"
``` ```

66
index.js Normal file
View File

@ -0,0 +1,66 @@
const { inspect } = require("util");
const core = require("@actions/core");
const exec = require("@actions/exec");
const setupPython = require("./src/setup-python");
async function run() {
try {
// Allows ncc to find assets to be included in the distribution
const src = __dirname + "/src";
core.debug(`src: ${src}`);
// Setup Python from the tool cache
setupPython("3.8.0", "x64");
// Install requirements
await exec.exec("pip", [
"install",
"--requirement",
`${src}/requirements.txt`
]);
// Fetch action inputs
const inputs = {
token: core.getInput("token"),
commitMessage: core.getInput("commit-message"),
commitAuthorEmail: core.getInput("author-email"),
commitAuthorName: core.getInput("author-name"),
title: core.getInput("title"),
body: core.getInput("body"),
labels: core.getInput("labels"),
assignees: core.getInput("assignees"),
reviewers: core.getInput("reviewers"),
teamReviewers: core.getInput("team-reviewers"),
milestone: core.getInput("milestone"),
branch: core.getInput("branch"),
base: core.getInput("base"),
branchSuffix: core.getInput("branch-suffix"),
debugEvent: core.getInput("debug-event")
};
core.debug(`Inputs: ${inspect(inputs)}`);
// Set environment variables from inputs.
if (inputs.token) process.env.GITHUB_TOKEN = inputs.token;
if (inputs.commitMessage) process.env.COMMIT_MESSAGE = inputs.commitMessage;
if (inputs.commitAuthorEmail) process.env.COMMIT_AUTHOR_EMAIL = inputs.commitAuthorEmail;
if (inputs.commitAuthorName) process.env.COMMIT_AUTHOR_NAME = inputs.commitAuthorName;
if (inputs.title) process.env.PULL_REQUEST_TITLE = inputs.title;
if (inputs.body) process.env.PULL_REQUEST_BODY = inputs.body;
if (inputs.labels) process.env.PULL_REQUEST_LABELS = inputs.labels;
if (inputs.assignees) process.env.PULL_REQUEST_ASSIGNEES = inputs.assignees;
if (inputs.reviewers) process.env.PULL_REQUEST_REVIEWERS = inputs.reviewers;
if (inputs.teamReviewers) process.env.PULL_REQUEST_TEAM_REVIEWERS = inputs.teamReviewers;
if (inputs.milestone) process.env.PULL_REQUEST_MILESTONE = inputs.milestone;
if (inputs.branch) process.env.PULL_REQUEST_BRANCH = inputs.branch;
if (inputs.base) process.env.PULL_REQUEST_BASE = inputs.base;
if (inputs.branchSuffix) process.env.BRANCH_SUFFIX = inputs.branchSuffix;
if (inputs.debugEvent) process.env.DEBUG_EVENT = inputs.debugEvent;
// Execute python script
await exec.exec("python", [`${src}/create-pull-request.py`]);
} catch (error) {
core.setFailed(error.message);
}
}
run();

71
package-lock.json generated Normal file
View File

@ -0,0 +1,71 @@
{
"name": "create-pull-request",
"version": "1.7.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"@actions/core": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@actions/core/-/core-1.2.0.tgz",
"integrity": "sha512-ZKdyhlSlyz38S6YFfPnyNgCDZuAF2T0Qv5eHflNWytPS8Qjvz39bZFMry9Bb/dpSnqWcNeav5yM2CTYpJeY+Dw=="
},
"@actions/exec": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@actions/exec/-/exec-1.0.1.tgz",
"integrity": "sha512-nvFkxwiicvpzNiCBF4wFBDfnBvi7xp/as7LE1hBxBxKG2L29+gkIPBiLKMVORL+Hg3JNf07AKRfl0V5djoypjQ=="
},
"@actions/io": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@actions/io/-/io-1.0.1.tgz",
"integrity": "sha512-rhq+tfZukbtaus7xyUtwKfuiCRXd1hWSfmJNEpFgBQJ4woqPEpsBw04awicjwz9tyG2/MVhAEMfVn664Cri5zA=="
},
"@actions/tool-cache": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@actions/tool-cache/-/tool-cache-1.1.2.tgz",
"integrity": "sha512-IJczPaZr02ECa3Lgws/TJEVco9tjOujiQSZbO3dHuXXjhd5vrUtfOgGwhmz3/f97L910OraPZ8SknofUk6RvOQ==",
"requires": {
"@actions/core": "^1.1.0",
"@actions/exec": "^1.0.1",
"@actions/io": "^1.0.1",
"semver": "^6.1.0",
"typed-rest-client": "^1.4.0",
"uuid": "^3.3.2"
}
},
"@zeit/ncc": {
"version": "0.20.5",
"resolved": "https://registry.npmjs.org/@zeit/ncc/-/ncc-0.20.5.tgz",
"integrity": "sha512-XU6uzwvv95DqxciQx+aOLhbyBx/13ky+RK1y88Age9Du3BlA4mMPCy13BGjayOrrumOzlq1XV3SD/BWiZENXlw==",
"dev": true
},
"semver": {
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw=="
},
"tunnel": {
"version": "0.0.4",
"resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.4.tgz",
"integrity": "sha1-LTeFoVjBdMmhbcLARuxfxfF0IhM="
},
"typed-rest-client": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/typed-rest-client/-/typed-rest-client-1.5.0.tgz",
"integrity": "sha512-DVZRlmsfnTjp6ZJaatcdyvvwYwbWvR4YDNFDqb+qdTxpvaVP99YCpBkA8rxsLtAPjBVoDe4fNsnMIdZTiPuKWg==",
"requires": {
"tunnel": "0.0.4",
"underscore": "1.8.3"
}
},
"underscore": {
"version": "1.8.3",
"resolved": "https://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz",
"integrity": "sha1-Tz+1OxBuYJf8+ctBCfKl6b36UCI="
},
"uuid": {
"version": "3.3.3",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.3.tgz",
"integrity": "sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ=="
}
}
}

28
package.json Normal file
View File

@ -0,0 +1,28 @@
{
"name": "create-pull-request",
"version": "1.7.3",
"description": "Creates a pull request for changes to your repository in the actions workspace",
"main": "index.js",
"scripts": {
"build": "ncc build index.js -o dist"
},
"repository": {
"type": "git",
"url": "git+https://github.com/peter-evans/create-pull-request.git"
},
"keywords": [],
"author": "Peter Evans",
"license": "MIT",
"bugs": {
"url": "https://github.com/peter-evans/create-pull-request/issues"
},
"homepage": "https://github.com/peter-evans/create-pull-request",
"dependencies": {
"@actions/core": "^1.1.1",
"@actions/exec": "^1.0.1",
"@actions/tool-cache": "^1.1.2"
},
"devDependencies": {
"@zeit/ncc": "0.20.5"
}
}

293
src/create-pull-request.py Executable file
View File

@ -0,0 +1,293 @@
#!/usr/bin/env python3
''' Create Pull Request '''
import json
import os
import random
import string
import sys
import time
from git import Repo
from github import Github, GithubException
def get_github_event(github_event_path):
with open(github_event_path) as f:
github_event = json.load(f)
if bool(os.environ.get('DEBUG_EVENT')):
print(os.environ['GITHUB_EVENT_NAME'])
print(json.dumps(github_event, sort_keys=True, indent=2))
return github_event
def get_head_short_sha1(repo):
return repo.git.rev_parse('--short', 'HEAD')
def get_random_suffix(size=7, chars=string.ascii_lowercase + string.digits):
return ''.join(random.choice(chars) for _ in range(size))
def remote_branch_exists(repo, branch):
for ref in repo.remotes.origin.refs:
if ref.name == ("origin/%s" % branch):
return True
return False
def get_author_default(event_name, event_data):
if event_name == "push":
email = "{head_commit[author][email]}".format(**event_data)
name = "{head_commit[author][name]}".format(**event_data)
else:
email = os.environ['GITHUB_ACTOR'] + '@users.noreply.github.com'
name = os.environ['GITHUB_ACTOR']
return email, name
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)
def set_git_remote_url(git, token, github_repository):
git.remote(
'set-url', 'origin', "https://x-access-token:%s@github.com/%s" %
(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:
git.stash('pop')
except BaseException:
git.checkout('--theirs', '.')
git.reset()
else:
print("Creating new branch '%s'" % branch)
git.checkout('HEAD', b=branch)
def push_changes(git, branch, commit_message):
git.add('-A')
git.commit(m=commit_message)
return git.push('-f', '--set-upstream', 'origin', branch)
def cs_string_to_list(str):
# Split the comma separated string into a list
l = [i.strip() for i in str.split(',')]
# Remove empty strings
return list(filter(None, l))
def process_event(github_token, github_repository, repo, branch, base):
# Fetch optional environment variables with default values
commit_message = os.getenv(
'COMMIT_MESSAGE',
"Auto-committed changes by create-pull-request action")
title = os.getenv(
'PULL_REQUEST_TITLE',
"Auto-generated by create-pull-request action")
body = os.getenv(
'PULL_REQUEST_BODY', "Auto-generated pull request by "
"[create-pull-request](https://github.com/peter-evans/create-pull-request) GitHub Action")
# Fetch optional environment variables with no default values
pull_request_labels = os.environ.get('PULL_REQUEST_LABELS')
pull_request_assignees = os.environ.get('PULL_REQUEST_ASSIGNEES')
pull_request_milestone = os.environ.get('PULL_REQUEST_MILESTONE')
pull_request_reviewers = os.environ.get('PULL_REQUEST_REVIEWERS')
pull_request_team_reviewers = os.environ.get('PULL_REQUEST_TEAM_REVIEWERS')
# Push the local changes to the remote branch
print("Pushing changes to 'origin/%s'" % branch)
push_result = push_changes(repo.git, branch, commit_message)
print(push_result)
# Create the pull request
github_repo = Github(github_token).get_repo(github_repository)
try:
pull_request = github_repo.create_pull(
title=title,
body=body,
base=base,
head=branch)
print("Created pull request #%d (%s => %s)" %
(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',
base=base,
head=head_branch)[0]
print("Updated pull request #%d (%s => %s)" %
(pull_request.number, branch, base))
else:
print(str(e))
sys.exit(1)
# Set the output variables
os.system(
'echo ::set-env name=PULL_REQUEST_NUMBER::%d' %
pull_request.number)
os.system(
'echo ::set-output name=pr_number::%d' %
pull_request.number)
# Set labels, assignees and milestone
if pull_request_labels is not None:
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 '%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 '%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 '%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."
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 '%s'" % pull_request_team_reviewers)
pull_request.create_review_request(
team_reviewers=cs_string_to_list(pull_request_team_reviewers))
# Fetch environment variables
github_token = os.environ['GITHUB_TOKEN']
github_repository = os.environ['GITHUB_REPOSITORY']
github_ref = os.environ['GITHUB_REF']
event_name = os.environ['GITHUB_EVENT_NAME']
# Get the JSON event data
event_data = get_github_event(os.environ['GITHUB_EVENT_PATH'])
# Set the repo to the working directory
repo = Repo(os.getcwd())
# Get the default for author email and name
author_email, author_name = get_author_default(event_name, event_data)
# Set commit author overrides
author_email = os.getenv('COMMIT_AUTHOR_EMAIL', author_email)
author_name = os.getenv('COMMIT_AUTHOR_NAME', author_name)
# Set git configuration
set_git_config(repo.git, author_email, author_name)
# Update URL for the 'origin' remote
set_git_remote_url(repo.git, github_token, github_repository)
# Fetch/Set the branch name
branch_prefix = os.getenv(
'PULL_REQUEST_BRANCH',
'create-pull-request/patch')
# Fetch an optional base branch override
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']
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 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()
# Fetch an optional environment variable to determine the branch suffix
branch_suffix = os.getenv('BRANCH_SUFFIX', 'short-commit-hash')
if branch_suffix == "short-commit-hash":
# Suffix with the short SHA1 hash
branch = "%s-%s" % (branch_prefix, get_head_short_sha1(repo))
elif branch_suffix == "timestamp":
# Suffix with the current timestamp
branch = "%s-%s" % (branch_prefix, int(time.time()))
elif branch_suffix == "random":
# Suffix with the current timestamp
branch = "%s-%s" % (branch_prefix, get_random_suffix())
elif branch_suffix == "none":
# Fixed branch name
branch = branch_prefix
else:
print(
"Branch suffix '%s' is not a valid value." %
branch_suffix)
sys.exit(1)
# 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(
"Pull request branch '%s' already exists for this commit. Skipping." %
branch)
sys.exit()
elif branch_suffix in ['timestamp', 'random']:
# Generated branch name collision with an existing branch
print(
"Pull request branch '%s' collided with a branch of the same name. Please re-run." %
branch)
sys.exit(1)
# Checkout branch
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("Modified or untracked files detected.")
process_event(
github_token,
github_repository,
repo,
branch,
base)
else:
print("No modified or untracked files detected. Skipping.")

2
src/requirements.txt Normal file
View File

@ -0,0 +1,2 @@
GitPython==3.0.4
PyGithub==1.44.1

52
src/setup-python.js Normal file
View File

@ -0,0 +1,52 @@
const core = require("@actions/core");
const tc = require("@actions/tool-cache");
const path = require("path");
const semver = require("semver");
/**
* Setup for Python from the GitHub Actions tool cache
* Converted from https://github.com/actions/setup-python
*
* @param {string} versionSpec version of Python
* @param {string} arch architecture (x64|x32)
*/
let setupPython = function(versionSpec, arch) {
return new Promise((resolve, reject) => {
const IS_WINDOWS = process.platform === "win32";
// Find the version of Python we want in the tool cache
const installDir = tc.find("Python", versionSpec, arch);
core.debug(`installDir: ${installDir}`);
// Set paths
core.exportVariable("pythonLocation", installDir);
core.addPath(installDir);
if (IS_WINDOWS) {
core.addPath(path.join(installDir, "Scripts"));
} else {
core.addPath(path.join(installDir, "bin"));
}
if (IS_WINDOWS) {
// Add --user directory
// `installDir` from tool cache should look like $AGENT_TOOLSDIRECTORY/Python/<semantic version>/x64/
// So if `findLocalTool` succeeded above, we must have a conformant `installDir`
const version = path.basename(path.dirname(installDir));
const major = semver.major(version);
const minor = semver.minor(version);
const userScriptsDir = path.join(
process.env["APPDATA"] || "",
"Python",
`Python${major}${minor}`,
"Scripts"
);
core.addPath(userScriptsDir);
}
// On Linux and macOS, pip will create the --user directory and add it to PATH as needed.
resolve();
});
};
module.exports = setupPython;