Compare commits
31 Commits
Author | SHA1 | Date | |
---|---|---|---|
c7b64af0a4 | |||
289fda9fea | |||
b021b9e27a | |||
c26314237b | |||
ed7dd8d236 | |||
ca0e9d75fd | |||
87c27ee3eb | |||
4beea725d3 | |||
9d58699da5 | |||
bceebba814 | |||
0b82710b2e | |||
4aaaa0e760 | |||
918bfcb8a3 | |||
1ecfd1ae40 | |||
4dd195d7c3 | |||
72b5f45bb4 | |||
5394814b39 | |||
83918398f5 | |||
b6a458d96a | |||
7b5ff6b642 | |||
82eddd8828 | |||
6df2a462d1 | |||
3689bd07d7 | |||
943f19ac64 | |||
e59d6c7fff | |||
c65a4f39b3 | |||
2d18371789 | |||
cc7020a609 | |||
d8700620d6 | |||
b7064071dc | |||
339e82d37b |
@ -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
|
||||
[](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:
|
||||
|
||||

|
||||

|
||||
|
||||
## License
|
||||
|
||||
|
64
dist/index.js
vendored
64
dist/index.js
vendored
@ -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,11 +1012,27 @@ async function run() {
|
||||
const src = __webpack_require__.ab + "src";
|
||||
core.debug(`src: ${src}`);
|
||||
|
||||
// Setup Python from the tool cache
|
||||
setupPython("3.8.x", "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`,
|
||||
@ -1041,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)}`);
|
||||
|
||||
@ -1065,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);
|
||||
}
|
||||
@ -1395,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
17
dist/src/common.py
vendored
@ -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>
|
||||
|
30
dist/src/create_pull_request.py
vendored
30
dist/src/create_pull_request.py
vendored
@ -1,5 +1,6 @@
|
||||
#!/usr/bin/env python3
|
||||
""" Create Pull Request """
|
||||
import base64
|
||||
import common as cmn
|
||||
import create_or_update_branch as coub
|
||||
import create_or_update_pull_request as coupr
|
||||
@ -31,6 +32,14 @@ def get_git_config_value(repo, name):
|
||||
return None
|
||||
|
||||
|
||||
def get_repository_detail(repo):
|
||||
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 +103,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 +115,23 @@ 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(repo)
|
||||
print(f"Target repository set to {github_repository}")
|
||||
|
||||
if protocol == "HTTPS":
|
||||
print(f"::debug::Using HTTPS protocol")
|
||||
# Encode and configure the basic credential for HTTPS access
|
||||
basic_credential = base64.b64encode(
|
||||
f"x-access-token:{github_token}".encode("utf-8")
|
||||
).decode("utf-8")
|
||||
# Mask the basic credential in logs and debug output
|
||||
print(f"::add-mask::{basic_credential}")
|
||||
repo.git.set_persistent_git_options(
|
||||
c=f"http.https://github.com/.extraheader=AUTHORIZATION: basic {basic_credential}"
|
||||
)
|
||||
|
||||
# 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,9 +187,6 @@ 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}"
|
||||
|
||||
# Create or update the pull request branch
|
||||
result = coub.create_or_update_branch(repo, repo_url, commit_message, base, branch)
|
||||
|
||||
|
4
dist/src/requirements.txt
vendored
4
dist/src/requirements.txt
vendored
@ -1,2 +1,2 @@
|
||||
GitPython==3.0.5
|
||||
PyGithub==1.45
|
||||
GitPython==3.0.8
|
||||
PyGithub==1.46
|
||||
|
24
dist/src/test_common.py
vendored
24
dist/src/test_common.py
vendored
@ -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/GitPython-3.0.5.tar.gz
vendored
BIN
dist/vendor/GitPython-3.0.5.tar.gz
vendored
Binary file not shown.
BIN
dist/vendor/GitPython-3.0.8.tar.gz
vendored
Normal file
BIN
dist/vendor/GitPython-3.0.8.tar.gz
vendored
Normal file
Binary file not shown.
BIN
dist/vendor/PyGithub-1.45.tar.gz
vendored
BIN
dist/vendor/PyGithub-1.45.tar.gz
vendored
Binary file not shown.
BIN
dist/vendor/PyGithub-1.46.tar.gz
vendored
Normal file
BIN
dist/vendor/PyGithub-1.46.tar.gz
vendored
Normal file
Binary file not shown.
Binary file not shown.
BIN
dist/vendor/six-1.14.0.tar.gz
vendored
BIN
dist/vendor/six-1.14.0.tar.gz
vendored
Binary file not shown.
BIN
dist/vendor/wrapt-1.11.2.tar.gz
vendored
BIN
dist/vendor/wrapt-1.11.2.tar.gz
vendored
Binary file not shown.
BIN
dist/vendor/wrapt-1.12.0.tar.gz
vendored
Normal file
BIN
dist/vendor/wrapt-1.12.0.tar.gz
vendored
Normal file
Binary file not shown.
Before Width: | Height: | Size: 207 KiB After Width: | Height: | Size: 207 KiB |
Before Width: | Height: | Size: 416 B After Width: | Height: | Size: 416 B |
Before Width: | Height: | Size: 414 KiB After Width: | Height: | Size: 414 KiB |
@ -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,7 +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)
|
||||
- [Tag push events](#tag-push-events)
|
||||
- [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)
|
||||
- [Running in a container](#running-in-a-container)
|
||||
- [Creating pull requests on tag push](#creating-pull-requests-on-tag-push)
|
||||
|
||||
## Terminology
|
||||
|
||||
@ -46,7 +51,7 @@ Workflow steps:
|
||||
|
||||
The following git diagram shows how the action creates and updates a pull request branch.
|
||||
|
||||

|
||||

|
||||
|
||||
## Guidelines
|
||||
|
||||
@ -107,7 +112,129 @@ jobs:
|
||||
if: github.event.pull_request.head.repo.full_name == github.repository
|
||||
```
|
||||
|
||||
### Tag push events
|
||||
### 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 a 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 }}
|
||||
```
|
||||
|
||||
### Running in a container
|
||||
|
||||
This action can be run inside a container by installing the action's dependencies either in the Docker image itself, or during the workflow.
|
||||
|
||||
The action requires `python3`, `pip3` and `git` to be installed and on the `PATH`.
|
||||
|
||||
Note that `actions/checkout` requires Git 2.18 or higher to be installed, otherwise it will just download the source of the repository instead of cloning it.
|
||||
|
||||
**Alpine container example:**
|
||||
```yml
|
||||
jobs:
|
||||
createPullRequestAlpine:
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: alpine
|
||||
steps:
|
||||
- name: Install dependencies
|
||||
run: apk --no-cache add git python3
|
||||
|
||||
- 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 }}
|
||||
```
|
||||
|
||||
**Ubuntu container example:**
|
||||
```yml
|
||||
jobs:
|
||||
createPullRequestAlpine:
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: ubuntu
|
||||
steps:
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
apt-get update
|
||||
apt-get install -y software-properties-common
|
||||
add-apt-repository -y ppa:git-core/ppa
|
||||
apt-get install -y python3 python3-pip git
|
||||
|
||||
- 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.
|
||||
@ -133,8 +260,7 @@ jobs:
|
||||
git checkout -b temp-${GITHUB_REF:10}
|
||||
git push --set-upstream origin temp-${GITHUB_REF:10}
|
||||
|
||||
- name: Create changes to pull request
|
||||
run: <create changes here>
|
||||
# Make changes to pull request here
|
||||
|
||||
- name: Create Pull Request
|
||||
uses: peter-evans/create-pull-request@v2
|
||||
@ -163,8 +289,7 @@ jobs:
|
||||
with:
|
||||
ref: master
|
||||
|
||||
- name: Create changes to pull request
|
||||
run: <create changes here>
|
||||
# Make changes to pull request here
|
||||
|
||||
- name: Create Pull Request
|
||||
uses: peter-evans/create-pull-request@v2
|
||||
|
@ -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.
|
||||
|
27
index.js
27
index.js
@ -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,11 +10,27 @@ async function run() {
|
||||
const src = __dirname + "/src";
|
||||
core.debug(`src: ${src}`);
|
||||
|
||||
// Setup Python from the tool cache
|
||||
setupPython("3.8.x", "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`,
|
||||
@ -39,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)}`);
|
||||
|
||||
@ -63,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
11
package-lock.json
generated
@ -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",
|
||||
|
@ -23,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"
|
||||
}
|
||||
}
|
||||
|
@ -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>
|
||||
|
@ -1,5 +1,6 @@
|
||||
#!/usr/bin/env python3
|
||||
""" Create Pull Request """
|
||||
import base64
|
||||
import common as cmn
|
||||
import create_or_update_branch as coub
|
||||
import create_or_update_pull_request as coupr
|
||||
@ -31,6 +32,14 @@ def get_git_config_value(repo, name):
|
||||
return None
|
||||
|
||||
|
||||
def get_repository_detail(repo):
|
||||
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 +103,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 +115,23 @@ 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(repo)
|
||||
print(f"Target repository set to {github_repository}")
|
||||
|
||||
if protocol == "HTTPS":
|
||||
print(f"::debug::Using HTTPS protocol")
|
||||
# Encode and configure the basic credential for HTTPS access
|
||||
basic_credential = base64.b64encode(
|
||||
f"x-access-token:{github_token}".encode("utf-8")
|
||||
).decode("utf-8")
|
||||
# Mask the basic credential in logs and debug output
|
||||
print(f"::add-mask::{basic_credential}")
|
||||
repo.git.set_persistent_git_options(
|
||||
c=f"http.https://github.com/.extraheader=AUTHORIZATION: basic {basic_credential}"
|
||||
)
|
||||
|
||||
# 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,9 +187,6 @@ 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}"
|
||||
|
||||
# Create or update the pull request branch
|
||||
result = coub.create_or_update_branch(repo, repo_url, commit_message, base, branch)
|
||||
|
||||
|
@ -1,2 +1,2 @@
|
||||
GitPython==3.0.5
|
||||
PyGithub==1.45
|
||||
GitPython==3.0.8
|
||||
PyGithub==1.46
|
||||
|
@ -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"
|
||||
|
Reference in New Issue
Block a user