Compare commits

..

35 Commits

Author SHA1 Message Date
87c27ee3eb Merge pull request #118 from peter-evans/dev
Assume python3 on PATH when running in a container
2020-02-14 00:07:03 +09:00
4beea725d3 Call python3 when running in a container 2020-02-13 17:37:08 +09:00
9d58699da5 Skip python setup when running in a container 2020-02-13 16:26:04 +09:00
bceebba814 Merge pull request #117 from peter-evans/renovate/zeit-ncc-0.x
Update dependency @zeit/ncc to v0.21.1
2020-02-13 09:58:46 +09:00
0b82710b2e Update dependency @zeit/ncc to v0.21.1 2020-02-12 16:54:11 +00:00
4aaaa0e760 Update README 2020-02-11 16:36:16 +09:00
918bfcb8a3 Update README 2020-02-11 16:28:16 +09:00
1ecfd1ae40 Merge pull request #116 from peter-evans/dev
Update dependency PyGithub to v1.46
2020-02-11 14:03:17 +09:00
4dd195d7c3 Update vendored dependencies 2020-02-11 13:51:59 +09:00
72b5f45bb4 Update dependency PyGithub to v1.46 2020-02-11 13:49:50 +09:00
5394814b39 Update documentation 2020-02-11 01:21:05 +09:00
83918398f5 Update documentation 2020-02-11 01:15:59 +09:00
b6a458d96a Merge pull request #114 from peter-evans/dev
Add support for ssh protocol
2020-02-10 23:06:03 +09:00
7b5ff6b642 Add support for ssh protocol 2020-02-10 22:52:00 +09:00
82eddd8828 Merge pull request #112 from peter-evans/dev
Skip python setup for alpine linux
2020-02-10 18:47:40 +09:00
6df2a462d1 Skip python setup for alpine linux 2020-02-10 09:53:43 +09:00
3689bd07d7 Merge pull request #110 from peter-evans/dev
Update dependency GitPython to v3.0.7
2020-02-08 16:45:18 +09:00
943f19ac64 Update vendored dependencies 2020-02-08 16:12:36 +09:00
e59d6c7fff Update dependency GitPython to v3.0.7 2020-02-08 16:09:50 +09:00
c65a4f39b3 Update documentation 2020-02-08 16:04:23 +09:00
2d18371789 Update documentation 2020-02-08 12:52:06 +09:00
cc7020a609 Merge pull request #107 from peter-evans/dev
Determine target github repository from git config
2020-02-07 19:01:09 +09:00
d8700620d6 Determine target github repository from git config 2020-02-07 11:05:01 +09:00
b7064071dc Move assets 2020-01-25 16:29:22 +09:00
339e82d37b Update documentation 2020-01-24 13:00:38 +09:00
a319015452 Merge pull request #102 from peter-evans/dev
Vendor python dependencies
2020-01-23 18:59:19 +09:00
df0d07269b Vendor python dependencies 2020-01-23 16:59:19 +09:00
2aa04baf2e Merge pull request #101 from peter-evans/dev
Update title and body when pr exists
2020-01-23 09:54:43 +09:00
d29f1e9296 Update title and body when pr exists 2020-01-23 09:47:35 +09:00
8ee12880b0 Update documentation 2020-01-21 22:35:00 +09:00
8d39de8771 Update documentation 2020-01-21 22:27:02 +09:00
c202684c92 Merge pull request #98 from peter-evans/dev
Follow the python minor version
2020-01-17 17:48:11 +09:00
e970adccb4 Follow the python minor version 2020-01-17 17:39:10 +09:00
9f9af3e969 Merge pull request #97 from peter-evans/dev
Update Python to version 3.8.1
2020-01-17 15:52:01 +09:00
615d7c82e3 Update Python to version 3.8.1 2020-01-17 15:46:49 +09:00
34 changed files with 434 additions and 37 deletions

3
.gitignore vendored
View File

@ -1,3 +1,6 @@
__pycache__
.python-version
node_modules
.DS_Store

View File

