diff --git a/dist/676.index.js b/dist/676.index.js index eb9978c71..5603a578a 100644 --- a/dist/676.index.js +++ b/dist/676.index.js @@ -146,29 +146,36 @@ const sortPrsByQueuePosition = (queuedPrs) => queuedPrs return { number: pr.number, label, - queuePosition + queuePosition, + sha: pr.head.sha }; }) .sort((pr1, pr2) => pr1.queuePosition - pr2.queuePosition); const updateQueuePosition = (pr, index) => __awaiter(void 0, void 0, void 0, function* () { - const { number, label, queuePosition } = pr; + const { number, label, queuePosition, sha } = pr; const newQueuePosition = index + 1; if (!label || isNaN(queuePosition) || queuePosition === newQueuePosition) { return; } if (newQueuePosition === 1) { - const { data: pullRequest } = yield octokit/* octokit.pulls.get */.K.pulls.get(Object.assign({ pull_number: number }, github.context.repo)); + const { data: firstPrInQueue } = yield octokit/* octokit.pulls.get */.K.pulls.get(Object.assign({ pull_number: number }, github.context.repo)); yield (0,set_commit_status.setCommitStatus)({ - sha: pullRequest.head.sha, + sha: firstPrInQueue.head.sha, context: constants/* MERGE_QUEUE_STATUS */.Cb, state: 'success', description: 'This PR is next to merge.' }); - yield Promise.all([(0,remove_label.removeLabelIfExists)(constants/* JUMP_THE_QUEUE_PR_LABEL */.nJ, number), (0,prepare_queued_pr_for_merge.updatePrWithMainline)(pullRequest)]); + yield Promise.all([(0,remove_label.removeLabelIfExists)(constants/* JUMP_THE_QUEUE_PR_LABEL */.nJ, number), (0,prepare_queued_pr_for_merge.updatePrWithDefaultBranch)(firstPrInQueue)]); } return Promise.all([ octokit/* octokit.issues.addLabels */.K.issues.addLabels(Object.assign({ labels: [`${constants/* QUEUED_FOR_MERGE_PREFIX */.Ee} #${newQueuePosition}`], issue_number: number }, github.context.repo)), - (0,remove_label.removeLabelIfExists)(label, number) + (0,remove_label.removeLabelIfExists)(label, number), + (0,set_commit_status.setCommitStatus)({ + sha, + context: constants/* MERGE_QUEUE_STATUS */.Cb, + state: 'pending', + description: 'This PR is in line to merge.' + }) ]); }); @@ -288,7 +295,7 @@ const enableAutoMerge = (pullRequestId, mergeMethod = 'SQUASH') => manage_merge_ __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ "prepareQueuedPrForMerge": () => (/* binding */ prepareQueuedPrForMerge), -/* harmony export */ "updatePrWithMainline": () => (/* binding */ updatePrWithMainline) +/* harmony export */ "updatePrWithDefaultBranch": () => (/* binding */ updatePrWithDefaultBranch) /* harmony export */ }); /* harmony import */ var _actions_core__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(2186); /* harmony import */ var _actions_core__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_actions_core__WEBPACK_IMPORTED_MODULE_0__); @@ -327,7 +334,7 @@ const prepareQueuedPrForMerge = () => __awaiter(void 0, void 0, void 0, function const { data } = yield _octokit__WEBPACK_IMPORTED_MODULE_3__/* .octokit.pulls.list */ .K.pulls.list(Object.assign({ state: 'open', per_page: 100 }, _actions_github__WEBPACK_IMPORTED_MODULE_2__.context.repo)); const pullRequest = findNextPrToMerge(data); if (pullRequest) { - return updatePrWithMainline(pullRequest); + return updatePrWithDefaultBranch(pullRequest); } }); const findNextPrToMerge = (pullRequests) => { @@ -335,7 +342,7 @@ const findNextPrToMerge = (pullRequests) => { return (_a = pullRequests.find(pr => hasRequiredLabels(pr, [_constants__WEBPACK_IMPORTED_MODULE_1__/* .READY_FOR_MERGE_PR_LABEL */ .Ak, _constants__WEBPACK_IMPORTED_MODULE_1__/* .JUMP_THE_QUEUE_PR_LABEL */ .nJ]))) !== null && _a !== void 0 ? _a : pullRequests.find(pr => hasRequiredLabels(pr, [_constants__WEBPACK_IMPORTED_MODULE_1__/* .READY_FOR_MERGE_PR_LABEL */ .Ak, _constants__WEBPACK_IMPORTED_MODULE_1__/* .FIRST_QUEUED_PR_LABEL */ .IH])); }; const hasRequiredLabels = (pr, requiredLabels) => requiredLabels.every(mergeQueueLabel => pr.labels.some(label => label.name === mergeQueueLabel)); -const updatePrWithMainline = (pullRequest) => __awaiter(void 0, void 0, void 0, function* () { +const updatePrWithDefaultBranch = (pullRequest) => __awaiter(void 0, void 0, void 0, function* () { var _a, _b, _c, _d; if (((_a = pullRequest.head.user) === null || _a === void 0 ? void 0 : _a.login) && ((_b = pullRequest.base.user) === null || _b === void 0 ? void 0 : _b.login) && ((_c = pullRequest.head.user) === null || _c === void 0 ? void 0 : _c.login) !== ((_d = pullRequest.base.user) === null || _d === void 0 ? void 0 : _d.login)) { try { diff --git a/dist/676.index.js.map b/dist/676.index.js.map index c5ba7010a..4c70bae78 100644 --- a/dist/676.index.js.map +++ b/dist/676.index.js.map @@ -1 +1 @@ -{"version":3,"file":"676.index.js","mappings":";;;;;;;;;;;;;;;;;;;;;;;;AAAA;;;;;;;;;;;AAWA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;;;;;AAWA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AC7CA;;;;;;;;;;;AAWA;;;;;;;;;;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAKA;AACA;AACA;;;;;AClEA;;;;;;;;;;;AAWA;;;;;;;;;;AAEA;AACA;AAOA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAGA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAKA;AACA;AAAA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;;AAEA;;;;AAIA;AACA;AAAA;AACA;AACA;AACA;AACA;;;;;;;;;;;;;;;;;;;;AChHA;;;;;;;;;;;AAWA;;;;;;;;;;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAKA;AACA;AACA;AACA;AACA;AAEA;;AACA;AACA;AAEA;AAGA;;AACA;AACA;AACA;AACA;AAIA;AAAA;AACA;AACA;AACA;;AAAA;AACA;AACA;AACA;AACA;AAKA;AAAA;AACA;AACA;AACA;AAAA;AACA;AACA;;AAAA;AACA;AACA;;;;;;;;;;;;;;;;;;;;AClEA;;;;;;;;;;;AAWA;;;;;;;;;;AAEA;AAEA;AACA;AACA;AAEA;AAAA;;AACA;AACA;AAAA;AAEA;AAEA;AACA;AACA;AAKA;AAAA;AACA;AACA;AACA;AACA;AACA;;;;;;;;;;;;;;;;;;;;;ACrCA;;;;;;;;;;;AAWA;;;;;;;;;;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAAA;;AACA;AACA;AACA;AAIA;AAAA;AAEA;AACA;AACA;AACA;AAIA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAGA;AAGA;AACA;;;;;;;;;;;;;;;;;;ACrDA;;;;;;;;;;;AAWA;AAEA;AACA;AACA;AAEA;AACA;;;;;;;;;;;AClBA;;;;;;;;;;;AAWA;AAEA;AA2CA;;;;;;;;;;;;;;;;;ACxDA;;;;;;;;;;;AAWA;;;;;;;;;;AAEA;AACA;AACA;AACA;AAQA;AACA;AACA;AAGA;AACA;AACA;AACA;AACA;AAIA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;;;;;;;;;;;;;;AChDA;;;;;;;;;;;AAWA;;;;;;;;;;AAGA;AACA;AAEA;AACA;AAQA;AACA;AACA;AACA;AACA","sources":["webpack://github-helpers/./src/constants.ts","webpack://github-helpers/./src/utils/update-merge-queue.ts","webpack://github-helpers/./src/helpers/manage-merge-queue.ts","webpack://github-helpers/./src/helpers/prepare-queued-pr-for-merge.ts","webpack://github-helpers/./src/helpers/remove-label.ts","webpack://github-helpers/./src/helpers/set-commit-status.ts","webpack://github-helpers/./src/octokit.ts","webpack://github-helpers/./src/types/generated.ts","webpack://github-helpers/./src/utils/notify-user.ts","webpack://github-helpers/./src/utils/paginate-open-pull-requests.ts"],"sourcesContent":["/*\nCopyright 2021 Expedia, Inc.\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n https://www.apache.org/licenses/LICENSE-2.0\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// These extra headers are for experimental API features on Github Enterprise. See https://docs.github.com/en/enterprise-server@3.0/rest/overview/api-previews for details.\nconst PREVIEWS = ['ant-man', 'flash', 'groot', 'inertia', 'starfox'];\nexport const GITHUB_OPTIONS = {\n headers: {\n accept: PREVIEWS.map(preview => `application/vnd.github.${preview}-preview+json`).join()\n }\n};\n\nexport const DEFAULT_EXEMPT_DESCRIPTION = 'Passed in case the check is exempt.';\nexport const DEFAULT_PIPELINE_STATUS = 'Pipeline Status';\nexport const DEFAULT_PIPELINE_DESCRIPTION = 'Pipeline clear.';\nexport const PRODUCTION_ENVIRONMENT = 'production';\nexport const LATE_REVIEW = 'Late Review';\nexport const CORE_APPROVED_PR_LABEL = 'CORE APPROVED';\nexport const PEER_APPROVED_PR_LABEL = 'PEER APPROVED';\nexport const READY_FOR_MERGE_PR_LABEL = 'READY FOR MERGE';\nexport const MERGE_QUEUE_STATUS = 'QUEUE CHECKER';\nexport const QUEUED_FOR_MERGE_PREFIX = 'QUEUED FOR MERGE';\nexport const FIRST_QUEUED_PR_LABEL = `${QUEUED_FOR_MERGE_PREFIX} #1`;\nexport const JUMP_THE_QUEUE_PR_LABEL = 'JUMP THE QUEUE';\nexport const DEFAULT_PR_TITLE_REGEX = '^(build|ci|chore|docs|feat|fix|perf|refactor|style|test|revert|Revert|BREAKING CHANGE)((.*))?: .+$';\nexport const COPYRIGHT_HEADER = `/*\nCopyright 2021 Expedia, Inc.\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n https://www.apache.org/licenses/LICENSE-2.0\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/`;\n","/*\nCopyright 2021 Expedia, Inc.\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n https://www.apache.org/licenses/LICENSE-2.0\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport { JUMP_THE_QUEUE_PR_LABEL, MERGE_QUEUE_STATUS, QUEUED_FOR_MERGE_PREFIX } from '../constants';\nimport { PullRequest, PullRequestList } from '../types/github';\nimport { context } from '@actions/github';\nimport { map } from 'bluebird';\nimport { octokit } from '../octokit';\nimport { removeLabelIfExists } from '../helpers/remove-label';\nimport { updatePrWithMainline } from '../helpers/prepare-queued-pr-for-merge';\nimport { setCommitStatus } from '../helpers/set-commit-status';\n\nexport const updateMergeQueue = (queuedPrs: PullRequestList) => {\n const sortedPrs = sortPrsByQueuePosition(queuedPrs);\n return map(sortedPrs, updateQueuePosition);\n};\n\nconst sortPrsByQueuePosition = (queuedPrs: PullRequestList): QueuedPr[] =>\n queuedPrs\n .map(pr => {\n const label = pr.labels.find(label => label.name?.startsWith(QUEUED_FOR_MERGE_PREFIX))?.name;\n const isJumpingTheQueue = Boolean(pr.labels.find(label => label.name === JUMP_THE_QUEUE_PR_LABEL));\n const queuePosition = isJumpingTheQueue ? 0 : Number(label?.split('#')?.[1]);\n return {\n number: pr.number,\n label,\n queuePosition\n };\n })\n .sort((pr1, pr2) => pr1.queuePosition - pr2.queuePosition);\n\nconst updateQueuePosition = async (pr: QueuedPr, index: number) => {\n const { number, label, queuePosition } = pr;\n const newQueuePosition = index + 1;\n if (!label || isNaN(queuePosition) || queuePosition === newQueuePosition) {\n return;\n }\n if (newQueuePosition === 1) {\n const { data: pullRequest } = await octokit.pulls.get({ pull_number: number, ...context.repo });\n await setCommitStatus({\n sha: pullRequest.head.sha,\n context: MERGE_QUEUE_STATUS,\n state: 'success',\n description: 'This PR is next to merge.'\n });\n await Promise.all([removeLabelIfExists(JUMP_THE_QUEUE_PR_LABEL, number), updatePrWithMainline(pullRequest as PullRequest)]);\n }\n\n return Promise.all([\n octokit.issues.addLabels({\n labels: [`${QUEUED_FOR_MERGE_PREFIX} #${newQueuePosition}`],\n issue_number: number,\n ...context.repo\n }),\n removeLabelIfExists(label, number)\n ]);\n};\n\ntype QueuedPr = {\n number: number;\n label?: string;\n queuePosition: number;\n};\n","/*\nCopyright 2021 Expedia, Inc.\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n https://www.apache.org/licenses/LICENSE-2.0\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport * as core from '@actions/core';\nimport {\n FIRST_QUEUED_PR_LABEL,\n JUMP_THE_QUEUE_PR_LABEL,\n MERGE_QUEUE_STATUS,\n QUEUED_FOR_MERGE_PREFIX,\n READY_FOR_MERGE_PR_LABEL\n} from '../constants';\nimport { HelperInputs } from '../types/generated';\nimport { PullRequest, PullRequestList } from '../types/github';\nimport { context } from '@actions/github';\nimport { map } from 'bluebird';\nimport { notifyUser } from '../utils/notify-user';\nimport { octokit, octokitGraphql } from '../octokit';\nimport { removeLabelIfExists } from './remove-label';\nimport { setCommitStatus } from './set-commit-status';\nimport { updateMergeQueue } from '../utils/update-merge-queue';\nimport { paginateAllOpenPullRequests } from '../utils/paginate-open-pull-requests';\n\nexport class ManageMergeQueue extends HelperInputs {\n login?: string;\n slack_webhook_url?: string;\n}\n\nexport const manageMergeQueue = async ({ login, slack_webhook_url }: ManageMergeQueue = {}) => {\n const { data: pullRequest } = await octokit.pulls.get({ pull_number: context.issue.number, ...context.repo });\n if (pullRequest.merged || !pullRequest.labels.find(label => label.name === READY_FOR_MERGE_PR_LABEL)) {\n core.info('This PR is not in the merge queue.');\n return removePrFromQueue(pullRequest as PullRequest);\n }\n const queuedPrs = await getQueuedPullRequests();\n const queuePosition = queuedPrs.length;\n if (pullRequest.labels.find(label => label.name === JUMP_THE_QUEUE_PR_LABEL)) {\n return updateMergeQueue(queuedPrs);\n }\n if (!pullRequest.labels.find(label => label.name?.startsWith(QUEUED_FOR_MERGE_PREFIX))) {\n await addPrToQueue(pullRequest as PullRequest, queuePosition);\n }\n\n const isFirstQueuePosition = queuePosition === 1 || pullRequest.labels.find(label => label.name === FIRST_QUEUED_PR_LABEL);\n await setCommitStatus({\n sha: pullRequest.head.sha,\n context: MERGE_QUEUE_STATUS,\n state: isFirstQueuePosition ? 'success' : 'pending',\n description: isFirstQueuePosition ? 'This PR is next to merge.' : 'This PR is in line to merge.'\n });\n\n if (slack_webhook_url && login && queuePosition === 1) {\n await notifyUser({\n login,\n pull_number: context.issue.number,\n slack_webhook_url\n });\n }\n};\n\nexport const removePrFromQueue = async (pullRequest: PullRequest) => {\n const queueLabel = pullRequest.labels.find(label => label.name?.startsWith(QUEUED_FOR_MERGE_PREFIX))?.name;\n if (queueLabel) {\n await map([READY_FOR_MERGE_PR_LABEL, queueLabel], async label => removeLabelIfExists(label, pullRequest.number));\n await setCommitStatus({\n sha: pullRequest.head.sha,\n context: MERGE_QUEUE_STATUS,\n state: 'pending',\n description: 'This PR is not in the merge queue.'\n });\n const queuedPrs = await getQueuedPullRequests();\n await updateMergeQueue(queuedPrs);\n }\n};\n\nconst addPrToQueue = async (pullRequest: PullRequest, queuePosition: number) =>\n Promise.all([\n octokit.issues.addLabels({\n labels: [`${QUEUED_FOR_MERGE_PREFIX} #${queuePosition}`],\n issue_number: context.issue.number,\n ...context.repo\n }),\n enableAutoMerge(pullRequest.node_id)\n ]);\n\nconst getQueuedPullRequests = async (): Promise => {\n const openPullRequests = await paginateAllOpenPullRequests();\n return openPullRequests.filter(pr => pr.labels.some(label => label.name === READY_FOR_MERGE_PR_LABEL));\n};\n\nexport const enableAutoMerge = async (pullRequestId: string, mergeMethod = 'SQUASH') => {\n try {\n return await octokitGraphql(`\n mutation {\n enablePullRequestAutoMerge(input: { pullRequestId: \"${pullRequestId}\", mergeMethod: ${mergeMethod} }) {\n clientMutationId\n }\n }\n `);\n } catch (error) {\n core.warning('Auto merge could not be enabled. Perhaps you need to enable auto-merge on your repo?');\n core.warning(error as Error);\n }\n};\n","/*\nCopyright 2021 Expedia, Inc.\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n https://www.apache.org/licenses/LICENSE-2.0\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport * as core from '@actions/core';\nimport { FIRST_QUEUED_PR_LABEL, JUMP_THE_QUEUE_PR_LABEL, READY_FOR_MERGE_PR_LABEL } from '../constants';\nimport { GithubError, PullRequest, PullRequestList, SinglePullRequest } from '../types/github';\nimport { context } from '@actions/github';\nimport { octokit } from '../octokit';\nimport { removePrFromQueue } from './manage-merge-queue';\n\nexport const prepareQueuedPrForMerge = async () => {\n const { data } = await octokit.pulls.list({\n state: 'open',\n per_page: 100,\n ...context.repo\n });\n const pullRequest = findNextPrToMerge(data);\n if (pullRequest) {\n return updatePrWithMainline(pullRequest as PullRequest);\n }\n};\n\nconst findNextPrToMerge = (pullRequests: PullRequestList) =>\n pullRequests.find(pr => hasRequiredLabels(pr, [READY_FOR_MERGE_PR_LABEL, JUMP_THE_QUEUE_PR_LABEL])) ??\n pullRequests.find(pr => hasRequiredLabels(pr, [READY_FOR_MERGE_PR_LABEL, FIRST_QUEUED_PR_LABEL]));\n\nconst hasRequiredLabels = (pr: SinglePullRequest, requiredLabels: string[]) =>\n requiredLabels.every(mergeQueueLabel => pr.labels.some(label => label.name === mergeQueueLabel));\n\nexport const updatePrWithMainline = async (pullRequest: PullRequest) => {\n if (pullRequest.head.user?.login && pullRequest.base.user?.login && pullRequest.head.user?.login !== pullRequest.base.user?.login) {\n try {\n // update fork default branch with upstream\n await octokit.repos.mergeUpstream({\n ...context.repo,\n branch: pullRequest.base.repo.default_branch\n });\n } catch (error) {\n if ((error as GithubError).status === 409) {\n core.setFailed('Attempt to update fork branch with upstream failed; conflict on default branch between fork and upstream.');\n } else core.setFailed((error as GithubError).message);\n }\n }\n try {\n await octokit.repos.merge({\n base: pullRequest.head.ref,\n head: 'HEAD',\n ...context.repo\n });\n } catch (error) {\n const noEvictUponConflict = core.getBooleanInput('no_evict_upon_conflict');\n if ((error as GithubError).status === 409) {\n if (!noEvictUponConflict) await removePrFromQueue(pullRequest);\n core.setFailed('The first PR in the queue has a merge conflict.');\n } else core.setFailed((error as GithubError).message);\n }\n};\n","/*\nCopyright 2021 Expedia, Inc.\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n https://www.apache.org/licenses/LICENSE-2.0\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport * as core from '@actions/core';\nimport { GithubError } from '../types/github';\nimport { HelperInputs } from '../types/generated';\nimport { context } from '@actions/github';\nimport { octokit } from '../octokit';\n\nexport class RemoveLabel extends HelperInputs {\n label = '';\n}\n\nexport const removeLabel = async ({ label }: RemoveLabel) => removeLabelIfExists(label, context.issue.number);\n\nexport const removeLabelIfExists = async (labelName: string, issue_number: number) => {\n try {\n await octokit.issues.removeLabel({\n name: labelName,\n issue_number,\n ...context.repo\n });\n } catch (error) {\n if ((error as GithubError).status === 404) {\n core.info('Label is not present on PR.');\n }\n }\n};\n","/*\nCopyright 2021 Expedia, Inc.\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n https://www.apache.org/licenses/LICENSE-2.0\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport * as core from '@actions/core';\nimport { PipelineState } from '../types/github';\nimport { HelperInputs } from '../types/generated';\nimport { context as githubContext } from '@actions/github';\nimport { map } from 'bluebird';\nimport { octokit } from '../octokit';\n\nexport class SetCommitStatus extends HelperInputs {\n sha = '';\n context = '';\n state = '';\n description?: string;\n target_url?: string;\n skip_if_already_set?: string;\n}\n\nexport const setCommitStatus = async ({ sha, context, state, description, target_url, skip_if_already_set }: SetCommitStatus) => {\n await map(context.split('\\n').filter(Boolean), async context => {\n if (skip_if_already_set === 'true') {\n const check_runs = await octokit.checks.listForRef({\n ...githubContext.repo,\n ref: sha\n });\n const run = check_runs.data.check_runs.find(({ name }) => name === context);\n const runCompletedAndIsValid = run?.status === 'completed' && (run?.conclusion === 'failure' || run?.conclusion === 'success');\n if (runCompletedAndIsValid) {\n core.info(`${context} already completed with a ${run.conclusion} conclusion.`);\n return;\n }\n }\n\n octokit.repos.createCommitStatus({\n sha,\n context,\n state: state as PipelineState,\n description,\n target_url,\n ...githubContext.repo\n });\n });\n};\n","/*\nCopyright 2021 Expedia, Inc.\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n https://www.apache.org/licenses/LICENSE-2.0\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport * as core from '@actions/core';\nimport * as fetch from '@adobe/node-fetch-retry';\nimport { getOctokit } from '@actions/github';\n\nconst githubToken = core.getInput('github_token', { required: true });\nexport const { rest: octokit, graphql: octokitGraphql } = getOctokit(githubToken, { request: { fetch } });\n","/*\nCopyright 2021 Expedia, Inc.\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n https://www.apache.org/licenses/LICENSE-2.0\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nexport class HelperInputs {\n helper?: string;\n github_token?: string;\n body?: string;\n project_name?: string;\n project_destination_column_name?: string;\n note?: string;\n project_origin_column_name?: string;\n sha?: string;\n context?: string;\n state?: string;\n description?: string;\n target_url?: string;\n environment?: string;\n environment_url?: string;\n label?: string;\n labels?: string;\n paths?: string;\n ignore_globs?: string;\n extensions?: string;\n override_filter_paths?: string;\n batches?: string;\n pattern?: string;\n teams?: string;\n login?: string;\n paths_no_filter?: string;\n slack_webhook_url?: string;\n number_of_assignees?: string;\n number_of_reviewers?: string;\n globs?: string;\n override_filter_globs?: string;\n title?: string;\n seconds?: string;\n pull_number?: string;\n base?: string;\n head?: string;\n days?: string;\n no_evict_upon_conflict?: string;\n skip_if_already_set?: string;\n delimiter?: string;\n team?: string;\n ignore_deleted?: string;\n return_full_payload?: string;\n}\n","/*\nCopyright 2021 Expedia, Inc.\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n https://www.apache.org/licenses/LICENSE-2.0\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport * as core from '@actions/core';\nimport axios from 'axios';\nimport { context } from '@actions/github';\nimport { octokit } from '../octokit';\n\ninterface NotifyUser {\n login: string;\n pull_number: number;\n slack_webhook_url: string;\n}\n\nexport const notifyUser = async ({ login, pull_number, slack_webhook_url }: NotifyUser) => {\n core.info(`Notifying user ${login}...`);\n const {\n data: { email }\n } = await octokit.users.getByUsername({ username: login });\n if (!email) {\n core.info(`No github email found for user ${login}. Ensure you have set your email to be publicly visible on your Github profile.`);\n return;\n }\n const {\n data: { title, html_url }\n } = await octokit.pulls.get({ pull_number, ...context.repo });\n\n try {\n await axios.post(slack_webhook_url, {\n assignee: email,\n title,\n html_url,\n repo: context.repo.repo\n });\n } catch (error) {\n core.warning('User notification failed');\n core.warning(error as Error);\n }\n};\n","/*\nCopyright 2022 Expedia, Inc.\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n https://www.apache.org/licenses/LICENSE-2.0\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport { PullRequestList } from '../types/github';\nimport { octokit } from '../octokit';\nimport { context } from '@actions/github';\n\nexport const paginateAllOpenPullRequests = async (page = 1): Promise => {\n const response = await octokit.pulls.list({\n state: 'open',\n sort: 'updated',\n direction: 'desc',\n per_page: 100,\n page,\n ...context.repo\n });\n if (!response.data.length) {\n return [];\n }\n return response.data.concat(await paginateAllOpenPullRequests(page + 1));\n};\n"],"names":[],"sourceRoot":""} \ No newline at end of file +{"version":3,"file":"676.index.js","mappings":";;;;;;;;;;;;;;;;;;;;;;;;AAAA;;;;;;;;;;;AAWA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;;;;;AAWA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AC7CA;;;;;;;;;;;AAWA;;;;;;;;;;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAKA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;ACzEA;;;;;;;;;;;AAWA;;;;;;;;;;AAEA;AACA;AAOA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAGA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAKA;AACA;AAAA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;;AAEA;;;;AAIA;AACA;AAAA;AACA;AACA;AACA;AACA;;;;;;;;;;;;;;;;;;;;AChHA;;;;;;;;;;;AAWA;;;;;;;;;;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAKA;AACA;AACA;AACA;AACA;AAEA;;AACA;AACA;AAEA;AAGA;;AACA;AACA;AACA;AACA;AAIA;AAAA;AACA;AACA;AACA;;AAAA;AACA;AACA;AACA;AACA;AAKA;AAAA;AACA;AACA;AACA;AAAA;AACA;AACA;;AAAA;AACA;AACA;;;;;;;;;;;;;;;;;;;;AClEA;;;;;;;;;;;AAWA;;;;;;;;;;AAEA;AAEA;AACA;AACA;AAEA;AAAA;;AACA;AACA;AAAA;AAEA;AAEA;AACA;AACA;AAKA;AAAA;AACA;AACA;AACA;AACA;AACA;;;;;;;;;;;;;;;;;;;;;ACrCA;;;;;;;;;;;AAWA;;;;;;;;;;AAEA;AAEA;AACA;AACA;AACA;AAEA;AAAA;;AACA;AACA;AACA;AAIA;AAAA;AAEA;AACA;AACA;AACA;AAIA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAGA;AAGA;AACA;;;;;;;;;;;;;;;;;;ACrDA;;;;;;;;;;;AAWA;AAEA;AACA;AACA;AAEA;AACA;;;;;;;;;;;AClBA;;;;;;;;;;;AAWA;AAEA;AA2CA;;;;;;;;;;;;;;;;;ACxDA;;;;;;;;;;;AAWA;;;;;;;;;;AAEA;AACA;AACA;AACA;AAQA;AACA;AACA;AAGA;AACA;AACA;AACA;AACA;AAIA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;;;;;;;;;;;;;;AChDA;;;;;;;;;;;AAWA;;;;;;;;;;AAGA;AACA;AAEA;AACA;AAQA;AACA;AACA;AACA;AACA","sources":["webpack://github-helpers/./src/constants.ts","webpack://github-helpers/./src/utils/update-merge-queue.ts","webpack://github-helpers/./src/helpers/manage-merge-queue.ts","webpack://github-helpers/./src/helpers/prepare-queued-pr-for-merge.ts","webpack://github-helpers/./src/helpers/remove-label.ts","webpack://github-helpers/./src/helpers/set-commit-status.ts","webpack://github-helpers/./src/octokit.ts","webpack://github-helpers/./src/types/generated.ts","webpack://github-helpers/./src/utils/notify-user.ts","webpack://github-helpers/./src/utils/paginate-open-pull-requests.ts"],"sourcesContent":["/*\nCopyright 2021 Expedia, Inc.\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n https://www.apache.org/licenses/LICENSE-2.0\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\n// These extra headers are for experimental API features on Github Enterprise. See https://docs.github.com/en/enterprise-server@3.0/rest/overview/api-previews for details.\nconst PREVIEWS = ['ant-man', 'flash', 'groot', 'inertia', 'starfox'];\nexport const GITHUB_OPTIONS = {\n headers: {\n accept: PREVIEWS.map(preview => `application/vnd.github.${preview}-preview+json`).join()\n }\n};\n\nexport const DEFAULT_EXEMPT_DESCRIPTION = 'Passed in case the check is exempt.';\nexport const DEFAULT_PIPELINE_STATUS = 'Pipeline Status';\nexport const DEFAULT_PIPELINE_DESCRIPTION = 'Pipeline clear.';\nexport const PRODUCTION_ENVIRONMENT = 'production';\nexport const LATE_REVIEW = 'Late Review';\nexport const CORE_APPROVED_PR_LABEL = 'CORE APPROVED';\nexport const PEER_APPROVED_PR_LABEL = 'PEER APPROVED';\nexport const READY_FOR_MERGE_PR_LABEL = 'READY FOR MERGE';\nexport const MERGE_QUEUE_STATUS = 'QUEUE CHECKER';\nexport const QUEUED_FOR_MERGE_PREFIX = 'QUEUED FOR MERGE';\nexport const FIRST_QUEUED_PR_LABEL = `${QUEUED_FOR_MERGE_PREFIX} #1`;\nexport const JUMP_THE_QUEUE_PR_LABEL = 'JUMP THE QUEUE';\nexport const DEFAULT_PR_TITLE_REGEX = '^(build|ci|chore|docs|feat|fix|perf|refactor|style|test|revert|Revert|BREAKING CHANGE)((.*))?: .+$';\nexport const COPYRIGHT_HEADER = `/*\nCopyright 2021 Expedia, Inc.\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n https://www.apache.org/licenses/LICENSE-2.0\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/`;\n","/*\nCopyright 2021 Expedia, Inc.\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n https://www.apache.org/licenses/LICENSE-2.0\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport { JUMP_THE_QUEUE_PR_LABEL, MERGE_QUEUE_STATUS, QUEUED_FOR_MERGE_PREFIX } from '../constants';\nimport { PullRequest, PullRequestList } from '../types/github';\nimport { context } from '@actions/github';\nimport { map } from 'bluebird';\nimport { octokit } from '../octokit';\nimport { removeLabelIfExists } from '../helpers/remove-label';\nimport { updatePrWithDefaultBranch } from '../helpers/prepare-queued-pr-for-merge';\nimport { setCommitStatus } from '../helpers/set-commit-status';\n\nexport const updateMergeQueue = (queuedPrs: PullRequestList) => {\n const sortedPrs = sortPrsByQueuePosition(queuedPrs);\n return map(sortedPrs, updateQueuePosition);\n};\n\nconst sortPrsByQueuePosition = (queuedPrs: PullRequestList) =>\n queuedPrs\n .map(pr => {\n const label = pr.labels.find(label => label.name?.startsWith(QUEUED_FOR_MERGE_PREFIX))?.name;\n const isJumpingTheQueue = Boolean(pr.labels.find(label => label.name === JUMP_THE_QUEUE_PR_LABEL));\n const queuePosition = isJumpingTheQueue ? 0 : Number(label?.split('#')?.[1]);\n return {\n number: pr.number,\n label,\n queuePosition,\n sha: pr.head.sha\n };\n })\n .sort((pr1, pr2) => pr1.queuePosition - pr2.queuePosition);\n\nconst updateQueuePosition = async (pr: ReturnType[number], index: number) => {\n const { number, label, queuePosition, sha } = pr;\n const newQueuePosition = index + 1;\n if (!label || isNaN(queuePosition) || queuePosition === newQueuePosition) {\n return;\n }\n if (newQueuePosition === 1) {\n const { data: firstPrInQueue } = await octokit.pulls.get({ pull_number: number, ...context.repo });\n await setCommitStatus({\n sha: firstPrInQueue.head.sha,\n context: MERGE_QUEUE_STATUS,\n state: 'success',\n description: 'This PR is next to merge.'\n });\n await Promise.all([removeLabelIfExists(JUMP_THE_QUEUE_PR_LABEL, number), updatePrWithDefaultBranch(firstPrInQueue as PullRequest)]);\n }\n\n return Promise.all([\n octokit.issues.addLabels({\n labels: [`${QUEUED_FOR_MERGE_PREFIX} #${newQueuePosition}`],\n issue_number: number,\n ...context.repo\n }),\n removeLabelIfExists(label, number),\n setCommitStatus({\n sha,\n context: MERGE_QUEUE_STATUS,\n state: 'pending',\n description: 'This PR is in line to merge.'\n })\n ]);\n};\n","/*\nCopyright 2021 Expedia, Inc.\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n https://www.apache.org/licenses/LICENSE-2.0\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport * as core from '@actions/core';\nimport {\n FIRST_QUEUED_PR_LABEL,\n JUMP_THE_QUEUE_PR_LABEL,\n MERGE_QUEUE_STATUS,\n QUEUED_FOR_MERGE_PREFIX,\n READY_FOR_MERGE_PR_LABEL\n} from '../constants';\nimport { HelperInputs } from '../types/generated';\nimport { PullRequest, PullRequestList } from '../types/github';\nimport { context } from '@actions/github';\nimport { map } from 'bluebird';\nimport { notifyUser } from '../utils/notify-user';\nimport { octokit, octokitGraphql } from '../octokit';\nimport { removeLabelIfExists } from './remove-label';\nimport { setCommitStatus } from './set-commit-status';\nimport { updateMergeQueue } from '../utils/update-merge-queue';\nimport { paginateAllOpenPullRequests } from '../utils/paginate-open-pull-requests';\n\nexport class ManageMergeQueue extends HelperInputs {\n login?: string;\n slack_webhook_url?: string;\n}\n\nexport const manageMergeQueue = async ({ login, slack_webhook_url }: ManageMergeQueue = {}) => {\n const { data: pullRequest } = await octokit.pulls.get({ pull_number: context.issue.number, ...context.repo });\n if (pullRequest.merged || !pullRequest.labels.find(label => label.name === READY_FOR_MERGE_PR_LABEL)) {\n core.info('This PR is not in the merge queue.');\n return removePrFromQueue(pullRequest as PullRequest);\n }\n const queuedPrs = await getQueuedPullRequests();\n const queuePosition = queuedPrs.length;\n if (pullRequest.labels.find(label => label.name === JUMP_THE_QUEUE_PR_LABEL)) {\n return updateMergeQueue(queuedPrs);\n }\n if (!pullRequest.labels.find(label => label.name?.startsWith(QUEUED_FOR_MERGE_PREFIX))) {\n await addPrToQueue(pullRequest as PullRequest, queuePosition);\n }\n\n const isFirstQueuePosition = queuePosition === 1 || pullRequest.labels.find(label => label.name === FIRST_QUEUED_PR_LABEL);\n await setCommitStatus({\n sha: pullRequest.head.sha,\n context: MERGE_QUEUE_STATUS,\n state: isFirstQueuePosition ? 'success' : 'pending',\n description: isFirstQueuePosition ? 'This PR is next to merge.' : 'This PR is in line to merge.'\n });\n\n if (slack_webhook_url && login && queuePosition === 1) {\n await notifyUser({\n login,\n pull_number: context.issue.number,\n slack_webhook_url\n });\n }\n};\n\nexport const removePrFromQueue = async (pullRequest: PullRequest) => {\n const queueLabel = pullRequest.labels.find(label => label.name?.startsWith(QUEUED_FOR_MERGE_PREFIX))?.name;\n if (queueLabel) {\n await map([READY_FOR_MERGE_PR_LABEL, queueLabel], async label => removeLabelIfExists(label, pullRequest.number));\n await setCommitStatus({\n sha: pullRequest.head.sha,\n context: MERGE_QUEUE_STATUS,\n state: 'pending',\n description: 'This PR is not in the merge queue.'\n });\n const queuedPrs = await getQueuedPullRequests();\n await updateMergeQueue(queuedPrs);\n }\n};\n\nconst addPrToQueue = async (pullRequest: PullRequest, queuePosition: number) =>\n Promise.all([\n octokit.issues.addLabels({\n labels: [`${QUEUED_FOR_MERGE_PREFIX} #${queuePosition}`],\n issue_number: context.issue.number,\n ...context.repo\n }),\n enableAutoMerge(pullRequest.node_id)\n ]);\n\nconst getQueuedPullRequests = async (): Promise => {\n const openPullRequests = await paginateAllOpenPullRequests();\n return openPullRequests.filter(pr => pr.labels.some(label => label.name === READY_FOR_MERGE_PR_LABEL));\n};\n\nexport const enableAutoMerge = async (pullRequestId: string, mergeMethod = 'SQUASH') => {\n try {\n return await octokitGraphql(`\n mutation {\n enablePullRequestAutoMerge(input: { pullRequestId: \"${pullRequestId}\", mergeMethod: ${mergeMethod} }) {\n clientMutationId\n }\n }\n `);\n } catch (error) {\n core.warning('Auto merge could not be enabled. Perhaps you need to enable auto-merge on your repo?');\n core.warning(error as Error);\n }\n};\n","/*\nCopyright 2021 Expedia, Inc.\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n https://www.apache.org/licenses/LICENSE-2.0\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport * as core from '@actions/core';\nimport { FIRST_QUEUED_PR_LABEL, JUMP_THE_QUEUE_PR_LABEL, READY_FOR_MERGE_PR_LABEL } from '../constants';\nimport { GithubError, PullRequest, PullRequestList, SinglePullRequest } from '../types/github';\nimport { context } from '@actions/github';\nimport { octokit } from '../octokit';\nimport { removePrFromQueue } from './manage-merge-queue';\n\nexport const prepareQueuedPrForMerge = async () => {\n const { data } = await octokit.pulls.list({\n state: 'open',\n per_page: 100,\n ...context.repo\n });\n const pullRequest = findNextPrToMerge(data);\n if (pullRequest) {\n return updatePrWithDefaultBranch(pullRequest as PullRequest);\n }\n};\n\nconst findNextPrToMerge = (pullRequests: PullRequestList) =>\n pullRequests.find(pr => hasRequiredLabels(pr, [READY_FOR_MERGE_PR_LABEL, JUMP_THE_QUEUE_PR_LABEL])) ??\n pullRequests.find(pr => hasRequiredLabels(pr, [READY_FOR_MERGE_PR_LABEL, FIRST_QUEUED_PR_LABEL]));\n\nconst hasRequiredLabels = (pr: SinglePullRequest, requiredLabels: string[]) =>\n requiredLabels.every(mergeQueueLabel => pr.labels.some(label => label.name === mergeQueueLabel));\n\nexport const updatePrWithDefaultBranch = async (pullRequest: PullRequest) => {\n if (pullRequest.head.user?.login && pullRequest.base.user?.login && pullRequest.head.user?.login !== pullRequest.base.user?.login) {\n try {\n // update fork default branch with upstream\n await octokit.repos.mergeUpstream({\n ...context.repo,\n branch: pullRequest.base.repo.default_branch\n });\n } catch (error) {\n if ((error as GithubError).status === 409) {\n core.setFailed('Attempt to update fork branch with upstream failed; conflict on default branch between fork and upstream.');\n } else core.setFailed((error as GithubError).message);\n }\n }\n try {\n await octokit.repos.merge({\n base: pullRequest.head.ref,\n head: 'HEAD',\n ...context.repo\n });\n } catch (error) {\n const noEvictUponConflict = core.getBooleanInput('no_evict_upon_conflict');\n if ((error as GithubError).status === 409) {\n if (!noEvictUponConflict) await removePrFromQueue(pullRequest);\n core.setFailed('The first PR in the queue has a merge conflict.');\n } else core.setFailed((error as GithubError).message);\n }\n};\n","/*\nCopyright 2021 Expedia, Inc.\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n https://www.apache.org/licenses/LICENSE-2.0\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport * as core from '@actions/core';\nimport { GithubError } from '../types/github';\nimport { HelperInputs } from '../types/generated';\nimport { context } from '@actions/github';\nimport { octokit } from '../octokit';\n\nexport class RemoveLabel extends HelperInputs {\n label = '';\n}\n\nexport const removeLabel = async ({ label }: RemoveLabel) => removeLabelIfExists(label, context.issue.number);\n\nexport const removeLabelIfExists = async (labelName: string, issue_number: number) => {\n try {\n await octokit.issues.removeLabel({\n name: labelName,\n issue_number,\n ...context.repo\n });\n } catch (error) {\n if ((error as GithubError).status === 404) {\n core.info('Label is not present on PR.');\n }\n }\n};\n","/*\nCopyright 2021 Expedia, Inc.\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n https://www.apache.org/licenses/LICENSE-2.0\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport * as core from '@actions/core';\nimport { PipelineState } from '../types/github';\nimport { HelperInputs } from '../types/generated';\nimport { context as githubContext } from '@actions/github';\nimport { map } from 'bluebird';\nimport { octokit } from '../octokit';\n\nexport class SetCommitStatus extends HelperInputs {\n sha = '';\n context = '';\n state = '';\n description?: string;\n target_url?: string;\n skip_if_already_set?: string;\n}\n\nexport const setCommitStatus = async ({ sha, context, state, description, target_url, skip_if_already_set }: SetCommitStatus) => {\n await map(context.split('\\n').filter(Boolean), async context => {\n if (skip_if_already_set === 'true') {\n const check_runs = await octokit.checks.listForRef({\n ...githubContext.repo,\n ref: sha\n });\n const run = check_runs.data.check_runs.find(({ name }) => name === context);\n const runCompletedAndIsValid = run?.status === 'completed' && (run?.conclusion === 'failure' || run?.conclusion === 'success');\n if (runCompletedAndIsValid) {\n core.info(`${context} already completed with a ${run.conclusion} conclusion.`);\n return;\n }\n }\n\n octokit.repos.createCommitStatus({\n sha,\n context,\n state: state as PipelineState,\n description,\n target_url,\n ...githubContext.repo\n });\n });\n};\n","/*\nCopyright 2021 Expedia, Inc.\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n https://www.apache.org/licenses/LICENSE-2.0\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport * as core from '@actions/core';\nimport * as fetch from '@adobe/node-fetch-retry';\nimport { getOctokit } from '@actions/github';\n\nconst githubToken = core.getInput('github_token', { required: true });\nexport const { rest: octokit, graphql: octokitGraphql } = getOctokit(githubToken, { request: { fetch } });\n","/*\nCopyright 2021 Expedia, Inc.\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n https://www.apache.org/licenses/LICENSE-2.0\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nexport class HelperInputs {\n helper?: string;\n github_token?: string;\n body?: string;\n project_name?: string;\n project_destination_column_name?: string;\n note?: string;\n project_origin_column_name?: string;\n sha?: string;\n context?: string;\n state?: string;\n description?: string;\n target_url?: string;\n environment?: string;\n environment_url?: string;\n label?: string;\n labels?: string;\n paths?: string;\n ignore_globs?: string;\n extensions?: string;\n override_filter_paths?: string;\n batches?: string;\n pattern?: string;\n teams?: string;\n login?: string;\n paths_no_filter?: string;\n slack_webhook_url?: string;\n number_of_assignees?: string;\n number_of_reviewers?: string;\n globs?: string;\n override_filter_globs?: string;\n title?: string;\n seconds?: string;\n pull_number?: string;\n base?: string;\n head?: string;\n days?: string;\n no_evict_upon_conflict?: string;\n skip_if_already_set?: string;\n delimiter?: string;\n team?: string;\n ignore_deleted?: string;\n return_full_payload?: string;\n}\n","/*\nCopyright 2021 Expedia, Inc.\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n https://www.apache.org/licenses/LICENSE-2.0\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport * as core from '@actions/core';\nimport axios from 'axios';\nimport { context } from '@actions/github';\nimport { octokit } from '../octokit';\n\ninterface NotifyUser {\n login: string;\n pull_number: number;\n slack_webhook_url: string;\n}\n\nexport const notifyUser = async ({ login, pull_number, slack_webhook_url }: NotifyUser) => {\n core.info(`Notifying user ${login}...`);\n const {\n data: { email }\n } = await octokit.users.getByUsername({ username: login });\n if (!email) {\n core.info(`No github email found for user ${login}. Ensure you have set your email to be publicly visible on your Github profile.`);\n return;\n }\n const {\n data: { title, html_url }\n } = await octokit.pulls.get({ pull_number, ...context.repo });\n\n try {\n await axios.post(slack_webhook_url, {\n assignee: email,\n title,\n html_url,\n repo: context.repo.repo\n });\n } catch (error) {\n core.warning('User notification failed');\n core.warning(error as Error);\n }\n};\n","/*\nCopyright 2022 Expedia, Inc.\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n https://www.apache.org/licenses/LICENSE-2.0\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport { PullRequestList } from '../types/github';\nimport { octokit } from '../octokit';\nimport { context } from '@actions/github';\n\nexport const paginateAllOpenPullRequests = async (page = 1): Promise => {\n const response = await octokit.pulls.list({\n state: 'open',\n sort: 'updated',\n direction: 'desc',\n per_page: 100,\n page,\n ...context.repo\n });\n if (!response.data.length) {\n return [];\n }\n return response.data.concat(await paginateAllOpenPullRequests(page + 1));\n};\n"],"names":[],"sourceRoot":""} \ No newline at end of file diff --git a/src/helpers/prepare-queued-pr-for-merge.ts b/src/helpers/prepare-queued-pr-for-merge.ts index 47fa473e9..f4b9661d5 100644 --- a/src/helpers/prepare-queued-pr-for-merge.ts +++ b/src/helpers/prepare-queued-pr-for-merge.ts @@ -26,7 +26,7 @@ export const prepareQueuedPrForMerge = async () => { }); const pullRequest = findNextPrToMerge(data); if (pullRequest) { - return updatePrWithMainline(pullRequest as PullRequest); + return updatePrWithDefaultBranch(pullRequest as PullRequest); } }; @@ -37,7 +37,7 @@ const findNextPrToMerge = (pullRequests: PullRequestList) => const hasRequiredLabels = (pr: SinglePullRequest, requiredLabels: string[]) => requiredLabels.every(mergeQueueLabel => pr.labels.some(label => label.name === mergeQueueLabel)); -export const updatePrWithMainline = async (pullRequest: PullRequest) => { +export const updatePrWithDefaultBranch = async (pullRequest: PullRequest) => { if (pullRequest.head.user?.login && pullRequest.base.user?.login && pullRequest.head.user?.login !== pullRequest.base.user?.login) { try { // update fork default branch with upstream diff --git a/src/utils/update-merge-queue.ts b/src/utils/update-merge-queue.ts index 7c6b5fb2c..8e665c2f6 100644 --- a/src/utils/update-merge-queue.ts +++ b/src/utils/update-merge-queue.ts @@ -17,7 +17,7 @@ import { context } from '@actions/github'; import { map } from 'bluebird'; import { octokit } from '../octokit'; import { removeLabelIfExists } from '../helpers/remove-label'; -import { updatePrWithMainline } from '../helpers/prepare-queued-pr-for-merge'; +import { updatePrWithDefaultBranch } from '../helpers/prepare-queued-pr-for-merge'; import { setCommitStatus } from '../helpers/set-commit-status'; export const updateMergeQueue = (queuedPrs: PullRequestList) => { @@ -25,7 +25,7 @@ export const updateMergeQueue = (queuedPrs: PullRequestList) => { return map(sortedPrs, updateQueuePosition); }; -const sortPrsByQueuePosition = (queuedPrs: PullRequestList): QueuedPr[] => +const sortPrsByQueuePosition = (queuedPrs: PullRequestList) => queuedPrs .map(pr => { const label = pr.labels.find(label => label.name?.startsWith(QUEUED_FOR_MERGE_PREFIX))?.name; @@ -34,26 +34,27 @@ const sortPrsByQueuePosition = (queuedPrs: PullRequestList): QueuedPr[] => return { number: pr.number, label, - queuePosition + queuePosition, + sha: pr.head.sha }; }) .sort((pr1, pr2) => pr1.queuePosition - pr2.queuePosition); -const updateQueuePosition = async (pr: QueuedPr, index: number) => { - const { number, label, queuePosition } = pr; +const updateQueuePosition = async (pr: ReturnType[number], index: number) => { + const { number, label, queuePosition, sha } = pr; const newQueuePosition = index + 1; if (!label || isNaN(queuePosition) || queuePosition === newQueuePosition) { return; } if (newQueuePosition === 1) { - const { data: pullRequest } = await octokit.pulls.get({ pull_number: number, ...context.repo }); + const { data: firstPrInQueue } = await octokit.pulls.get({ pull_number: number, ...context.repo }); await setCommitStatus({ - sha: pullRequest.head.sha, + sha: firstPrInQueue.head.sha, context: MERGE_QUEUE_STATUS, state: 'success', description: 'This PR is next to merge.' }); - await Promise.all([removeLabelIfExists(JUMP_THE_QUEUE_PR_LABEL, number), updatePrWithMainline(pullRequest as PullRequest)]); + await Promise.all([removeLabelIfExists(JUMP_THE_QUEUE_PR_LABEL, number), updatePrWithDefaultBranch(firstPrInQueue as PullRequest)]); } return Promise.all([ @@ -62,12 +63,12 @@ const updateQueuePosition = async (pr: QueuedPr, index: number) => { issue_number: number, ...context.repo }), - removeLabelIfExists(label, number) + removeLabelIfExists(label, number), + setCommitStatus({ + sha, + context: MERGE_QUEUE_STATUS, + state: 'pending', + description: 'This PR is in line to merge.' + }) ]); }; - -type QueuedPr = { - number: number; - label?: string; - queuePosition: number; -}; diff --git a/test/utils/update-merge-queue.test.ts b/test/utils/update-merge-queue.test.ts index d7807a915..5a874c110 100644 --- a/test/utils/update-merge-queue.test.ts +++ b/test/utils/update-merge-queue.test.ts @@ -18,7 +18,7 @@ import { context } from '@actions/github'; import { octokit } from '../../src/octokit'; import { removeLabelIfExists } from '../../src/helpers/remove-label'; import { updateMergeQueue } from '../../src/utils/update-merge-queue'; -import { updatePrWithMainline } from '../../src/helpers/prepare-queued-pr-for-merge'; +import { updatePrWithDefaultBranch } from '../../src/helpers/prepare-queued-pr-for-merge'; import { setCommitStatus } from '../../src/helpers/set-commit-status'; jest.mock('../../src/helpers/remove-label'); @@ -48,10 +48,12 @@ describe('updateMergeQueue', () => { describe('pr merge case', () => { const queuedPrs = [ { + head: { sha: 'sha123' }, number: 123, labels: [{ name: 'QUEUED FOR MERGE #2' }] }, { + head: { sha: 'sha456' }, number: 456, labels: [{ name: 'QUEUED FOR MERGE #3' }] } @@ -79,19 +81,19 @@ describe('updateMergeQueue', () => { }); it('should call updatePrWithDefaultBranch with correct params', () => { - expect(updatePrWithMainline).toHaveBeenCalledWith({ head: { sha: 'sha123' } }); + expect(updatePrWithDefaultBranch).toHaveBeenCalledWith({ head: { sha: 'sha123' } }); }); }); describe('middle pr taken out of queue case', () => { const queuedPrs = [ { - head: { sha: 'sha1' }, + head: { sha: 'sha123' }, number: 123, labels: [{ name: 'QUEUED FOR MERGE #1' }] }, { - head: { sha: 'sha3' }, + head: { sha: 'sha456' }, number: 456, labels: [{ name: 'QUEUED FOR MERGE #3' }] } @@ -118,20 +120,20 @@ describe('updateMergeQueue', () => { expect(removeLabelIfExists).toHaveBeenCalledWith('QUEUED FOR MERGE #3', 456); }); - it('should not update pr with mainline', () => { - expect(updatePrWithMainline).not.toHaveBeenCalled(); + it('should not update pr with default branch', () => { + expect(updatePrWithDefaultBranch).not.toHaveBeenCalled(); }); }); describe('first pr taken out of queue case', () => { const queuedPrs = [ { - head: { sha: 'sha2' }, + head: { sha: 'sha123' }, number: 123, labels: [{ name: 'QUEUED FOR MERGE #2' }] }, { - head: { sha: 'sha3' }, + head: { sha: 'sha456' }, number: 456, labels: [{ name: 'QUEUED FOR MERGE #3' }] } @@ -167,18 +169,20 @@ describe('updateMergeQueue', () => { }); }); - it('should update pr with mainline', () => { - expect(updatePrWithMainline).toHaveBeenCalled(); + it('should update pr with default branch', () => { + expect(updatePrWithDefaultBranch).toHaveBeenCalled(); }); }); describe('pr jumping the queue case', () => { const queuedPrs = [ { + head: { sha: 'sha123' }, number: 123, labels: [{ name: JUMP_THE_QUEUE_PR_LABEL }, { name: 'QUEUED FOR MERGE #5' }] }, { + head: { sha: 'sha456' }, number: 456, labels: [{ name: 'QUEUED FOR MERGE #1' }] } @@ -207,8 +211,23 @@ describe('updateMergeQueue', () => { expect(removeLabelIfExists).toHaveBeenCalledWith('QUEUED FOR MERGE #1', 456); }); - it('should not call updatePrWithDefaultBranch', () => { - expect(updatePrWithMainline).toHaveBeenCalledWith({ head: { sha: 'sha123' } }); + it('should set the correct commit statuses', () => { + expect(setCommitStatus).toHaveBeenCalledWith({ + sha: 'sha123', + context: MERGE_QUEUE_STATUS, + state: 'success', + description: 'This PR is next to merge.' + }); + expect(setCommitStatus).toHaveBeenCalledWith({ + sha: 'sha456', + context: MERGE_QUEUE_STATUS, + state: 'pending', + description: 'This PR is in line to merge.' + }); + }); + + it('should call updatePrWithDefaultBranch', () => { + expect(updatePrWithDefaultBranch).toHaveBeenCalledWith({ head: { sha: 'sha123' } }); }); }); });