Compare commits

..

1 Commits

Author SHA1 Message Date
b8ff24664a add throttling 2024-08-14 21:09:55 +01:00
7 changed files with 64 additions and 129 deletions

View File

@ -75,7 +75,6 @@ All inputs are **optional**. If not set, sensible defaults will be used.
| `team-reviewers` | A comma or newline-separated list of GitHub teams to request a review from. Note that a `repo` scoped [PAT](https://docs.github.com/en/github/authenticating-to-github/creating-a-personal-access-token), or equivalent [GitHub App permissions](docs/concepts-guidelines.md#authenticating-with-github-app-generated-tokens), are required. | |
| `milestone` | The number of the milestone to associate this pull request with. | |
| `draft` | Create a [draft pull request](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/about-pull-requests#draft-pull-requests). It is not possible to change draft status after creation except through the web interface. | `false` |
| `maintainer-can-modify` | Indicates whether [maintainers can modify](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/allowing-changes-to-a-pull-request-branch-created-from-a-fork) the pull request. | `true` |
#### commit-message
@ -117,10 +116,9 @@ The following outputs can be used by subsequent workflow steps.
- `pull-request-number` - The pull request number.
- `pull-request-url` - The URL of the pull request.
- `pull-request-operation` - The pull request operation performed by the action, `created`, `updated`, `closed` or `none`.
- `pull-request-operation` - The pull request operation performed by the action, `created`, `updated` or `closed`.
- `pull-request-head-sha` - The commit SHA of the pull request branch.
- `pull-request-branch` - The branch name of the pull request.
- `pull-request-commits-verified` - Whether GitHub considers the signature of the branch's commits to be verified; `true` or `false`.
Step outputs can be accessed as in the following example.
Note that in order to read the step outputs the action step must have an id.

View File

@ -53,7 +53,7 @@ inputs:
The pull request will be created to merge the fork's branch into the parent's base.
sign-commits:
description: 'Sign commits as `github-actions[bot]` when using `GITHUB_TOKEN`, or your own bot when using GitHub App tokens.'
default: false
default: true
title:
description: 'The title of the pull request.'
default: 'Changes by create-pull-request action'
@ -77,9 +77,6 @@ inputs:
draft:
description: 'Create a draft pull request. It is not possible to change draft status after creation except through the web interface'
default: false
maintainer-can-modify:
description: 'Indicates whether maintainers can modify the pull request.'
default: true
outputs:
pull-request-number:
description: 'The pull request number'

80
dist/index.js vendored
View File

@ -356,9 +356,8 @@ function createPullRequest(inputs) {
core.endGroup();
core.startGroup('Determining the base and head repositories');
const baseRemote = gitConfigHelper.getGitRemote();
// Init the GitHub clients
const ghBranch = new github_helper_1.GitHubHelper(baseRemote.hostname, inputs.gitToken);
const ghPull = new github_helper_1.GitHubHelper(baseRemote.hostname, inputs.token);
// Init the GitHub client
const githubHelper = new github_helper_1.GitHubHelper(baseRemote.hostname, inputs.token);
// Determine the head repository; the target for the pull request branch
const branchRemoteName = inputs.pushToFork ? 'fork' : 'origin';
const branchRepository = inputs.pushToFork
@ -367,8 +366,8 @@ function createPullRequest(inputs) {
if (inputs.pushToFork) {
// Check if the supplied fork is really a fork of the base
core.info(`Checking if '${branchRepository}' is a fork of '${baseRemote.repository}'`);
const baseParentRepository = yield ghBranch.getRepositoryParent(baseRemote.repository);
const branchParentRepository = yield ghBranch.getRepositoryParent(branchRepository);
const baseParentRepository = yield githubHelper.getRepositoryParent(baseRemote.repository);
const branchParentRepository = yield githubHelper.getRepositoryParent(branchRepository);
if (branchParentRepository == null) {
throw new Error(`Repository '${branchRepository}' is not a fork. Unable to continue.`);
}
@ -451,15 +450,9 @@ function createPullRequest(inputs) {
core.info(`Configured git committer as '${parsedCommitter.name} <${parsedCommitter.email}>'`);
core.info(`Configured git author as '${parsedAuthor.name} <${parsedAuthor.email}>'`);
core.endGroup();
// Action outputs
const outputs = new Map();
outputs.set('pull-request-branch', inputs.branch);
outputs.set('pull-request-operation', 'none');
outputs.set('pull-request-commits-verified', 'false');
// Create or update the pull request branch
core.startGroup('Create or update the pull request branch');
const result = yield (0, create_or_update_branch_1.createOrUpdateBranch)(git, inputs.commitMessage, inputs.base, inputs.branch, branchRemoteName, inputs.signoff, inputs.addPaths, inputs.signCommits);
outputs.set('pull-request-head-sha', result.headSha);
// Set the base. It would have been '' if not specified as an input
inputs.base = result.base;
core.endGroup();
@ -470,9 +463,7 @@ function createPullRequest(inputs) {
// Create signed commits via the GitHub API
const stashed = yield git.stashPush(['--include-untracked']);
yield git.checkout(inputs.branch);
const pushSignedCommitsResult = yield ghBranch.pushSignedCommits(result.branchCommits, result.baseSha, repoPath, branchRepository, inputs.branch);
outputs.set('pull-request-head-sha', pushSignedCommitsResult.sha);
outputs.set('pull-request-commits-verified', pushSignedCommitsResult.verified.toString());
yield githubHelper.pushSignedCommits(result.branchCommits, result.baseSha, repoPath, branchRepository, inputs.branch);
yield git.checkout('-');
if (stashed) {
yield git.stashPop();
@ -488,16 +479,22 @@ function createPullRequest(inputs) {
core.endGroup();
}
if (result.hasDiffWithBase) {
// Create or update the pull request
core.startGroup('Create or update the pull request');
const pull = yield ghPull.createOrUpdatePullRequest(inputs, baseRemote.repository, branchRepository);
outputs.set('pull-request-number', pull.number.toString());
outputs.set('pull-request-url', pull.html_url);
const pull = yield githubHelper.createOrUpdatePullRequest(inputs, baseRemote.repository, branchRepository);
core.endGroup();
// Set outputs
core.startGroup('Setting outputs');
core.setOutput('pull-request-number', pull.number);
core.setOutput('pull-request-url', pull.html_url);
if (pull.created) {
outputs.set('pull-request-operation', 'created');
core.setOutput('pull-request-operation', 'created');
}
else if (result.action == 'updated') {
outputs.set('pull-request-operation', 'updated');
core.setOutput('pull-request-operation', 'updated');
}
core.setOutput('pull-request-head-sha', result.headSha);
core.setOutput('pull-request-branch', inputs.branch);
// Deprecated
core.exportVariable('PULL_REQUEST_NUMBER', pull.number);
core.endGroup();
@ -515,17 +512,13 @@ function createPullRequest(inputs) {
branchRemoteName,
`refs/heads/${inputs.branch}`
]);
outputs.set('pull-request-operation', 'closed');
// Set outputs
core.startGroup('Setting outputs');
core.setOutput('pull-request-operation', 'closed');
core.endGroup();
}
}
}
// Set outputs
core.startGroup('Setting outputs');
for (const [key, value] of outputs) {
core.info(`${key} = ${value}`);
core.setOutput(key, value);
}
core.endGroup();
}
catch (error) {
core.setFailed(utils.getErrorMessage(error));
@ -1169,9 +1162,7 @@ const core = __importStar(__nccwpck_require__(2186));
const octokit_client_1 = __nccwpck_require__(5040);
const p_limit_1 = __importDefault(__nccwpck_require__(3783));
const utils = __importStar(__nccwpck_require__(918));
const ERROR_PR_ALREADY_EXISTS = 'A pull request already exists for';
const ERROR_PR_REVIEW_TOKEN_SCOPE = 'Validation Failed: "Could not resolve to a node with the global id of';
const ERROR_PR_FORK_COLLAB = `Fork collab can't be granted by someone without permission`;
const blobCreationLimit = (0, p_limit_1.default)(8);
class GitHubHelper {
constructor(githubServerHostname, token) {
@ -1202,7 +1193,7 @@ class GitHubHelper {
// Try to create the pull request
try {
core.info(`Attempting creation of pull request`);
const { data: pull } = yield this.octokit.rest.pulls.create(Object.assign(Object.assign({}, this.parseRepository(baseRepository)), { title: inputs.title, head: headBranch, head_repo: headRepository, base: inputs.base, body: inputs.body, draft: inputs.draft, maintainer_can_modify: inputs.maintainerCanModify }));
const { data: pull } = yield this.octokit.rest.pulls.create(Object.assign(Object.assign({}, this.parseRepository(baseRepository)), { title: inputs.title, head: headBranch, head_repo: headRepository, base: inputs.base, body: inputs.body, draft: inputs.draft }));
core.info(`Created pull request #${pull.number} (${headBranch} => ${inputs.base})`);
return {
number: pull.number,
@ -1211,15 +1202,9 @@ class GitHubHelper {
};
}
catch (e) {
const errorMessage = utils.getErrorMessage(e);
if (errorMessage.includes(ERROR_PR_ALREADY_EXISTS)) {
if (utils.getErrorMessage(e).includes(`A pull request already exists for`)) {
core.info(`A pull request already exists for ${headBranch}`);
}
else if (errorMessage.includes(ERROR_PR_FORK_COLLAB)) {
core.warning('An attempt was made to create a pull request using a token that does not have write access to the head branch.');
core.warning(`For this case, set input 'maintainer-can-modify' to 'false' to allow pull request creation.`);
throw e;
}
else {
throw e;
}
@ -1292,15 +1277,11 @@ class GitHubHelper {
}
pushSignedCommits(branchCommits, baseSha, repoPath, branchRepository, branch) {
return __awaiter(this, void 0, void 0, function* () {
let headCommit = {
sha: baseSha,
verified: false
};
let headSha = baseSha;
for (const commit of branchCommits) {
headCommit = yield this.createCommit(commit, [headCommit.sha], repoPath, branchRepository);
headSha = yield this.createCommit(commit, [headSha], repoPath, branchRepository);
}
yield this.createOrUpdateRef(branchRepository, branch, headCommit.sha);
return headCommit;
yield this.createOrUpdateRef(branchRepository, branch, headSha);
});
}
createCommit(commit, parents, repoPath, branchRepository) {
@ -1331,10 +1312,7 @@ class GitHubHelper {
const { data: remoteCommit } = yield this.octokit.rest.git.createCommit(Object.assign(Object.assign({}, repository), { parents: parents, tree: treeSha, message: `${commit.subject}\n\n${commit.body}` }));
core.info(`Created commit ${remoteCommit.sha} for local commit ${commit.sha}`);
core.info(`Commit verified: ${remoteCommit.verification.verified}; reason: ${remoteCommit.verification.reason}`);
return {
sha: remoteCommit.sha,
verified: remoteCommit.verification.verified
};
return remoteCommit.sha;
});
}
createOrUpdateRef(branchRepository, branch, newHead) {
@ -1427,8 +1405,7 @@ function run() {
reviewers: utils.getInputAsArray('reviewers'),
teamReviewers: utils.getInputAsArray('team-reviewers'),
milestone: Number(core.getInput('milestone')),
draft: core.getBooleanInput('draft'),
maintainerCanModify: core.getBooleanInput('maintainer-can-modify')
draft: core.getBooleanInput('draft')
};
core.debug(`Inputs: ${(0, util_1.inspect)(inputs)}`);
if (!inputs.token) {
@ -1508,9 +1485,8 @@ exports.throttleOptions = {
return true;
}
},
onSecondaryRateLimit: (retryAfter, options) => {
onSecondaryRateLimit: (_, options) => {
core.warning(`Hit secondary rate limit for request ${options.method} ${options.url}`);
core.warning(`Requests may be retried after ${retryAfter} seconds.`);
}
};
const proxyFetch = (proxyUrl) => (url, opts) => {

View File

@ -33,7 +33,6 @@ export interface Inputs {
teamReviewers: string[]
milestone: number
draft: boolean
maintainerCanModify: boolean
}
export async function createPullRequest(inputs: Inputs): Promise<void> {
@ -47,9 +46,8 @@ export async function createPullRequest(inputs: Inputs): Promise<void> {
core.startGroup('Determining the base and head repositories')
const baseRemote = gitConfigHelper.getGitRemote()
// Init the GitHub clients
const ghBranch = new GitHubHelper(baseRemote.hostname, inputs.gitToken)
const ghPull = new GitHubHelper(baseRemote.hostname, inputs.token)
// Init the GitHub client
const githubHelper = new GitHubHelper(baseRemote.hostname, inputs.token)
// Determine the head repository; the target for the pull request branch
const branchRemoteName = inputs.pushToFork ? 'fork' : 'origin'
const branchRepository = inputs.pushToFork
@ -60,11 +58,11 @@ export async function createPullRequest(inputs: Inputs): Promise<void> {
core.info(
`Checking if '${branchRepository}' is a fork of '${baseRemote.repository}'`
)
const baseParentRepository = await ghBranch.getRepositoryParent(
const baseParentRepository = await githubHelper.getRepositoryParent(
baseRemote.repository
)
const branchParentRepository =
await ghBranch.getRepositoryParent(branchRepository)
await githubHelper.getRepositoryParent(branchRepository)
if (branchParentRepository == null) {
throw new Error(
`Repository '${branchRepository}' is not a fork. Unable to continue.`
@ -177,12 +175,6 @@ export async function createPullRequest(inputs: Inputs): Promise<void> {
)
core.endGroup()
// Action outputs
const outputs = new Map<string, string>()
outputs.set('pull-request-branch', inputs.branch)
outputs.set('pull-request-operation', 'none')
outputs.set('pull-request-commits-verified', 'false')
// Create or update the pull request branch
core.startGroup('Create or update the pull request branch')
const result = await createOrUpdateBranch(
@ -195,7 +187,6 @@ export async function createPullRequest(inputs: Inputs): Promise<void> {
inputs.addPaths,
inputs.signCommits
)
outputs.set('pull-request-head-sha', result.headSha)
// Set the base. It would have been '' if not specified as an input
inputs.base = result.base
core.endGroup()
@ -209,18 +200,13 @@ export async function createPullRequest(inputs: Inputs): Promise<void> {
// Create signed commits via the GitHub API
const stashed = await git.stashPush(['--include-untracked'])
await git.checkout(inputs.branch)
const pushSignedCommitsResult = await ghBranch.pushSignedCommits(
await githubHelper.pushSignedCommits(
result.branchCommits,
result.baseSha,
repoPath,
branchRepository,
inputs.branch
)
outputs.set('pull-request-head-sha', pushSignedCommitsResult.sha)
outputs.set(
'pull-request-commits-verified',
pushSignedCommitsResult.verified.toString()
)
await git.checkout('-')
if (stashed) {
await git.stashPop()
@ -236,19 +222,26 @@ export async function createPullRequest(inputs: Inputs): Promise<void> {
}
if (result.hasDiffWithBase) {
// Create or update the pull request
core.startGroup('Create or update the pull request')
const pull = await ghPull.createOrUpdatePullRequest(
const pull = await githubHelper.createOrUpdatePullRequest(
inputs,
baseRemote.repository,
branchRepository
)
outputs.set('pull-request-number', pull.number.toString())
outputs.set('pull-request-url', pull.html_url)
core.endGroup()
// Set outputs
core.startGroup('Setting outputs')
core.setOutput('pull-request-number', pull.number)
core.setOutput('pull-request-url', pull.html_url)
if (pull.created) {
outputs.set('pull-request-operation', 'created')
core.setOutput('pull-request-operation', 'created')
} else if (result.action == 'updated') {
outputs.set('pull-request-operation', 'updated')
core.setOutput('pull-request-operation', 'updated')
}
core.setOutput('pull-request-head-sha', result.headSha)
core.setOutput('pull-request-branch', inputs.branch)
// Deprecated
core.exportVariable('PULL_REQUEST_NUMBER', pull.number)
core.endGroup()
@ -267,18 +260,13 @@ export async function createPullRequest(inputs: Inputs): Promise<void> {
branchRemoteName,
`refs/heads/${inputs.branch}`
])
outputs.set('pull-request-operation', 'closed')
// Set outputs
core.startGroup('Setting outputs')
core.setOutput('pull-request-operation', 'closed')
core.endGroup()
}
}
}
// Set outputs
core.startGroup('Setting outputs')
for (const [key, value] of outputs) {
core.info(`${key} = ${value}`)
core.setOutput(key, value)
}
core.endGroup()
} catch (error) {
core.setFailed(utils.getErrorMessage(error))
} finally {

View File

@ -5,10 +5,8 @@ import {Octokit, OctokitOptions, throttleOptions} from './octokit-client'
import pLimit from 'p-limit'
import * as utils from './utils'
const ERROR_PR_ALREADY_EXISTS = 'A pull request already exists for'
const ERROR_PR_REVIEW_TOKEN_SCOPE =
'Validation Failed: "Could not resolve to a node with the global id of'
const ERROR_PR_FORK_COLLAB = `Fork collab can't be granted by someone without permission`
const blobCreationLimit = pLimit(8)
@ -23,11 +21,6 @@ interface Pull {
created: boolean
}
interface CommitResponse {
sha: string
verified: boolean
}
type TreeObject = {
path: string
mode: '100644' | '100755' | '040000' | '160000' | '120000'
@ -78,8 +71,7 @@ export class GitHubHelper {
head_repo: headRepository,
base: inputs.base,
body: inputs.body,
draft: inputs.draft,
maintainer_can_modify: inputs.maintainerCanModify
draft: inputs.draft
})
core.info(
`Created pull request #${pull.number} (${headBranch} => ${inputs.base})`
@ -90,17 +82,10 @@ export class GitHubHelper {
created: true
}
} catch (e) {
const errorMessage = utils.getErrorMessage(e)
if (errorMessage.includes(ERROR_PR_ALREADY_EXISTS)) {
if (
utils.getErrorMessage(e).includes(`A pull request already exists for`)
) {
core.info(`A pull request already exists for ${headBranch}`)
} else if (errorMessage.includes(ERROR_PR_FORK_COLLAB)) {
core.warning(
'An attempt was made to create a pull request using a token that does not have write access to the head branch.'
)
core.warning(
`For this case, set input 'maintainer-can-modify' to 'false' to allow pull request creation.`
)
throw e
} else {
throw e
}
@ -218,21 +203,17 @@ export class GitHubHelper {
repoPath: string,
branchRepository: string,
branch: string
): Promise<CommitResponse> {
let headCommit: CommitResponse = {
sha: baseSha,
verified: false
}
): Promise<void> {
let headSha = baseSha
for (const commit of branchCommits) {
headCommit = await this.createCommit(
headSha = await this.createCommit(
commit,
[headCommit.sha],
[headSha],
repoPath,
branchRepository
)
}
await this.createOrUpdateRef(branchRepository, branch, headCommit.sha)
return headCommit
await this.createOrUpdateRef(branchRepository, branch, headSha)
}
private async createCommit(
@ -240,7 +221,7 @@ export class GitHubHelper {
parents: string[],
repoPath: string,
branchRepository: string
): Promise<CommitResponse> {
): Promise<string> {
const repository = this.parseRepository(branchRepository)
let treeSha = commit.tree
if (commit.changes.length > 0) {
@ -289,10 +270,7 @@ export class GitHubHelper {
core.info(
`Commit verified: ${remoteCommit.verification.verified}; reason: ${remoteCommit.verification.reason}`
)
return {
sha: remoteCommit.sha,
verified: remoteCommit.verification.verified
}
return remoteCommit.sha
}
private async createOrUpdateRef(

View File

@ -28,8 +28,7 @@ async function run(): Promise<void> {
reviewers: utils.getInputAsArray('reviewers'),
teamReviewers: utils.getInputAsArray('team-reviewers'),
milestone: Number(core.getInput('milestone')),
draft: core.getBooleanInput('draft'),
maintainerCanModify: core.getBooleanInput('maintainer-can-modify')
draft: core.getBooleanInput('draft')
}
core.debug(`Inputs: ${inspect(inputs)}`)

View File

@ -25,11 +25,10 @@ export const throttleOptions = {
return true
}
},
onSecondaryRateLimit: (retryAfter, options) => {
onSecondaryRateLimit: (_, options) => {
core.warning(
`Hit secondary rate limit for request ${options.method} ${options.url}`
)
core.warning(`Requests may be retried after ${retryAfter} seconds.`)
}
}