@ -1,4 +1,4 @@
# <img width="24" height="24" src="assets/logo.svg"> Create Pull Request
# <img width="24" height="24" src="docs/assets/logo.svg"> Create Pull Request
[![GitHub Marketplace](https://img.shields.io/badge/Marketplace-Create%20Pull%20Request-blue.svg?colorA=24292e&colorB=0366d6&style=flat&longCache=true&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAM6wAADOsB5dZE0gAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAERSURBVCiRhZG/SsMxFEZPfsVJ61jbxaF0cRQRcRJ9hlYn30IHN/+9iquDCOIsblIrOjqKgy5aKoJQj4O3EEtbPwhJbr6Te28CmdSKeqzeqr0YbfVIrTBKakvtOl5dtTkK+v4HfA9PEyBFCY9AGVgCBLaBp1jPAyfAJ/AAdIEG0dNAiyP7+K1qIfMdonZic6+WJoBJvQlvuwDqcXadUuqPA1NKAlexbRTAIMvMOCjTbMwl1LtI/6KWJ5Q6rT6Ht1MA58AX8Apcqqt5r2qhrgAXQC3CZ6i1+KMd9TRu3MvA3aH/fFPnBodb6oe6HM8+lYHrGdRXW8M9bMZtPXUji69lmf5Cmamq7quNLFZXD9Rq7v0Bpc1o/tp0fisAAAAASUVORK5CYII=)](https://github.com/marketplace/actions/create-pull-request)
A GitHub action to create a pull request for changes to your repository in the actions workspace.
@ -18,7 +18,7 @@ Create Pull Request action will:
## Documentation
- [Concepts and guidelines](docs/concepts-guidelines.md)
- [Concepts, guidelines and advanced usage](docs/concepts-guidelines.md)
- [Examples](docs/examples.md)
- [Updating from v1](docs/updating.md)
@ -37,8 +37,7 @@ You can also pin to a [specific release](https://github.com/peter-evans/create-p
With the exception of `token`, all inputs are **optional**. If not set, sensible default values will be used.
**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.
**Note**: If you want pull requests created by this action to trigger an `on: push` or `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`. Alternatively, allow the action to [push using SSH](https://github.com/peter-evans/create-pull-request/blob/master/docs/concepts-guidelines.md#push-using-ssh-deploy-keys) by configuring a deploy key.
| Name | Description | Default |
| --- | --- | --- |
@ -194,7 +193,7 @@ jobs:
This reference configuration will create pull requests that look like this:
![Pull Request Example](assets/pull-request-example.png)
![Pull Request Example](docs/assets/pull-request-example.png)
## License

68
dist/index.js vendored
View File

@ -1001,6 +1001,7 @@ module.exports = require("os");
/***/ (function(__unusedmodule, __unusedexports, __webpack_require__) {
const { inspect } = __webpack_require__(669);
const isDocker = __webpack_require__(160);
const core = __webpack_require__(470);
const exec = __webpack_require__(986);
const setupPython = __webpack_require__(139);
@ -1011,14 +1012,32 @@ async function run() {
const src = __webpack_require__.ab + "src";
core.debug(`src: ${src}`);
// Setup Python from the tool cache
setupPython("3.8.0", "x64");
// Determine how to access python and pip
const { pip, python } = (function() {
if (isDocker()) {
core.info("Running inside a Docker container");
// Python 3 assumed to be installed and on the PATH
return {
pip: "pip3",
python: "python3"
};
} else {
// Setup Python from the tool cache
setupPython("3.x", "x64");
return {
pip: "pip",
python: "python"
};
}
})();
// Install requirements
await exec.exec("pip", [
await exec.exec(pip, [
"install",
"--requirement",
`${src}/requirements.txt`
`${src}/requirements.txt`,
"--no-index",
`--find-links=${__dirname}/vendor`
]);
// Fetch action inputs
@ -1039,7 +1058,7 @@ async function run() {
projectColumn: core.getInput("project-column"),
branch: core.getInput("branch"),
base: core.getInput("base"),
branchSuffix: core.getInput("branch-suffix"),
branchSuffix: core.getInput("branch-suffix")
};
core.debug(`Inputs: ${inspect(inputs)}`);
@ -1063,7 +1082,7 @@ async function run() {
if (inputs.branchSuffix) process.env.CPR_BRANCH_SUFFIX = inputs.branchSuffix;
// Execute python script
await exec.exec("python", [`${src}/create_pull_request.py`]);
await exec.exec(python, [`${src}/create_pull_request.py`]);
} catch (error) {
core.setFailed(error.message);
}
@ -1393,6 +1412,43 @@ if (process.env.NODE_DEBUG && /\btunnel\b/.test(process.env.NODE_DEBUG)) {
exports.debug = debug; // for test
/***/ }),
/***/ 160:
/***/ (function(module, __unusedexports, __webpack_require__) {
"use strict";
const fs = __webpack_require__(747);
let isDocker;
function hasDockerEnv() {
try {
fs.statSync('/.dockerenv');
return true;
} catch (_) {
return false;
}
}
function hasDockerCGroup() {
try {
return fs.readFileSync('/proc/self/cgroup', 'utf8').includes('docker');
} catch (_) {
return false;
}
}
module.exports = () => {
if (isDocker === undefined) {
isDocker = hasDockerEnv() || hasDockerCGroup();
}
return isDocker;
};
/***/ }),
/***/ 211:

17
dist/src/common.py vendored
View File

@ -8,6 +8,23 @@ def get_random_string(length=7, chars=string.ascii_lowercase + string.digits):
return "".join(random.choice(chars) for _ in range(length))
def parse_github_repository(url):
# Parse the protocol and github repository from a URL
# e.g. HTTPS, peter-evans/create-pull-request
https_pattern = re.compile(r"^https://github.com/(.+/.+)$")
ssh_pattern = re.compile(r"^git@github.com:(.+/.+).git$")
match = https_pattern.match(url)
if match is not None:
return "HTTPS", match.group(1)
match = ssh_pattern.match(url)
if match is not None:
return "SSH", match.group(1)
raise ValueError(f"The format of '{url}' is not a valid GitHub repository URL")
def parse_display_name_email(display_name_email):
# Parse the name and email address from a string in the following format
# Display Name <email@address.com>

View File

@ -72,6 +72,8 @@ def create_or_update_pull_request(
pull_request = github_repo.get_pulls(
state="open", base=base, head=head_branch
)[0]
# Update title and body
pull_request.as_issue().edit(title=title, body=body)
print(f"Updated pull request #{pull_request.number} ({branch} => {base})")
else:
print(str(e))

View File

@ -31,6 +31,14 @@ def get_git_config_value(repo, name):
return None
def get_repository_detail():
remote_origin_url = get_git_config_value(repo, "remote.origin.url")
if remote_origin_url is None:
raise ValueError("Failed to fetch 'remote.origin.url' from git config")
protocol, github_repository = cmn.parse_github_repository(remote_origin_url)
return remote_origin_url, protocol, github_repository
def git_user_config_is_set(repo):
name = get_git_config_value(repo, "user.name")
email = get_git_config_value(repo, "user.email")
@ -94,7 +102,6 @@ def set_committer_author(repo, committer, author):
# Get required environment variables
github_token = os.environ["GITHUB_TOKEN"]
github_repository = os.environ["GITHUB_REPOSITORY"]
# Get environment variables with defaults
path = os.getenv("CPR_PATH", os.getcwd())
branch = os.getenv("CPR_BRANCH", DEFAULT_BRANCH)
@ -107,6 +114,11 @@ base = os.environ.get("CPR_BASE")
# Set the repo path
repo = Repo(path)
# Determine the GitHub repository from git config
# This will be the target repository for the pull request
repo_url, protocol, github_repository = get_repository_detail()
print(f"Target repository set to {github_repository}")
# Determine if the checked out ref is a valid base for a pull request
# The action needs the checked out HEAD ref to be a branch
# This check will fail in the following cases:
@ -162,8 +174,10 @@ except ValueError as e:
print(f"::error::{e} " + "Unable to continue. Exiting.")
sys.exit(1)
# Set the repository URL
repo_url = f"https://x-access-token:{github_token}@github.com/{github_repository}"
# Set the auth token in the repo URL
# This supports checkout@v1. From v2 the auth token is saved for further use.
if protocol == "HTTPS":
repo_url = f"https://x-access-token:{github_token}@github.com/{github_repository}"
# Create or update the pull request branch
result = coub.create_or_update_branch(repo, repo_url, commit_message, base, branch)

View File

@ -1,2 +1,2 @@
GitPython==3.0.5
PyGithub==1.45
GitPython==3.0.7
PyGithub==1.46

View File

@ -9,6 +9,30 @@ def test_get_random_string():
assert len(cmn.get_random_string(length=20)) == 20
def test_parse_github_repository_success():
protocol, repository = cmn.parse_github_repository(
"https://github.com/peter-evans/create-pull-request"
)
assert protocol == "HTTPS"
assert repository == "peter-evans/create-pull-request"
protocol, repository = cmn.parse_github_repository(
"git@github.com:peter-evans/create-pull-request.git"
)
assert protocol == "SSH"
assert repository == "peter-evans/create-pull-request"
def test_parse_github_repository_failure():
url = "https://github.com/peter-evans"
with pytest.raises(ValueError) as e_info:
cmn.parse_github_repository(url)
assert (
e_info.value.args[0]
== f"The format of '{url}' is not a valid GitHub repository URL"
)
def test_parse_display_name_email_success():
name, email = cmn.parse_display_name_email("abc def <abc@def.com>")
assert name == "abc def"

BIN
dist/vendor/Deprecated-1.2.7.tar.gz vendored Normal file

Binary file not shown.

BIN
dist/vendor/GitPython-3.0.7.tar.gz vendored Normal file

Binary file not shown.

BIN
dist/vendor/PyGithub-1.46.tar.gz vendored Normal file

Binary file not shown.

BIN
dist/vendor/PyJWT-1.7.1.tar.gz vendored Normal file

Binary file not shown.

BIN
dist/vendor/certifi-2019.11.28.tar.gz vendored Normal file

Binary file not shown.

BIN
dist/vendor/chardet-3.0.4.tar.gz vendored Normal file

Binary file not shown.

BIN
dist/vendor/gitdb2-2.0.6.tar.gz vendored Normal file

Binary file not shown.

BIN
dist/vendor/idna-2.8.tar.gz vendored Normal file

Binary file not shown.

BIN
dist/vendor/requests-2.22.0.tar.gz vendored Normal file

Binary file not shown.

BIN
dist/vendor/smmap2-2.0.5.tar.gz vendored Normal file

Binary file not shown.

BIN
dist/vendor/urllib3-1.25.8.tar.gz vendored Normal file

Binary file not shown.

BIN
dist/vendor/wrapt-1.11.2.tar.gz vendored Normal file

Binary file not shown.

View File

Before

Width:  |  Height:  |  Size: 207 KiB

After

Width:  |  Height:  |  Size: 207 KiB

View File

Before

Width:  |  Height:  |  Size: 416 B

After

Width:  |  Height:  |  Size: 416 B

View File

Before

Width:  |  Height:  |  Size: 414 KiB

After

Width:  |  Height:  |  Size: 414 KiB

View File

@ -1,6 +1,6 @@
# Concepts and guidelines
# Concepts, guidelines and advanced usage
This document covers terminology, how the action works, and general usage guidelines.
This document covers terminology, how the action works, general usage guidelines, and advanced usage.
- [Terminology](#terminology)
- [Events and checkout](#events-and-checkout)
@ -9,6 +9,12 @@ This document covers terminology, how the action works, and general usage guidel
- [Providing a consistent base](#providing-a-consistent-base)
- [Pull request events](#pull-request-events)
- [Restrictions on forked repositories](#restrictions-on-forked-repositories)
- [Security](#security)
- [Advanced usage](#advanced-usage)
- [Creating pull requests in a remote repository](#creating-pull-requests-in-a-remote-repository)
- [Push using SSH (deploy keys)](#push-using-ssh-deploy-keys)
- [Using in an alpine linux container](#using-in-an-alpine-linux-container)
- [Creating pull requests on tag push](#creating-pull-requests-on-tag-push)
## Terminology
@ -45,7 +51,7 @@ Workflow steps:
The following git diagram shows how the action creates and updates a pull request branch.
![Create Pull Request GitGraph](../assets/cpr-gitgraph.png)
![Create Pull Request GitGraph](assets/cpr-gitgraph.png)
## Guidelines
@ -105,3 +111,163 @@ jobs:
# Check if the event is not triggered by a fork
if: github.event.pull_request.head.repo.full_name == github.repository
```
### Security
From a security perspective it's good practice to fork third-party actions, review the code, and use your fork of the action in workflows.
By using third-party actions directly the risk exists that it could be modified to do something malicious, such as capturing secrets.
This action uses [ncc](https://github.com/zeit/ncc) to compile the Node.js code and dependencies into a single file.
Python dependencies are vendored and committed to the repository [here](https://github.com/peter-evans/create-pull-request/tree/master/dist/vendor).
No dependencies are downloaded during the action execution.
Vendored Python dependencies can be reviewed by rebuilding the [dist](https://github.com/peter-evans/create-pull-request/tree/master/dist) directory and redownloading dependencies.
The following commands require Node and Python 3.
```
npm install
npm run clean
npm run package
```
The `dist` directory should be rebuilt leaving no git diff.
## Advanced usage
### Creating pull requests in a remote repository
Checking out a branch from a different repository from where the workflow is executing will make *that repository* the target for the created pull request. In this case, a `repo` scoped [Personal Access Token (PAT)](https://help.github.com/en/github/authenticating-to-github/creating-a-personal-access-token-for-the-command-line) is required.
```yml
- uses: actions/checkout@v2
with:
token: ${{ secrets.PAT }}
repository: owner/repo
# Make changes to pull request here
- uses: peter-evans/create-pull-request@v2
with:
token: ${{ secrets.PAT }}
```
### Push using SSH (deploy keys)
[Deploy keys](https://developer.github.com/v3/guides/managing-deploy-keys/#deploy-keys) can be set per repository and so are arguably more secure than using a `repo` scoped [Personal Access Token (PAT)](https://help.github.com/en/github/authenticating-to-github/creating-a-personal-access-token-for-the-command-line).
Allowing the action to push with a configured deploy key will trigger `on: push` workflows. This makes it an alternative to using a PAT to trigger checks for pull requests.
How to use SSH (deploy keys) with create-pull-request action:
1. [Create an new SSH key pair](https://help.github.com/en/github/authenticating-to-github/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent#generating-a-new-ssh-key) for your repository. Do not set a passphrase.
2. Copy the contents of the public key (.pub file) to a new repository [deploy key](https://developer.github.com/v3/guides/managing-deploy-keys/#deploy-keys) and check the box to "Allow write access."
3. Add a secret to the repository containing the entire contents of the private key.
4. As shown in the example steps below, use the [`webfactory/ssh-agent`](https://github.com/webfactory/ssh-agent) action to install the private key and clone your repository. Remember to checkout the `base` of your pull request if it's not the default branch, e.g. `git checkout my-branch`.
```yml
steps:
- uses: webfactory/ssh-agent@v0.2.0
with:
ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}
- name: Checkout via SSH
run: git clone git@github.com:peter-evans/create-pull-request.git .
# Make changes to pull request here
- name: Create Pull Request
uses: peter-evans/create-pull-request@v2
with:
token: ${{ secrets.GITHUB_TOKEN }}
```
### Using in an alpine linux container
This action can be run inside an Alpine Linux container by pre-installing the correct binaries for the action's dependencies.
The following example workflow installs git and Python dependencies at the start of the job. You can also bake these dependencies into your own Alpine Docker image if you prefer. Note that git must be installed *before* running `actions/checkout`, otherwise it will just download the source of the repository instead of cloning it.
```yml
jobs:
createPullRequestAlpine:
runs-on: ubuntu-latest
container:
image: alpine
steps:
- name: Install dependencies
run: |
apk --no-cache add git python3
ln -sf python3 /usr/bin/python
ln -sf pip3 /usr/bin/pip
- uses: actions/checkout@v2
# Make changes to pull request here
- name: Create Pull Request
uses: peter-evans/create-pull-request@v2
with:
token: ${{ secrets.GITHUB_TOKEN }}
```
### Creating pull requests on tag push
An `on: push` workflow will also trigger when tags are pushed.
During these events, the `actions/checkout` action will check out the `ref/tags/<tag>` git ref by default.
This means the repository will *not* be checked out on an active branch.
If you would like to run `create-pull-request` action on the tagged commit you can achieve this by creating a temporary branch as follows.
```yml
on:
push:
tags:
- 'v*.*.*'
jobs:
example:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Create a temporary tag branch
run: |
git config --global user.name 'GitHub'
git config --global user.email 'noreply@github.com'
git checkout -b temp-${GITHUB_REF:10}
git push --set-upstream origin temp-${GITHUB_REF:10}
# Make changes to pull request here
- name: Create Pull Request
uses: peter-evans/create-pull-request@v2
with:
token: ${{ secrets.GITHUB_TOKEN }}
base: master
- name: Delete tag branch
run: |
git push --delete origin temp-${GITHUB_REF:10}
```
This is an alternative, simpler workflow to the one above. However, this is not guaranteed to checkout the tagged commit.
There is a chance that in between the tag being pushed and checking out the `master` branch in the workflow, another commit is made to `master`. If that possibility is not a concern, this workflow will work fine.
```yml
on:
push:
tags:
- 'v*.*.*'
jobs:
example:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
ref: master
# Make changes to pull request here
- name: Create Pull Request
uses: peter-evans/create-pull-request@v2
with:
token: ${{ secrets.GITHUB_TOKEN }}
```

View File

@ -2,6 +2,7 @@
- [Use case: Create a pull request to update X on push](#use-case-create-a-pull-request-to-update-x-on-push)
- [Update project authors](#update-project-authors)
- [Keep a branch up-to-date with another](#keep-a-branch-up-to-date-with-another)
- [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 SwaggerUI for GitHub Pages](#update-swaggerui-for-github-pages)
@ -51,6 +52,36 @@ jobs:
branch: update-authors
```
### Keep a branch up-to-date with another
This is a use case where a branch should be kept up to date with another by opening a pull request to update it. The pull request should then be updated with new changes until it is merged or closed.
In this example scenario, a branch called `production` should be updated via pull request to keep it in sync with `master`. Merging the pull request is effectively promoting those changes to production.
```yml
name: Create production promotion pull request
on:
push:
branches:
- master
jobs:
productionPromotion:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
ref: production
- name: Reset promotion branch
run: |
git fetch origin master:master
git reset --hard master
- name: Create Pull Request
uses: peter-evans/create-pull-request@v2
with:
token: ${{ secrets.GITHUB_TOKEN }}
branch: production-promotion
```
## Use case: Create a pull request to update X periodically
This pattern will work well for updating any kind of static content from an external source. The workflow executes on a schedule and raises a pull request when there are changes.
@ -258,7 +289,7 @@ jobs:
ref: ${{ github.head_ref }}
- name: autopep8
id: autopep8
uses: peter-evans/autopep8@v1.1.0
uses: peter-evans/autopep8@v1
with:
args: --exit-code --recursive --in-place --aggressive --aggressive .
- name: Set autopep8 branch name

View File

@ -1,4 +1,5 @@
const { inspect } = require("util");
const isDocker = require("is-docker");
const core = require("@actions/core");
const exec = require("@actions/exec");
const setupPython = require("./src/setup-python");
@ -9,14 +10,32 @@ async function run() {
const src = __dirname + "/src";
core.debug(`src: ${src}`);
// Setup Python from the tool cache
setupPython("3.8.0", "x64");
// Determine how to access python and pip
const { pip, python } = (function() {
if (isDocker()) {
core.info("Running inside a Docker container");
// Python 3 assumed to be installed and on the PATH
return {
pip: "pip3",
python: "python3"
};
} else {
// Setup Python from the tool cache
setupPython("3.x", "x64");
return {
pip: "pip",
python: "python"
};
}
})();
// Install requirements
await exec.exec("pip", [
await exec.exec(pip, [
"install",
"--requirement",
`${src}/requirements.txt`
`${src}/requirements.txt`,
"--no-index",
`--find-links=${__dirname}/vendor`
]);
// Fetch action inputs
@ -37,7 +56,7 @@ async function run() {
projectColumn: core.getInput("project-column"),
branch: core.getInput("branch"),
base: core.getInput("base"),
branchSuffix: core.getInput("branch-suffix"),
branchSuffix: core.getInput("branch-suffix")
};
core.debug(`Inputs: ${inspect(inputs)}`);
@ -61,7 +80,7 @@ async function run() {
if (inputs.branchSuffix) process.env.CPR_BRANCH_SUFFIX = inputs.branchSuffix;
// Execute python script
await exec.exec("python", [`${src}/create_pull_request.py`]);
await exec.exec(python, [`${src}/create_pull_request.py`]);
} catch (error) {
core.setFailed(error.message);
}

11
package-lock.json generated
View File

@ -36,11 +36,16 @@
}
},
"@zeit/ncc": {
"version": "0.21.0",
"resolved": "https://registry.npmjs.org/@zeit/ncc/-/ncc-0.21.0.tgz",
"integrity": "sha512-RUMdvVK/w78oo+yBjruZltt0kJXYar2un/1bYQ2LuHG7GmFVm+QjxzEmySwREctaJdEnBvlMdUNWd9hXHxEI3g==",
"version": "0.21.1",
"resolved": "https://registry.npmjs.org/@zeit/ncc/-/ncc-0.21.1.tgz",
"integrity": "sha512-M9WzgquSOt2nsjRkYM9LRylBLmmlwNCwYbm3Up3PDEshfvdmIfqpFNSK8EJvR18NwZjGHE5z2avlDtYQx2JQnw==",
"dev": true
},
"is-docker": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.0.0.tgz",
"integrity": "sha512-pJEdRugimx4fBMra5z2/5iRdZ63OhYV0vr0Dwm5+xtW4D1FvRkB8hamMIhnWfyJeDdyr/aa7BDyNbtG38VxgoQ=="
},
"qs": {
"version": "6.9.1",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.9.1.tgz",

View File

@ -4,7 +4,10 @@
"description": "Creates a pull request for changes to your repository in the actions workspace",
"main": "index.js",
"scripts": {
"package": "ncc build index.js -o dist"
"clean": "rm -rf dist",
"build": "ncc build index.js -o dist",
"vendor-deps": "pip download -r src/requirements.txt --no-binary=:all: -d dist/vendor",
"package": "npm run build && npm run vendor-deps"
},
"repository": {
"type": "git",
@ -20,9 +23,10 @@
"dependencies": {
"@actions/core": "^1.1.1",
"@actions/exec": "^1.0.1",
"@actions/tool-cache": "^1.1.2"
"@actions/tool-cache": "^1.1.2",
"is-docker": "^2.0.0"
},
"devDependencies": {
"@zeit/ncc": "0.21.0"
"@zeit/ncc": "0.21.1"
}
}

View File

@ -8,6 +8,23 @@ def get_random_string(length=7, chars=string.ascii_lowercase + string.digits):
return "".join(random.choice(chars) for _ in range(length))
def parse_github_repository(url):
# Parse the protocol and github repository from a URL
# e.g. HTTPS, peter-evans/create-pull-request
https_pattern = re.compile(r"^https://github.com/(.+/.+)$")
ssh_pattern = re.compile(r"^git@github.com:(.+/.+).git$")
match = https_pattern.match(url)
if match is not None:
return "HTTPS", match.group(1)
match = ssh_pattern.match(url)
if match is not None:
return "SSH", match.group(1)
raise ValueError(f"The format of '{url}' is not a valid GitHub repository URL")
def parse_display_name_email(display_name_email):
# Parse the name and email address from a string in the following format
# Display Name <email@address.com>

View File

@ -72,6 +72,8 @@ def create_or_update_pull_request(
pull_request = github_repo.get_pulls(
state="open", base=base, head=head_branch
)[0]
# Update title and body
pull_request.as_issue().edit(title=title, body=body)
print(f"Updated pull request #{pull_request.number} ({branch} => {base})")
else:
print(str(e))

View File

@ -31,6 +31,14 @@ def get_git_config_value(repo, name):
return None
def get_repository_detail():
remote_origin_url = get_git_config_value(repo, "remote.origin.url")
if remote_origin_url is None:
raise ValueError("Failed to fetch 'remote.origin.url' from git config")
protocol, github_repository = cmn.parse_github_repository(remote_origin_url)
return remote_origin_url, protocol, github_repository
def git_user_config_is_set(repo):
name = get_git_config_value(repo, "user.name")
email = get_git_config_value(repo, "user.email")
@ -94,7 +102,6 @@ def set_committer_author(repo, committer, author):
# Get required environment variables
github_token = os.environ["GITHUB_TOKEN"]
github_repository = os.environ["GITHUB_REPOSITORY"]
# Get environment variables with defaults
path = os.getenv("CPR_PATH", os.getcwd())
branch = os.getenv("CPR_BRANCH", DEFAULT_BRANCH)
@ -107,6 +114,11 @@ base = os.environ.get("CPR_BASE")
# Set the repo path
repo = Repo(path)
# Determine the GitHub repository from git config
# This will be the target repository for the pull request
repo_url, protocol, github_repository = get_repository_detail()
print(f"Target repository set to {github_repository}")
# Determine if the checked out ref is a valid base for a pull request
# The action needs the checked out HEAD ref to be a branch
# This check will fail in the following cases:
@ -162,8 +174,10 @@ except ValueError as e:
print(f"::error::{e} " + "Unable to continue. Exiting.")
sys.exit(1)
# Set the repository URL
repo_url = f"https://x-access-token:{github_token}@github.com/{github_repository}"
# Set the auth token in the repo URL
# This supports checkout@v1. From v2 the auth token is saved for further use.
if protocol == "HTTPS":
repo_url = f"https://x-access-token:{github_token}@github.com/{github_repository}"
# Create or update the pull request branch
result = coub.create_or_update_branch(repo, repo_url, commit_message, base, branch)

View File

@ -1,2 +1,2 @@
GitPython==3.0.5
PyGithub==1.45
GitPython==3.0.7
PyGithub==1.46

View File

@ -9,6 +9,30 @@ def test_get_random_string():
assert len(cmn.get_random_string(length=20)) == 20
def test_parse_github_repository_success():
protocol, repository = cmn.parse_github_repository(
"https://github.com/peter-evans/create-pull-request"
)
assert protocol == "HTTPS"
assert repository == "peter-evans/create-pull-request"
protocol, repository = cmn.parse_github_repository(
"git@github.com:peter-evans/create-pull-request.git"
)
assert protocol == "SSH"
assert repository == "peter-evans/create-pull-request"
def test_parse_github_repository_failure():
url = "https://github.com/peter-evans"
with pytest.raises(ValueError) as e_info:
cmn.parse_github_repository(url)
assert (
e_info.value.args[0]
== f"The format of '{url}' is not a valid GitHub repository URL"
)
def test_parse_display_name_email_success():
name, email = cmn.parse_display_name_email("abc def <abc@def.com>")
assert name == "abc def"