'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; } function _objectSpread2(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; } function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } async function valueToTreeObject(octokit, owner, repo, path, value) { var _value$mode; const defaultMode = "100644"; // Text files can be changed through the .content key if (typeof value === "string") { return { path, mode: defaultMode, content: value }; } const mode = (_value$mode = value.mode) !== null && _value$mode !== void 0 ? _value$mode : defaultMode; // UTF-8 files can be treated as text files // https://github.com/gr2m/octokit-plugin-create-pull-request/pull/133 if (value.encoding === "utf-8") { return { path, mode: mode, content: value.content }; } // Binary files need to be created first using the git blob API, // then changed by referencing in the .sha key const { data } = await octokit.request("POST /repos/{owner}/{repo}/git/blobs", _objectSpread2({ owner, repo }, value)); const blobSha = data.sha; return { path, mode: mode, sha: blobSha }; } const DELETE_FILE = Symbol("DELETE_FILE"); async function createTree(state, changes) { const { octokit, owner, repo, ownerOrFork, latestCommitSha, latestCommitTreeSha } = state; let tree = []; for (const path of Object.keys(changes.files)) { const value = changes.files[path]; if (value === DELETE_FILE) { // Deleting a non-existent file from a tree leads to an "GitRPC::BadObjectState" error, // so we only attempt to delete the file if it exists. try { // https://developer.github.com/v3/repos/contents/#get-contents await octokit.request("HEAD /repos/{owner}/{repo}/contents/:path", { owner: ownerOrFork, repo, ref: latestCommitSha, path }); tree.push({ path, mode: "100644", sha: null }); continue; } catch (error) { continue; } } // When passed a function, retrieve the content of the file, pass it // to the function, then return the result if (typeof value === "function") { let result; try { const { data: file } = await octokit.request("GET /repos/{owner}/{repo}/contents/:path", { owner: ownerOrFork, repo, ref: latestCommitSha, path }); result = await value(Object.assign(file, { exists: true })); if (result === DELETE_FILE) { try { // https://developer.github.com/v3/repos/contents/#get-contents await octokit.request("HEAD /repos/{owner}/{repo}/contents/:path", { owner: ownerOrFork, repo, ref: latestCommitSha, path }); tree.push({ path, mode: "100644", sha: null }); continue; } catch (error) { // istanbul ignore next continue; } } } catch (error) { // @ts-ignore // istanbul ignore if if (error.status !== 404) throw error; // @ts-ignore result = await value({ exists: false }); } if (result === null || typeof result === "undefined" || typeof result === "symbol") { continue; } tree.push( // @ts-expect-error - Argument result can never be of type Symbol at this branch // because the above condition will catch it and move on to the next iteration cycle await valueToTreeObject(octokit, ownerOrFork, repo, path, result)); continue; } // @ts-expect-error - Argument value can never be of type Symbol at this branch // because the above condition will catch it and initiate a file deletion operation tree.push(await valueToTreeObject(octokit, ownerOrFork, repo, path, value)); continue; } tree = tree.filter(Boolean); if (tree.length === 0) { return null; } // https://developer.github.com/v3/git/trees/#create-a-tree const { data: { sha: newTreeSha } } = await octokit.request("POST /repos/{owner}/{repo}/git/trees", { owner: ownerOrFork, repo, base_tree: latestCommitTreeSha, tree }); return newTreeSha; } async function createCommit(state, treeCreated, changes) { const { octokit, repo, ownerOrFork, latestCommitSha } = state; const message = treeCreated ? changes.commit : typeof changes.emptyCommit === "string" ? changes.emptyCommit : changes.commit; const commit = { message, author: changes.author, committer: changes.committer, tree: state.latestCommitTreeSha, parents: [latestCommitSha] }; // https://developer.github.com/v3/git/commits/#create-a-commit const { data: latestCommit } = await octokit.request("POST /repos/{owner}/{repo}/git/commits", _objectSpread2(_objectSpread2({ owner: ownerOrFork, repo }, commit), {}, { signature: changes.signature ? await changes.signature(commit) : undefined })); return latestCommit.sha; } async function composeCreatePullRequest(octokit, { owner, repo, title, body, base, head, createWhenEmpty, changes: changesOption, draft = false, labels = [], forceFork = false, update = false }) { var _branchInfo$repositor, _branchInfo$repositor2, _branchInfo$repositor3, _branchInfo$repositor4; 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 }; // https://developer.github.com/v3/repos/#get-a-repository 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) { // https://developer.github.com/v3/users/#get-the-authenticated-user const user = await octokit.request("GET /user"); // https://developer.github.com/v3/repos/forks/#list-forks const forks = await octokit.request("GET /repos/{owner}/{repo}/forks", { owner, repo }); const hasFork = forks.data.find( /* istanbul ignore next - fork owner can be null, but we don't test that */ fork => fork.owner && fork.owner.login === user.data.login); if (!hasFork) { // https://developer.github.com/v3/repos/forks/#create-a-fork await octokit.request("POST /repos/{owner}/{repo}/forks", { owner, repo }); } state.ownerOrFork = user.data.login; } // https://developer.github.com/v3/repos/commits/#list-commits-on-a-repository 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 ($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$repositor = branchInfo.repository.ref) === null || _branchInfo$repositor === void 0 ? void 0 : (_branchInfo$repositor2 = _branchInfo$repositor.associatedPullRequests) === null || _branchInfo$repositor2 === void 0 ? void 0 : (_branchInfo$repositor3 = _branchInfo$repositor2.edges) === null || _branchInfo$repositor3 === void 0 ? void 0 : (_branchInfo$repositor4 = _branchInfo$repositor3[0]) === null || _branchInfo$repositor4 === void 0 ? void 0 : _branchInfo$repositor4.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) { // https://docs.github.com/en/rest/git/refs#update-a-reference await octokit.request("PATCH /repos/{owner}/{repo}/git/refs/{ref}", { owner: state.ownerOrFork, repo, sha: state.latestCommitSha, ref: `heads/${head}`, force: true }); } else { // https://developer.github.com/v3/git/refs/#create-a-reference 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) { // https://docs.github.com/en/rest/pulls/pulls#update-a-pull-request res = await octokit.request("PATCH /repos/{owner}/{repo}/pulls/{pull_number}", _objectSpread2({ pull_number: existingPullRequest.number }, pullRequestOptions)); } else { // https://developer.github.com/v3/pulls/#create-a-pull-request 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 }); // istanbul ignore if 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) { // @ts-ignore // istanbul ignore if 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; } // @ts-ignore if (error.status !== 403) throw error; } } return res; } const VERSION = "5.1.1"; /** * @param octokit Octokit instance */ function createPullRequest(octokit) { return { createPullRequest: composeCreatePullRequest.bind(null, octokit) }; } createPullRequest.VERSION = VERSION; exports.DELETE_FILE = DELETE_FILE; exports.composeCreatePullRequest = composeCreatePullRequest; exports.createPullRequest = createPullRequest; //# sourceMappingURL=index.js.map