Compare commits
28 Commits
Author | SHA1 | Date | |
---|---|---|---|
1e6b4d1790 | |||
a70c6ebe2a | |||
5bd05538d0 | |||
26bc40eea1 | |||
6bb0e7771c | |||
4c347a4514 | |||
cff2c3381d | |||
e48dab0c1c | |||
ac9f92d6e7 | |||
3ff256ce08 | |||
0bcb10560b | |||
3c4b2793c1 | |||
22870d7816 | |||
e324a22ee1 | |||
8b60386018 | |||
6852d55922 | |||
32e5bb80a5 | |||
619cf2115d | |||
69008aa567 | |||
ae4278bf24 | |||
88da40fea7 | |||
694e068136 | |||
64c34f6885 | |||
52ada17960 | |||
ce00b952cf | |||
0d42c285a3 | |||
ea1eaf1734 | |||
d5c5ea3e20 |
17
.eslintrc.json
Normal file
17
.eslintrc.json
Normal file
@ -0,0 +1,17 @@
|
||||
{
|
||||
"env": {
|
||||
"commonjs": true,
|
||||
"es6": true,
|
||||
"node": true
|
||||
},
|
||||
"extends": "eslint:recommended",
|
||||
"globals": {
|
||||
"Atomics": "readonly",
|
||||
"SharedArrayBuffer": "readonly"
|
||||
},
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 2018
|
||||
},
|
||||
"rules": {
|
||||
}
|
||||
}
|
1
.github/workflows/cpr-example-command.yml
vendored
1
.github/workflows/cpr-example-command.yml
vendored
@ -30,6 +30,7 @@ jobs:
|
||||
project: Example Project
|
||||
project-column: To do
|
||||
branch: example-patches
|
||||
request-to-parent: false
|
||||
- name: Check outputs
|
||||
run: |
|
||||
echo "Pull Request Number - ${{ env.PULL_REQUEST_NUMBER }}"
|
||||
|
@ -56,6 +56,7 @@ With the exception of `token`, all inputs are **optional**. If not set, sensible
|
||||
| `project` | The name of the project for which a card should be created. Requires `project-column`. | |
|
||||
| `project-column` | The name of the project column under which a card should be created. Requires `project`. | |
|
||||
| `branch` | The branch name. See [Branch naming](#branch-naming) for details. | `create-pull-request/patch` |
|
||||
| `request-to-parent` | Create the pull request in the parent repository of the checked out fork. | `false` |
|
||||
| `base` | Sets the pull request base branch. | Defaults to the branch checked out in the workflow. |
|
||||
| `branch-suffix` | The branch suffix type. Valid values are `random`, `timestamp` and `short-commit-hash`. See [Branch naming](#branch-naming) for details. | |
|
||||
|
||||
@ -181,10 +182,12 @@ jobs:
|
||||
labels: report, automated pr
|
||||
assignees: peter-evans
|
||||
reviewers: peter-evans
|
||||
team-reviewers: owners, maintainers
|
||||
milestone: 1
|
||||
project: Example Project
|
||||
project-column: To do
|
||||
branch: example-patches
|
||||
request-to-parent: false
|
||||
- name: Check outputs
|
||||
run: |
|
||||
echo "Pull Request Number - ${{ env.PULL_REQUEST_NUMBER }}"
|
||||
|
@ -32,6 +32,9 @@ inputs:
|
||||
description: 'The name of the project column under which a card should be created.'
|
||||
branch:
|
||||
description: 'The pull request branch name.'
|
||||
request-to-parent:
|
||||
description: 'Create the pull request in the parent repository of the checked out fork.'
|
||||
default: false
|
||||
base:
|
||||
description: 'The pull request base branch.'
|
||||
branch-suffix:
|
||||
@ -43,5 +46,5 @@ runs:
|
||||
using: 'node12'
|
||||
main: 'dist/index.js'
|
||||
branding:
|
||||
icon: 'git-pull-request'
|
||||
icon: 'git-pull-request'
|
||||
color: 'gray-dark'
|
||||
|
0
dist/src/common.py → dist/cpr/common.py
vendored
0
dist/src/common.py → dist/cpr/common.py
vendored
@ -56,25 +56,37 @@ def create_or_update_pull_request(
|
||||
team_reviewers,
|
||||
project_name,
|
||||
project_column_name,
|
||||
request_to_parent,
|
||||
):
|
||||
if request_to_parent is None:
|
||||
request_to_parent = False
|
||||
else:
|
||||
request_to_parent = request_to_parent.lower() in ['true', '1', 't', 'y', 'yes', 'on']
|
||||
|
||||
github_repo = head_repo = Github(github_token).get_repo(github_repository)
|
||||
if request_to_parent:
|
||||
github_repo = github_repo.parent
|
||||
if github_repo is None:
|
||||
raise ValueError("The checked out repository is not a fork. Input 'request-to-parent' should be set to false.")
|
||||
|
||||
head_branch = f"{head_repo.owner.login}:{branch}"
|
||||
|
||||
# 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
|
||||
title=title, body=body, base=base, head=head_branch
|
||||
)
|
||||
print(f"Created pull request #{pull_request.number} ({branch} => {base})")
|
||||
print(f"Created pull request #{pull_request.number} ({head_branch} => {github_repo.owner.login}:{base})")
|
||||
except GithubException as e:
|
||||
if e.status == 422:
|
||||
# A pull request exists for this branch and base
|
||||
head_branch = "{}:{}".format(github_repository.split("/")[0], branch)
|
||||
# Get the 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})")
|
||||
print(f"Updated pull request #{pull_request.number} ({head_branch} => {github_repo.owner.login}:{base})")
|
||||
else:
|
||||
print(str(e))
|
||||
raise
|
@ -224,4 +224,5 @@ if result["action"] in ["created", "updated"]:
|
||||
os.environ.get("CPR_TEAM_REVIEWERS"),
|
||||
os.environ.get("CPR_PROJECT_NAME"),
|
||||
os.environ.get("CPR_PROJECT_COLUMN_NAME"),
|
||||
os.environ.get("CPR_REQUEST_TO_PARENT"),
|
||||
)
|
2
dist/cpr/requirements.txt
vendored
Normal file
2
dist/cpr/requirements.txt
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
GitPython==3.1.0
|
||||
PyGithub==1.47
|
375
dist/index.js
vendored
375
dist/index.js
vendored
@ -34,7 +34,7 @@ module.exports =
|
||||
/******/ // the startup function
|
||||
/******/ function startup() {
|
||||
/******/ // Load entry module and return exports
|
||||
/******/ return __webpack_require__(104);
|
||||
/******/ return __webpack_require__(676);
|
||||
/******/ };
|
||||
/******/
|
||||
/******/ // run startup
|
||||
@ -973,21 +973,6 @@ module.exports = util.assign(
|
||||
|
||||
module.exports = require("tls");
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ 58:
|
||||
/***/ (function(module, __unusedexports, __webpack_require__) {
|
||||
|
||||
// Unique ID creation requires a high quality random # generator. In node.js
|
||||
// this is pretty straight-forward - we use the crypto API.
|
||||
|
||||
var crypto = __webpack_require__(417);
|
||||
|
||||
module.exports = function nodeRNG() {
|
||||
return crypto.randomBytes(16);
|
||||
};
|
||||
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ 87:
|
||||
@ -998,109 +983,6 @@ module.exports = require("os");
|
||||
/***/ }),
|
||||
|
||||
/***/ 104:
|
||||
/***/ (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);
|
||||
|
||||
async function run() {
|
||||
try {
|
||||
// Allows ncc to find assets to be included in the distribution
|
||||
const src = __webpack_require__.ab + "src";
|
||||
core.debug(`src: ${src}`);
|
||||
|
||||
// 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, [
|
||||
"install",
|
||||
"--requirement",
|
||||
`${src}/requirements.txt`,
|
||||
"--no-index",
|
||||
`--find-links=${__dirname}/vendor`
|
||||
]);
|
||||
|
||||
// Fetch action inputs
|
||||
const inputs = {
|
||||
token: core.getInput("token"),
|
||||
path: core.getInput("path"),
|
||||
commitMessage: core.getInput("commit-message"),
|
||||
committer: core.getInput("committer"),
|
||||
author: core.getInput("author"),
|
||||
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"),
|
||||
project: core.getInput("project"),
|
||||
projectColumn: core.getInput("project-column"),
|
||||
branch: core.getInput("branch"),
|
||||
base: core.getInput("base"),
|
||||
branchSuffix: core.getInput("branch-suffix")
|
||||
};
|
||||
core.debug(`Inputs: ${inspect(inputs)}`);
|
||||
|
||||
// Set environment variables from inputs.
|
||||
if (inputs.token) process.env.GITHUB_TOKEN = inputs.token;
|
||||
if (inputs.path) process.env.CPR_PATH = inputs.path;
|
||||
if (inputs.commitMessage) process.env.CPR_COMMIT_MESSAGE = inputs.commitMessage;
|
||||
if (inputs.committer) process.env.CPR_COMMITTER = inputs.committer;
|
||||
if (inputs.author) process.env.CPR_AUTHOR = inputs.author;
|
||||
if (inputs.title) process.env.CPR_TITLE = inputs.title;
|
||||
if (inputs.body) process.env.CPR_BODY = inputs.body;
|
||||
if (inputs.labels) process.env.CPR_LABELS = inputs.labels;
|
||||
if (inputs.assignees) process.env.CPR_ASSIGNEES = inputs.assignees;
|
||||
if (inputs.reviewers) process.env.CPR_REVIEWERS = inputs.reviewers;
|
||||
if (inputs.teamReviewers) process.env.CPR_TEAM_REVIEWERS = inputs.teamReviewers;
|
||||
if (inputs.milestone) process.env.CPR_MILESTONE = inputs.milestone;
|
||||
if (inputs.project) process.env.CPR_PROJECT_NAME = inputs.project;
|
||||
if (inputs.projectColumn) process.env.CPR_PROJECT_COLUMN_NAME = inputs.projectColumn;
|
||||
if (inputs.branch) process.env.CPR_BRANCH = inputs.branch;
|
||||
if (inputs.base) process.env.CPR_BASE = inputs.base;
|
||||
if (inputs.branchSuffix) process.env.CPR_BRANCH_SUFFIX = inputs.branchSuffix;
|
||||
|
||||
// Execute python script
|
||||
await exec.exec(python, [`${src}/create_pull_request.py`]);
|
||||
} catch (error) {
|
||||
core.setFailed(error.message);
|
||||
}
|
||||
}
|
||||
|
||||
run();
|
||||
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ 129:
|
||||
/***/ (function(module) {
|
||||
|
||||
module.exports = require("child_process");
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ 139:
|
||||
/***/ (function(module, __unusedexports, __webpack_require__) {
|
||||
|
||||
const core = __webpack_require__(470);
|
||||
@ -1157,6 +1039,28 @@ let setupPython = function(versionSpec, arch) {
|
||||
module.exports = setupPython;
|
||||
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ 129:
|
||||
/***/ (function(module) {
|
||||
|
||||
module.exports = require("child_process");
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ 139:
|
||||
/***/ (function(module, __unusedexports, __webpack_require__) {
|
||||
|
||||
// Unique ID creation requires a high quality random # generator. In node.js
|
||||
// this is pretty straight-forward - we use the crypto API.
|
||||
|
||||
var crypto = __webpack_require__(417);
|
||||
|
||||
module.exports = function nodeRNG() {
|
||||
return crypto.randomBytes(16);
|
||||
};
|
||||
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ 141:
|
||||
@ -4301,6 +4205,237 @@ function isUnixExecutable(stats) {
|
||||
}
|
||||
//# sourceMappingURL=io-util.js.map
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ 676:
|
||||
/***/ (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__(104);
|
||||
const {
|
||||
getRepoPath,
|
||||
getAndUnsetConfigOption,
|
||||
addConfigOption
|
||||
} = __webpack_require__(718);
|
||||
|
||||
const EXTRAHEADER_OPTION = "http.https://github.com/.extraheader";
|
||||
const EXTRAHEADER_VALUE_REGEX = "^AUTHORIZATION:";
|
||||
|
||||
async function run() {
|
||||
try {
|
||||
// Allows ncc to find assets to be included in the distribution
|
||||
const cpr = __webpack_require__.ab + "cpr";
|
||||
core.debug(`cpr: ${cpr}`);
|
||||
|
||||
// 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, [
|
||||
"install",
|
||||
"--requirement",
|
||||
`${cpr}/requirements.txt`,
|
||||
"--no-index",
|
||||
`--find-links=${__dirname}/vendor`
|
||||
]);
|
||||
|
||||
// Fetch action inputs
|
||||
const inputs = {
|
||||
token: core.getInput("token"),
|
||||
path: core.getInput("path"),
|
||||
commitMessage: core.getInput("commit-message"),
|
||||
committer: core.getInput("committer"),
|
||||
author: core.getInput("author"),
|
||||
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"),
|
||||
project: core.getInput("project"),
|
||||
projectColumn: core.getInput("project-column"),
|
||||
branch: core.getInput("branch"),
|
||||
request_to_parent: core.getInput("request-to-parent"),
|
||||
base: core.getInput("base"),
|
||||
branchSuffix: core.getInput("branch-suffix")
|
||||
};
|
||||
core.debug(`Inputs: ${inspect(inputs)}`);
|
||||
|
||||
// Set environment variables from inputs.
|
||||
if (inputs.token) process.env.GITHUB_TOKEN = inputs.token;
|
||||
if (inputs.path) process.env.CPR_PATH = inputs.path;
|
||||
if (inputs.commitMessage) process.env.CPR_COMMIT_MESSAGE = inputs.commitMessage;
|
||||
if (inputs.committer) process.env.CPR_COMMITTER = inputs.committer;
|
||||
if (inputs.author) process.env.CPR_AUTHOR = inputs.author;
|
||||
if (inputs.title) process.env.CPR_TITLE = inputs.title;
|
||||
if (inputs.body) process.env.CPR_BODY = inputs.body;
|
||||
if (inputs.labels) process.env.CPR_LABELS = inputs.labels;
|
||||
if (inputs.assignees) process.env.CPR_ASSIGNEES = inputs.assignees;
|
||||
if (inputs.reviewers) process.env.CPR_REVIEWERS = inputs.reviewers;
|
||||
if (inputs.teamReviewers) process.env.CPR_TEAM_REVIEWERS = inputs.teamReviewers;
|
||||
if (inputs.milestone) process.env.CPR_MILESTONE = inputs.milestone;
|
||||
if (inputs.project) process.env.CPR_PROJECT_NAME = inputs.project;
|
||||
if (inputs.projectColumn) process.env.CPR_PROJECT_COLUMN_NAME = inputs.projectColumn;
|
||||
if (inputs.branch) process.env.CPR_BRANCH = inputs.branch;
|
||||
if (inputs.request_to_parent) process.env.CPR_REQUEST_TO_PARENT = inputs.request_to_parent;
|
||||
if (inputs.base) process.env.CPR_BASE = inputs.base;
|
||||
if (inputs.branchSuffix) process.env.CPR_BRANCH_SUFFIX = inputs.branchSuffix;
|
||||
|
||||
// Get the repository path
|
||||
var repoPath = getRepoPath(inputs.path);
|
||||
// Get the extraheader config option if it exists
|
||||
var extraHeaderOption = await getAndUnsetConfigOption(
|
||||
repoPath,
|
||||
EXTRAHEADER_OPTION,
|
||||
EXTRAHEADER_VALUE_REGEX
|
||||
);
|
||||
|
||||
// Execute create pull request
|
||||
await exec.exec(python, [`${cpr}/create_pull_request.py`]);
|
||||
} catch (error) {
|
||||
core.setFailed(error.message);
|
||||
} finally {
|
||||
// Restore the extraheader config option
|
||||
if (extraHeaderOption) {
|
||||
if (
|
||||
await addConfigOption(
|
||||
repoPath,
|
||||
EXTRAHEADER_OPTION,
|
||||
extraHeaderOption.value
|
||||
)
|
||||
)
|
||||
core.debug(`Restored config option '${EXTRAHEADER_OPTION}'`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
run();
|
||||
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ 718:
|
||||
/***/ (function(module, __unusedexports, __webpack_require__) {
|
||||
|
||||
const core = __webpack_require__(470);
|
||||
const exec = __webpack_require__(986);
|
||||
const path = __webpack_require__(622);
|
||||
|
||||
function getRepoPath(relativePath) {
|
||||
let githubWorkspacePath = process.env["GITHUB_WORKSPACE"];
|
||||
if (!githubWorkspacePath) {
|
||||
throw new Error("GITHUB_WORKSPACE not defined");
|
||||
}
|
||||
githubWorkspacePath = path.resolve(githubWorkspacePath);
|
||||
core.debug(`githubWorkspacePath: ${githubWorkspacePath}`);
|
||||
|
||||
repoPath = githubWorkspacePath;
|
||||
if (relativePath) repoPath = path.resolve(repoPath, relativePath);
|
||||
|
||||
core.debug(`repoPath: ${repoPath}`);
|
||||
return repoPath;
|
||||
}
|
||||
|
||||
async function execGit(repoPath, args, ignoreReturnCode = false) {
|
||||
const stdout = [];
|
||||
const options = {
|
||||
cwd: repoPath,
|
||||
ignoreReturnCode: ignoreReturnCode,
|
||||
listeners: {
|
||||
stdout: data => {
|
||||
stdout.push(data.toString());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var result = {};
|
||||
result.exitCode = await exec.exec("git", args, options);
|
||||
result.stdout = stdout.join("");
|
||||
return result;
|
||||
}
|
||||
|
||||
async function addConfigOption(repoPath, name, value) {
|
||||
const result = await execGit(
|
||||
repoPath,
|
||||
["config", "--local", "--add", name, value],
|
||||
true
|
||||
);
|
||||
return result.exitCode === 0;
|
||||
}
|
||||
|
||||
async function unsetConfigOption(repoPath, name, valueRegex=".") {
|
||||
const result = await execGit(
|
||||
repoPath,
|
||||
["config", "--local", "--unset", name, valueRegex],
|
||||
true
|
||||
);
|
||||
return result.exitCode === 0;
|
||||
}
|
||||
|
||||
async function configOptionExists(repoPath, name, valueRegex=".") {
|
||||
const result = await execGit(
|
||||
repoPath,
|
||||
["config", "--local", "--name-only", "--get-regexp", name, valueRegex],
|
||||
true
|
||||
);
|
||||
return result.exitCode === 0;
|
||||
}
|
||||
|
||||
async function getConfigOption(repoPath, name, valueRegex=".") {
|
||||
const result = await execGit(
|
||||
repoPath,
|
||||
["config", "--local", "--get-regexp", name, valueRegex],
|
||||
true
|
||||
);
|
||||
const option = result.stdout.trim().split(`${name} `);
|
||||
return {
|
||||
name: name,
|
||||
value: option[1]
|
||||
}
|
||||
}
|
||||
|
||||
async function getAndUnsetConfigOption(repoPath, name, valueRegex=".") {
|
||||
if (await configOptionExists(repoPath, name, valueRegex)) {
|
||||
const option = await getConfigOption(repoPath, name, valueRegex);
|
||||
if (await unsetConfigOption(repoPath, name, valueRegex)) {
|
||||
core.debug(`Unset config option '${name}'`);
|
||||
return option;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getRepoPath,
|
||||
execGit,
|
||||
addConfigOption,
|
||||
unsetConfigOption,
|
||||
configOptionExists,
|
||||
getConfigOption,
|
||||
getAndUnsetConfigOption
|
||||
};
|
||||
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ 722:
|
||||
@ -4733,7 +4868,7 @@ module.exports = require("zlib");
|
||||
/***/ 826:
|
||||
/***/ (function(module, __unusedexports, __webpack_require__) {
|
||||
|
||||
var rng = __webpack_require__(58);
|
||||
var rng = __webpack_require__(139);
|
||||
var bytesToUuid = __webpack_require__(722);
|
||||
|
||||
function v4(options, buf, offset) {
|
||||
|
2
dist/src/requirements.txt
vendored
2
dist/src/requirements.txt
vendored
@ -1,2 +0,0 @@
|
||||
GitPython==3.0.8
|
||||
PyGithub==1.46
|
52
dist/src/setup-python.js
vendored
52
dist/src/setup-python.js
vendored
@ -1,52 +0,0 @@
|
||||
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;
|
BIN
dist/vendor/GitPython-3.0.8.tar.gz
vendored
BIN
dist/vendor/GitPython-3.0.8.tar.gz
vendored
Binary file not shown.
BIN
dist/vendor/GitPython-3.1.0.tar.gz
vendored
Normal file
BIN
dist/vendor/GitPython-3.1.0.tar.gz
vendored
Normal file
Binary file not shown.
BIN
dist/vendor/PyGithub-1.46.tar.gz
vendored
BIN
dist/vendor/PyGithub-1.46.tar.gz
vendored
Binary file not shown.
BIN
dist/vendor/PyGithub-1.47.tar.gz
vendored
Normal file
BIN
dist/vendor/PyGithub-1.47.tar.gz
vendored
Normal file
Binary file not shown.
Binary file not shown.
BIN
dist/vendor/idna-2.8.tar.gz
vendored
BIN
dist/vendor/idna-2.8.tar.gz
vendored
Binary file not shown.
BIN
dist/vendor/idna-2.9.tar.gz
vendored
Normal file
BIN
dist/vendor/idna-2.9.tar.gz
vendored
Normal file
Binary file not shown.
BIN
dist/vendor/requests-2.22.0.tar.gz
vendored
BIN
dist/vendor/requests-2.22.0.tar.gz
vendored
Binary file not shown.
BIN
dist/vendor/requests-2.23.0.tar.gz
vendored
Normal file
BIN
dist/vendor/requests-2.23.0.tar.gz
vendored
Normal file
Binary file not shown.
BIN
dist/vendor/smmap-3.0.1.tar.gz
vendored
Normal file
BIN
dist/vendor/smmap-3.0.1.tar.gz
vendored
Normal file
Binary file not shown.
BIN
dist/vendor/smmap2-2.0.5.tar.gz
vendored
BIN
dist/vendor/smmap2-2.0.5.tar.gz
vendored
Binary file not shown.
BIN
dist/vendor/wrapt-1.12.0.tar.gz
vendored
BIN
dist/vendor/wrapt-1.12.0.tar.gz
vendored
Binary file not shown.
BIN
dist/vendor/wrapt-1.12.1.tar.gz
vendored
Normal file
BIN
dist/vendor/wrapt-1.12.1.tar.gz
vendored
Normal file
Binary file not shown.
@ -13,6 +13,7 @@ This document covers terminology, how the action works, general usage guidelines
|
||||
- [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)
|
||||
- [Push pull request branches to a fork](#push-pull-request-branches-to-a-fork)
|
||||
- [Running in a container](#running-in-a-container)
|
||||
- [Creating pull requests on tag push](#creating-pull-requests-on-tag-push)
|
||||
|
||||
@ -180,6 +181,34 @@ How to use SSH (deploy keys) with create-pull-request action:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
```
|
||||
|
||||
### Push pull request branches to a fork
|
||||
|
||||
To enforce security, you can use a dedicated user using [machine account](https://help.github.com/en/github/site-policy/github-terms-of-service#3-account-requirements).
|
||||
This user has no access to the main repository, it will use their own fork to push code and create the pull request.
|
||||
|
||||
1. Create a new github user, then login with this user.
|
||||
2. fork the repository.
|
||||
3. create a [Personal Access Token (PAT)](https://help.github.com/en/github/authenticating-to-github/creating-a-personal-access-token-for-the-command-line).
|
||||
4. logout and go back to your main user.
|
||||
5. Add a secret to the repository containing the above PAT.
|
||||
6. As shown in the example below, switch the git remote to the fork's url after checkout and set the action input `request-on-parent` to `true`.
|
||||
|
||||
```yaml
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- run: |
|
||||
git config user.password ${{ secrets.PAT }}
|
||||
git remote set-url origin https://github.com/bot-user/fork-project
|
||||
git fetch --unshallow -p origin
|
||||
|
||||
# Make changes to pull request here
|
||||
|
||||
- uses: peter-evans/create-pull-request@v2
|
||||
with:
|
||||
token: ${{ secrets.PAT }}
|
||||
request-on-parent: true
|
||||
```
|
||||
|
||||
### 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.
|
||||
|
3
jest.config.js
Normal file
3
jest.config.js
Normal file
@ -0,0 +1,3 @@
|
||||
process.env = Object.assign(process.env, {
|
||||
GITHUB_WORKSPACE: __dirname
|
||||
});
|
5972
package-lock.json
generated
5972
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
10
package.json
10
package.json
@ -5,8 +5,10 @@
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"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",
|
||||
"lint": "eslint src/index.js",
|
||||
"test": "eslint src/index.js && jest",
|
||||
"build": "ncc build src/index.js -o dist",
|
||||
"vendor-deps": "pip download -r src/cpr/requirements.txt --no-binary=:all: -d dist/vendor",
|
||||
"package": "npm run build && npm run vendor-deps"
|
||||
},
|
||||
"repository": {
|
||||
@ -27,6 +29,8 @@
|
||||
"is-docker": "^2.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@zeit/ncc": "0.21.1"
|
||||
"@zeit/ncc": "0.22.0",
|
||||
"eslint": "6.8.0",
|
||||
"jest": "25.2.3"
|
||||
}
|
||||
}
|
||||
|
@ -56,25 +56,37 @@ def create_or_update_pull_request(
|
||||
team_reviewers,
|
||||
project_name,
|
||||
project_column_name,
|
||||
request_to_parent,
|
||||
):
|
||||
if request_to_parent is None:
|
||||
request_to_parent = False
|
||||
else:
|
||||
request_to_parent = request_to_parent.lower() in ['true', '1', 't', 'y', 'yes', 'on']
|
||||
|
||||
github_repo = head_repo = Github(github_token).get_repo(github_repository)
|
||||
if request_to_parent:
|
||||
github_repo = github_repo.parent
|
||||
if github_repo is None:
|
||||
raise ValueError("The checked out repository is not a fork. Input 'request-to-parent' should be set to false.")
|
||||
|
||||
head_branch = f"{head_repo.owner.login}:{branch}"
|
||||
|
||||
# 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
|
||||
title=title, body=body, base=base, head=head_branch
|
||||
)
|
||||
print(f"Created pull request #{pull_request.number} ({branch} => {base})")
|
||||
print(f"Created pull request #{pull_request.number} ({head_branch} => {github_repo.owner.login}:{base})")
|
||||
except GithubException as e:
|
||||
if e.status == 422:
|
||||
# A pull request exists for this branch and base
|
||||
head_branch = "{}:{}".format(github_repository.split("/")[0], branch)
|
||||
# Get the 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})")
|
||||
print(f"Updated pull request #{pull_request.number} ({head_branch} => {github_repo.owner.login}:{base})")
|
||||
else:
|
||||
print(str(e))
|
||||
raise
|
@ -192,7 +192,7 @@ result = coub.create_or_update_branch(repo, repo_url, commit_message, base, bran
|
||||
|
||||
if result["action"] in ["created", "updated"]:
|
||||
# The branch was created or updated
|
||||
print(f"Pushing pull request branch to 'origin/{branch}'")
|
||||
print(f"Pushing pull request branch to '{repo.full_name}/{branch}'")
|
||||
repo.git.push("--force", repo_url, f"HEAD:refs/heads/{branch}")
|
||||
|
||||
# Set the base. It would have been 'None' if not specified as an input
|
||||
@ -224,4 +224,5 @@ if result["action"] in ["created", "updated"]:
|
||||
os.environ.get("CPR_TEAM_REVIEWERS"),
|
||||
os.environ.get("CPR_PROJECT_NAME"),
|
||||
os.environ.get("CPR_PROJECT_COLUMN_NAME"),
|
||||
os.environ.get("CPR_REQUEST_TO_PARENT"),
|
||||
)
|
2
src/cpr/requirements.txt
Normal file
2
src/cpr/requirements.txt
Normal file
@ -0,0 +1,2 @@
|
||||
GitPython==3.1.0
|
||||
PyGithub==1.47
|
97
src/git.js
Normal file
97
src/git.js
Normal file
@ -0,0 +1,97 @@
|
||||
const core = require("@actions/core");
|
||||
const exec = require("@actions/exec");
|
||||
const path = require("path");
|
||||
|
||||
function getRepoPath(relativePath) {
|
||||
let githubWorkspacePath = process.env["GITHUB_WORKSPACE"];
|
||||
if (!githubWorkspacePath) {
|
||||
throw new Error("GITHUB_WORKSPACE not defined");
|
||||
}
|
||||
githubWorkspacePath = path.resolve(githubWorkspacePath);
|
||||
core.debug(`githubWorkspacePath: ${githubWorkspacePath}`);
|
||||
|
||||
repoPath = githubWorkspacePath;
|
||||
if (relativePath) repoPath = path.resolve(repoPath, relativePath);
|
||||
|
||||
core.debug(`repoPath: ${repoPath}`);
|
||||
return repoPath;
|
||||
}
|
||||
|
||||
async function execGit(repoPath, args, ignoreReturnCode = false) {
|
||||
const stdout = [];
|
||||
const options = {
|
||||
cwd: repoPath,
|
||||
ignoreReturnCode: ignoreReturnCode,
|
||||
listeners: {
|
||||
stdout: data => {
|
||||
stdout.push(data.toString());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var result = {};
|
||||
result.exitCode = await exec.exec("git", args, options);
|
||||
result.stdout = stdout.join("");
|
||||
return result;
|
||||
}
|
||||
|
||||
async function addConfigOption(repoPath, name, value) {
|
||||
const result = await execGit(
|
||||
repoPath,
|
||||
["config", "--local", "--add", name, value],
|
||||
true
|
||||
);
|
||||
return result.exitCode === 0;
|
||||
}
|
||||
|
||||
async function unsetConfigOption(repoPath, name, valueRegex=".") {
|
||||
const result = await execGit(
|
||||
repoPath,
|
||||
["config", "--local", "--unset", name, valueRegex],
|
||||
true
|
||||
);
|
||||
return result.exitCode === 0;
|
||||
}
|
||||
|
||||
async function configOptionExists(repoPath, name, valueRegex=".") {
|
||||
const result = await execGit(
|
||||
repoPath,
|
||||
["config", "--local", "--name-only", "--get-regexp", name, valueRegex],
|
||||
true
|
||||
);
|
||||
return result.exitCode === 0;
|
||||
}
|
||||
|
||||
async function getConfigOption(repoPath, name, valueRegex=".") {
|
||||
const result = await execGit(
|
||||
repoPath,
|
||||
["config", "--local", "--get-regexp", name, valueRegex],
|
||||
true
|
||||
);
|
||||
const option = result.stdout.trim().split(`${name} `);
|
||||
return {
|
||||
name: name,
|
||||
value: option[1]
|
||||
}
|
||||
}
|
||||
|
||||
async function getAndUnsetConfigOption(repoPath, name, valueRegex=".") {
|
||||
if (await configOptionExists(repoPath, name, valueRegex)) {
|
||||
const option = await getConfigOption(repoPath, name, valueRegex);
|
||||
if (await unsetConfigOption(repoPath, name, valueRegex)) {
|
||||
core.debug(`Unset config option '${name}'`);
|
||||
return option;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getRepoPath,
|
||||
execGit,
|
||||
addConfigOption,
|
||||
unsetConfigOption,
|
||||
configOptionExists,
|
||||
getConfigOption,
|
||||
getAndUnsetConfigOption
|
||||
};
|
98
src/git.test.js
Normal file
98
src/git.test.js
Normal file
@ -0,0 +1,98 @@
|
||||
const path = require("path");
|
||||
const {
|
||||
getRepoPath,
|
||||
execGit,
|
||||
addConfigOption,
|
||||
unsetConfigOption,
|
||||
configOptionExists,
|
||||
getConfigOption,
|
||||
getAndUnsetConfigOption
|
||||
} = require("./git");
|
||||
|
||||
test("getRepoPath", async () => {
|
||||
expect(getRepoPath()).toEqual(process.env["GITHUB_WORKSPACE"]);
|
||||
expect(getRepoPath("foo")).toEqual(
|
||||
path.resolve(process.env["GITHUB_WORKSPACE"], "foo")
|
||||
);
|
||||
});
|
||||
|
||||
test("execGit", async () => {
|
||||
const repoPath = getRepoPath();
|
||||
const result = await execGit(
|
||||
repoPath,
|
||||
["config", "--local", "--name-only", "--get-regexp", "remote.origin.url"],
|
||||
true
|
||||
);
|
||||
expect(result.exitCode).toEqual(0);
|
||||
expect(result.stdout.trim()).toEqual("remote.origin.url");
|
||||
});
|
||||
|
||||
test("add and unset config option", async () => {
|
||||
const repoPath = getRepoPath();
|
||||
const add = await addConfigOption(repoPath, "test.add.and.unset.config.option", "foo");
|
||||
expect(add).toBeTruthy();
|
||||
const unset = await unsetConfigOption(repoPath, "test.add.and.unset.config.option");
|
||||
expect(unset).toBeTruthy();
|
||||
});
|
||||
|
||||
test("add and unset config option with value regex", async () => {
|
||||
const repoPath = getRepoPath();
|
||||
const add = await addConfigOption(repoPath, "test.add.and.unset.config.option", "foo bar");
|
||||
expect(add).toBeTruthy();
|
||||
const unset = await unsetConfigOption(repoPath, "test.add.and.unset.config.option", "^foo");
|
||||
expect(unset).toBeTruthy();
|
||||
});
|
||||
|
||||
test("configOptionExists returns true", async () => {
|
||||
const repoPath = getRepoPath();
|
||||
const result = await configOptionExists(repoPath, "remote.origin.url");
|
||||
expect(result).toBeTruthy();
|
||||
});
|
||||
|
||||
test("configOptionExists returns false", async () => {
|
||||
const repoPath = getRepoPath();
|
||||
const result = await configOptionExists(repoPath, "this.key.does.not.exist");
|
||||
expect(result).toBeFalsy();
|
||||
});
|
||||
|
||||
test("get config option", async () => {
|
||||
const repoPath = getRepoPath();
|
||||
const add = await addConfigOption(repoPath, "test.get.config.option", "foo");
|
||||
expect(add).toBeTruthy();
|
||||
const option = await getConfigOption(repoPath, "test.get.config.option");
|
||||
expect(option.value).toEqual("foo");
|
||||
const unset = await unsetConfigOption(repoPath, "test.get.config.option");
|
||||
expect(unset).toBeTruthy();
|
||||
});
|
||||
|
||||
test("get config option with value regex", async () => {
|
||||
const repoPath = getRepoPath();
|
||||
const add = await addConfigOption(repoPath, "test.get.config.option", "foo bar");
|
||||
expect(add).toBeTruthy();
|
||||
const option = await getConfigOption(repoPath, "test.get.config.option", "^foo");
|
||||
expect(option.value).toEqual("foo bar");
|
||||
const unset = await unsetConfigOption(repoPath, "test.get.config.option", "^foo");
|
||||
expect(unset).toBeTruthy();
|
||||
});
|
||||
|
||||
test("get and unset config option is successful", async () => {
|
||||
const repoPath = getRepoPath();
|
||||
const add = await addConfigOption(repoPath, "test.get.and.unset.config.option", "foo");
|
||||
expect(add).toBeTruthy();
|
||||
const getAndUnset = await getAndUnsetConfigOption(repoPath, "test.get.and.unset.config.option");
|
||||
expect(getAndUnset.value).toEqual("foo");
|
||||
});
|
||||
|
||||
test("get and unset config option is successful with value regex", async () => {
|
||||
const repoPath = getRepoPath();
|
||||
const add = await addConfigOption(repoPath, "test.get.and.unset.config.option", "foo bar");
|
||||
expect(add).toBeTruthy();
|
||||
const getAndUnset = await getAndUnsetConfigOption(repoPath, "test.get.and.unset.config.option", "^foo");
|
||||
expect(getAndUnset.value).toEqual("foo bar");
|
||||
});
|
||||
|
||||
test("get and unset config option is unsuccessful", async () => {
|
||||
const repoPath = getRepoPath();
|
||||
const getAndUnset = await getAndUnsetConfigOption(repoPath, "this.key.does.not.exist");
|
||||
expect(getAndUnset).toBeNull();
|
||||
});
|
@ -2,13 +2,21 @@ 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");
|
||||
const setupPython = require("./setup-python");
|
||||
const {
|
||||
getRepoPath,
|
||||
getAndUnsetConfigOption,
|
||||
addConfigOption
|
||||
} = require("./git");
|
||||
|
||||
const EXTRAHEADER_OPTION = "http.https://github.com/.extraheader";
|
||||
const EXTRAHEADER_VALUE_REGEX = "^AUTHORIZATION:";
|
||||
|
||||
async function run() {
|
||||
try {
|
||||
// Allows ncc to find assets to be included in the distribution
|
||||
const src = __dirname + "/src";
|
||||
core.debug(`src: ${src}`);
|
||||
const cpr = __dirname + "/cpr";
|
||||
core.debug(`cpr: ${cpr}`);
|
||||
|
||||
// Determine how to access python and pip
|
||||
const { pip, python } = (function() {
|
||||
@ -33,7 +41,7 @@ async function run() {
|
||||
await exec.exec(pip, [
|
||||
"install",
|
||||
"--requirement",
|
||||
`${src}/requirements.txt`,
|
||||
`${cpr}/requirements.txt`,
|
||||
"--no-index",
|
||||
`--find-links=${__dirname}/vendor`
|
||||
]);
|
||||
@ -55,6 +63,7 @@ async function run() {
|
||||
project: core.getInput("project"),
|
||||
projectColumn: core.getInput("project-column"),
|
||||
branch: core.getInput("branch"),
|
||||
request_to_parent: core.getInput("request-to-parent"),
|
||||
base: core.getInput("base"),
|
||||
branchSuffix: core.getInput("branch-suffix")
|
||||
};
|
||||
@ -76,13 +85,35 @@ async function run() {
|
||||
if (inputs.project) process.env.CPR_PROJECT_NAME = inputs.project;
|
||||
if (inputs.projectColumn) process.env.CPR_PROJECT_COLUMN_NAME = inputs.projectColumn;
|
||||
if (inputs.branch) process.env.CPR_BRANCH = inputs.branch;
|
||||
if (inputs.request_to_parent) process.env.CPR_REQUEST_TO_PARENT = inputs.request_to_parent;
|
||||
if (inputs.base) process.env.CPR_BASE = inputs.base;
|
||||
if (inputs.branchSuffix) process.env.CPR_BRANCH_SUFFIX = inputs.branchSuffix;
|
||||
|
||||
// Execute python script
|
||||
await exec.exec(python, [`${src}/create_pull_request.py`]);
|
||||
// Get the repository path
|
||||
var repoPath = getRepoPath(inputs.path);
|
||||
// Get the extraheader config option if it exists
|
||||
var extraHeaderOption = await getAndUnsetConfigOption(
|
||||
repoPath,
|
||||
EXTRAHEADER_OPTION,
|
||||
EXTRAHEADER_VALUE_REGEX
|
||||
);
|
||||
|
||||
// Execute create pull request
|
||||
await exec.exec(python, [`${cpr}/create_pull_request.py`]);
|
||||
} catch (error) {
|
||||
core.setFailed(error.message);
|
||||
} finally {
|
||||
// Restore the extraheader config option
|
||||
if (extraHeaderOption) {
|
||||
if (
|
||||
await addConfigOption(
|
||||
repoPath,
|
||||
EXTRAHEADER_OPTION,
|
||||
extraHeaderOption.value
|
||||
)
|
||||
)
|
||||
core.debug(`Restored config option '${EXTRAHEADER_OPTION}'`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,2 +0,0 @@
|
||||
GitPython==3.0.8
|
||||
PyGithub==1.46
|
Reference in New Issue
Block a user