UNPKG

octokit-plugin-create-pull-request

Version:

Octokit plugin to create a pull request with multiple file changes

199 lines (198 loc) 5.38 kB
import { createTree } from "./create-tree.js"; import { createCommit } from "./create-commit.js"; async function composeCreatePullRequest(octokit, { owner, repo, title, body, base, head, createWhenEmpty, changes: changesOption, draft = false, labels = [], forceFork = false, update = false }) { if (head === base) { throw new Error( '[octokit-plugin-create-pull-request] "head" cannot be the same value as "base"' ); } const changes = Array.isArray(changesOption) ? changesOption : [changesOption]; if (changes.length === 0) throw new Error( '[octokit-plugin-create-pull-request] "changes" cannot be an empty array' ); const state = { octokit, owner, repo }; const { data: repository, headers } = await octokit.request( "GET /repos/{owner}/{repo}", { owner, repo } ); const isUser = !!headers["x-oauth-scopes"]; if (!repository.permissions) { throw new Error( "[octokit-plugin-create-pull-request] Missing authentication" ); } if (!base) { base = repository.default_branch; } state.ownerOrFork = owner; if (forceFork || isUser && !repository.permissions.push) { const user = await octokit.request("GET /user"); const forks = await octokit.request("GET /repos/{owner}/{repo}/forks", { owner, repo }); const hasFork = forks.data.find( /* v8 ignore next - fork owner can be null, but we don't test that */ (fork) => fork.owner && fork.owner.login === user.data.login ); if (!hasFork) { await octokit.request("POST /repos/{owner}/{repo}/forks", { owner, repo }); } state.ownerOrFork = user.data.login; } const { data: [latestCommit] } = await octokit.request("GET /repos/{owner}/{repo}/commits", { owner, repo, sha: base, per_page: 1 }); state.latestCommitSha = latestCommit.sha; state.latestCommitTreeSha = latestCommit.commit.tree.sha; const baseCommitTreeSha = latestCommit.commit.tree.sha; for (const change of changes) { let treeCreated = false; if (change.files && Object.keys(change.files).length) { const latestCommitTreeSha = await createTree( state, change ); if (latestCommitTreeSha) { state.latestCommitTreeSha = latestCommitTreeSha; treeCreated = true; } } if (treeCreated || change.emptyCommit !== false) { state.latestCommitSha = await createCommit( state, treeCreated, change ); } } const hasNoChanges = baseCommitTreeSha === state.latestCommitTreeSha; if (hasNoChanges && createWhenEmpty === false) { return null; } const branchInfo = await octokit.graphql( ` query getPullRequestsForBranch($owner: String!, $repo: String!, $head: String!) { repository(name: $repo, owner: $owner) { ref(qualifiedName: $head) { associatedPullRequests(first: 1, states: OPEN) { edges { node { id number url } } } } } }`, { owner: state.ownerOrFork, repo, head } ); const branchExists = !!branchInfo.repository.ref; const existingPullRequest = branchInfo.repository.ref?.associatedPullRequests?.edges?.[0]?.node; if (existingPullRequest && !update) { throw new Error( `[octokit-plugin-create-pull-request] Pull request already exists: ${existingPullRequest.url}. Set update=true to enable updating` ); } if (branchExists) { await octokit.request("PATCH /repos/{owner}/{repo}/git/refs/{ref}", { owner: state.ownerOrFork, repo, sha: state.latestCommitSha, ref: `heads/${head}`, force: true }); } else { await octokit.request("POST /repos/{owner}/{repo}/git/refs", { owner: state.ownerOrFork, repo, sha: state.latestCommitSha, ref: `refs/heads/${head}` }); } const pullRequestOptions = { owner, repo, head: `${state.ownerOrFork}:${head}`, base, title, body, draft }; let res; if (existingPullRequest) { res = await octokit.request( "PATCH /repos/{owner}/{repo}/pulls/{pull_number}", { pull_number: existingPullRequest.number, ...pullRequestOptions } ); } else { res = await octokit.request( "POST /repos/{owner}/{repo}/pulls", pullRequestOptions ); } if (labels.length) { try { const labelRes = await octokit.request( "POST /repos/{owner}/{repo}/issues/{number}/labels", { owner, repo, number: res.data.number, labels } ); if (labelRes.data.length > labels.length) { octokit.log.warn( "The pull request already contains more labels than the ones provided. This could be due to the presence of previous labels." ); } } catch (error) { if (error.status === 403) { octokit.log.warn( "You do not have permissions to apply labels to this pull request. However, the pull request has been successfully created without the requested labels." ); return res; } if (error.status !== 403) throw error; } } return res; } export { composeCreatePullRequest };