From f01f07e777d521672098467a1c13b238f5f6d7c1 Mon Sep 17 00:00:00 2001 From: danadajian Date: Mon, 3 Jan 2022 12:44:04 -0500 Subject: [PATCH] feat: add support for jump the queue label (#50) --- dist/473.index.js | 97 +++++++++++-------- dist/473.index.js.map | 2 +- package.json | 3 +- src/helpers/manage-merge-queue.ts | 47 +++++---- src/utils/update-merge-queue.ts | 61 +++++++----- test/helpers/add-pr-approval-label.test.ts | 4 - test/helpers/assign-pr-reviewers.test.ts | 4 - test/helpers/check-pr-title.test.ts | 4 - test/helpers/filter-paths.test.ts | 4 - test/helpers/manage-merge-queue.test.ts | 33 ++++++- .../prepare-queued-pr-for-merge.test.ts | 4 - test/helpers/set-deployment-status.test.ts | 4 - test/utils/update-merge-queue.test.ts | 44 ++++++++- 13 files changed, 196 insertions(+), 115 deletions(-) diff --git a/dist/473.index.js b/dist/473.index.js index f692668df..16a7cfc3f 100644 --- a/dist/473.index.js +++ b/dist/473.index.js @@ -112,39 +112,45 @@ var __awaiter = (undefined && undefined.__awaiter) || function (thisArg, _argume const updateMergeQueue = (queuedPrs) => { - const prsSortedByQueuePosition = queuedPrs - .map(pr => { - var _a, _b; - const label = (_a = pr.labels.find(label => { var _a; return (_a = label.name) === null || _a === void 0 ? void 0 : _a.startsWith(constants/* QUEUED_FOR_MERGE_PREFIX */.Ee); })) === null || _a === void 0 ? void 0 : _a.name; - const queuePosition = Number((_b = label === null || label === void 0 ? void 0 : label.split('#')) === null || _b === void 0 ? void 0 : _b[1]); - return { - pull_number: pr.number, - label, - queuePosition - }; - }) - .sort((pr1, pr2) => pr1.queuePosition - pr2.queuePosition); - return (0,bluebird.map)(prsSortedByQueuePosition, (pr, index) => __awaiter(void 0, void 0, void 0, function* () { - const { pull_number, label, queuePosition } = pr; - const newQueuePosition = index + 1; - if (!label || queuePosition === newQueuePosition) { - return; - } - if (newQueuePosition === 1) { - const { data: pullRequest } = yield octokit/* octokit.pulls.get */.K.pulls.get(Object.assign({ pull_number }, github.context.repo)); - yield (0,set_commit_status.setCommitStatus)({ + const sortedPrs = sortPrsByQueuePosition(queuedPrs); + return (0,bluebird.map)(sortedPrs, updateQueuePosition); +}; +const sortPrsByQueuePosition = (queuedPrs) => queuedPrs + .map(pr => { + var _a, _b; + const label = (_a = pr.labels.find(label => { var _a; return (_a = label.name) === null || _a === void 0 ? void 0 : _a.startsWith(constants/* QUEUED_FOR_MERGE_PREFIX */.Ee); })) === null || _a === void 0 ? void 0 : _a.name; + const isJumpingTheQueue = Boolean(pr.labels.find(label => label.name === constants/* JUMP_THE_QUEUE_PR_LABEL */.nJ)); + const queuePosition = isJumpingTheQueue ? 0 : Number((_b = label === null || label === void 0 ? void 0 : label.split('#')) === null || _b === void 0 ? void 0 : _b[1]); + return { + number: pr.number, + label, + queuePosition + }; +}) + .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 newQueuePosition = index + 1; + if (!label || 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)); + yield Promise.all([ + (0,set_commit_status.setCommitStatus)({ sha: pullRequest.head.sha, context: constants/* MERGE_QUEUE_STATUS */.Cb, state: 'success', description: 'This PR is next to merge.' - }); - } - return Promise.all([ - octokit/* octokit.issues.addLabels */.K.issues.addLabels(Object.assign({ labels: [`${constants/* QUEUED_FOR_MERGE_PREFIX */.Ee} #${newQueuePosition}`], issue_number: pull_number }, github.context.repo)), - (0,remove_label.removeLabelIfExists)(label, pull_number) + }), + (0,remove_label.removeLabelIfExists)(constants/* JUMP_THE_QUEUE_PR_LABEL */.nJ, number) ]); - })); -}; + } + 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) + ]); +}); ;// CONCATENATED MODULE: ./src/helpers/manage-merge-queue.ts /* @@ -177,25 +183,18 @@ var manage_merge_queue_awaiter = (undefined && undefined.__awaiter) || function const manageMergeQueue = () => manage_merge_queue_awaiter(void 0, void 0, void 0, function* () { - const issue_number = github.context.issue.number; - const { data: pullRequest } = yield octokit/* octokit.pulls.get */.K.pulls.get(Object.assign({ pull_number: issue_number }, github.context.repo)); + const { data: pullRequest } = yield octokit/* octokit.pulls.get */.K.pulls.get(Object.assign({ pull_number: github.context.issue.number }, github.context.repo)); if (pullRequest.merged || !pullRequest.labels.find(label => label.name === constants/* READY_FOR_MERGE_PR_LABEL */.Ak)) { core.info('This PR is not in the merge queue.'); - return removePRFromQueue(pullRequest); + return removePrFromQueue(pullRequest); } - const { data: { total_count: queuePosition } } = yield getQueuedPrData(); - const isFirstQueuePosition = queuePosition === 1 || pullRequest.labels.find(label => label.name === constants/* FIRST_QUEUED_PR_LABEL */.IH); - return Promise.all([ - octokit/* octokit.issues.addLabels */.K.issues.addLabels(Object.assign({ labels: [`${constants/* QUEUED_FOR_MERGE_PREFIX */.Ee} #${queuePosition}`], issue_number }, github.context.repo)), - (0,set_commit_status.setCommitStatus)({ - sha: pullRequest.head.sha, - context: constants/* MERGE_QUEUE_STATUS */.Cb, - state: isFirstQueuePosition ? 'success' : 'pending', - description: isFirstQueuePosition ? 'This PR is next to merge.' : 'This PR is in line to merge.' - }) - ]); + const { data: { items, total_count: queuePosition } } = yield getQueuedPrData(); + if (pullRequest.labels.find(label => label.name === constants/* JUMP_THE_QUEUE_PR_LABEL */.nJ)) { + return updateMergeQueue(items); + } + return addPrToQueue(pullRequest, queuePosition); }); -const removePRFromQueue = (pullRequest) => manage_merge_queue_awaiter(void 0, void 0, void 0, function* () { +const removePrFromQueue = (pullRequest) => manage_merge_queue_awaiter(void 0, void 0, void 0, function* () { var _a; const queueLabel = (_a = pullRequest.labels.find(label => { var _a; return (_a = label.name) === null || _a === void 0 ? void 0 : _a.startsWith(constants/* QUEUED_FOR_MERGE_PREFIX */.Ee); })) === null || _a === void 0 ? void 0 : _a.name; if (queueLabel) { @@ -204,6 +203,18 @@ const removePRFromQueue = (pullRequest) => manage_merge_queue_awaiter(void 0, vo yield updateMergeQueue(items); } }); +const addPrToQueue = (pullRequest, queuePosition) => { + const isFirstQueuePosition = queuePosition === 1 || pullRequest.labels.find(label => label.name === constants/* FIRST_QUEUED_PR_LABEL */.IH); + return Promise.all([ + octokit/* octokit.issues.addLabels */.K.issues.addLabels(Object.assign({ labels: [`${constants/* QUEUED_FOR_MERGE_PREFIX */.Ee} #${queuePosition}`], issue_number: github.context.issue.number }, github.context.repo)), + (0,set_commit_status.setCommitStatus)({ + sha: pullRequest.head.sha, + context: constants/* MERGE_QUEUE_STATUS */.Cb, + state: isFirstQueuePosition ? 'success' : 'pending', + description: isFirstQueuePosition ? 'This PR is next to merge.' : 'This PR is in line to merge.' + }) + ]); +}; const getQueuedPrData = () => { const { repo, owner } = github.context.repo; return octokit/* octokit.search.issuesAndPullRequests */.K.search.issuesAndPullRequests({ diff --git a/dist/473.index.js.map b/dist/473.index.js.map index b25940352..85d4b4488 100644 --- a/dist/473.index.js.map +++ b/dist/473.index.js.map @@ -1 +1 @@ -{"version":3,"file":"473.index.js","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/remove-label.ts","webpack://github-helpers/./src/helpers/set-commit-status.ts","webpack://github-helpers/./src/octokit.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 operations. Newer versions of octokit may not require this\nexport const GITHUB_OPTIONS = {\n headers: {\n accept: 'application/vnd.github.ant-man-preview+json,application/vnd.github.flash-preview+json,application/vnd.github.v3+json'\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 DEFAULT_BRANCH = 'main';\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)((.*))?: .+$';\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 { MERGE_QUEUE_STATUS, QUEUED_FOR_MERGE_PREFIX } from '../constants';\nimport { PullRequestSearchResults } from '../types';\nimport { context } from '@actions/github';\nimport { map } from 'bluebird';\nimport { octokit } from '../octokit';\nimport { removeLabelIfExists } from '../helpers/remove-label';\nimport { setCommitStatus } from '../helpers/set-commit-status';\n\nexport const updateMergeQueue = (queuedPrs: PullRequestSearchResults) => {\n const prsSortedByQueuePosition = queuedPrs\n .map(pr => {\n const label = pr.labels.find(label => label.name?.startsWith(QUEUED_FOR_MERGE_PREFIX))?.name;\n const queuePosition = Number(label?.split('#')?.[1]);\n return {\n pull_number: pr.number,\n label,\n queuePosition\n };\n })\n .sort((pr1, pr2) => pr1.queuePosition - pr2.queuePosition);\n return map(prsSortedByQueuePosition, async (pr, index) => {\n const { pull_number, label, queuePosition } = pr;\n const newQueuePosition = index + 1;\n if (!label || queuePosition === newQueuePosition) {\n return;\n }\n if (newQueuePosition === 1) {\n const { data: pullRequest } = await octokit.pulls.get({ pull_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 }\n\n return Promise.all([\n octokit.issues.addLabels({\n labels: [`${QUEUED_FOR_MERGE_PREFIX} #${newQueuePosition}`],\n issue_number: pull_number,\n ...context.repo\n }),\n removeLabelIfExists(label, pull_number)\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 { FIRST_QUEUED_PR_LABEL, MERGE_QUEUE_STATUS, QUEUED_FOR_MERGE_PREFIX, READY_FOR_MERGE_PR_LABEL } from '../constants';\nimport { PullRequest } from '../types';\nimport { context } from '@actions/github';\nimport { map } from 'bluebird';\nimport { octokit } from '../octokit';\nimport { removeLabelIfExists } from './remove-label';\nimport { setCommitStatus } from './set-commit-status';\nimport { updateMergeQueue } from '../utils/update-merge-queue';\n\nexport const manageMergeQueue = async () => {\n const issue_number = context.issue.number;\n const { data: pullRequest } = await octokit.pulls.get({ pull_number: 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);\n }\n\n const {\n data: { total_count: queuePosition }\n } = await getQueuedPrData();\n const isFirstQueuePosition = queuePosition === 1 || pullRequest.labels.find(label => label.name === FIRST_QUEUED_PR_LABEL);\n return Promise.all([\n octokit.issues.addLabels({\n labels: [`${QUEUED_FOR_MERGE_PREFIX} #${queuePosition}`],\n issue_number,\n ...context.repo\n }),\n 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};\n\nconst 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], label => removeLabelIfExists(label, pullRequest.number));\n const {\n data: { items }\n } = await getQueuedPrData();\n await updateMergeQueue(items);\n }\n};\n\nconst getQueuedPrData = () => {\n const { repo, owner } = context.repo;\n return octokit.search.issuesAndPullRequests({\n q: `org:${owner}+repo:${repo}+is:pr+is:open+label:\"${READY_FOR_MERGE_PR_LABEL}\"`\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 { context } from '@actions/github';\nimport { octokit } from '../octokit';\n\ninterface RemoveLabel {\n label: string;\n}\n\nexport const removeLabel = ({ label }: RemoveLabel) => removeLabelIfExists(label, context.issue.number);\n\nexport const removeLabelIfExists = (labelName: string, issue_number: number) =>\n octokit.issues\n .removeLabel({\n name: labelName,\n issue_number,\n ...context.repo\n })\n .catch(error => {\n if (error.status === 404) {\n core.info('Label is not present on PR.');\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 { PipelineState } from '../types';\nimport { context as githubContext } from '@actions/github';\nimport { map } from 'bluebird';\nimport { octokit } from '../octokit';\n\ninterface SetCommitStatus {\n sha: string;\n context: string;\n state: PipelineState;\n description?: string;\n target_url?: string;\n}\n\nexport const setCommitStatus = ({ sha, context, state, description, target_url }: SetCommitStatus) =>\n map(context.split('\\n'), context =>\n octokit.repos.createCommitStatus({\n sha,\n context,\n state,\n description,\n target_url,\n ...githubContext.repo\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 { Octokit } from '@octokit/rest';\nimport { getOctokit } from '@actions/github';\n\nexport const octokit = getOctokit(core.getInput('github_token', { required: true })).rest as unknown as Octokit;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AAAA;;;;;;;;;;;AAWA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;A;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AChCA;;;;;;;;;;;AAWA;;;;;;;;;;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAKA;AACA;AACA;AACA;;;AC1DA;;;;;;;;;;;AAWA;;;;;;;;;;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAGA;AACA;AACA;AAKA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;;AACA;AACA;AACA;AACA;AAGA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;;;A;;;;;;;;;;;;;;;;AClEA;;;;;;;;;;;AAWA;AAEA;AACA;AACA;AAMA;AAEA,qJAEA;AAKA;AACA;AACA;AACA;AACA;;;A;;;;;;;;;;;;;;;AClCA;;;;;;;;;;;AAWA;AAGA;AACA;AACA;AAUA;AAIA;AACA;AACA;AACA;;;A;;;;;;;;;;;;;ACjCA;;;;;;;;;;;AAWA;AAEA;AAEA;AAEA;;;A;;;A","sourceRoot":""} \ No newline at end of file +{"version":3,"file":"473.index.js","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/remove-label.ts","webpack://github-helpers/./src/helpers/set-commit-status.ts","webpack://github-helpers/./src/octokit.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 operations. Newer versions of octokit may not require this\nexport const GITHUB_OPTIONS = {\n headers: {\n accept: 'application/vnd.github.ant-man-preview+json,application/vnd.github.flash-preview+json,application/vnd.github.v3+json'\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 DEFAULT_BRANCH = 'main';\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)((.*))?: .+$';\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 { PullRequestSearchResults } from '../types';\nimport { context } from '@actions/github';\nimport { map } from 'bluebird';\nimport { octokit } from '../octokit';\nimport { removeLabelIfExists } from '../helpers/remove-label';\nimport { setCommitStatus } from '../helpers/set-commit-status';\n\nexport const updateMergeQueue = (queuedPrs: PullRequestSearchResults) => {\n const sortedPrs = sortPrsByQueuePosition(queuedPrs);\n return map(sortedPrs, updateQueuePosition);\n};\n\nconst sortPrsByQueuePosition = (queuedPrs: PullRequestSearchResults): 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 || queuePosition === newQueuePosition) {\n return;\n }\n if (newQueuePosition === 1) {\n const { data: pullRequest } = await octokit.pulls.get({ pull_number: number, ...context.repo });\n await Promise.all([\n setCommitStatus({\n sha: pullRequest.head.sha,\n context: MERGE_QUEUE_STATUS,\n state: 'success',\n description: 'This PR is next to merge.'\n }),\n removeLabelIfExists(JUMP_THE_QUEUE_PR_LABEL, number)\n ]);\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 { PullRequest } from '../types';\nimport { context } from '@actions/github';\nimport { map } from 'bluebird';\nimport { octokit } from '../octokit';\nimport { removeLabelIfExists } from './remove-label';\nimport { setCommitStatus } from './set-commit-status';\nimport { updateMergeQueue } from '../utils/update-merge-queue';\n\nexport const manageMergeQueue = async () => {\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);\n }\n const {\n data: { items, total_count: queuePosition }\n } = await getQueuedPrData();\n if (pullRequest.labels.find(label => label.name === JUMP_THE_QUEUE_PR_LABEL)) {\n return updateMergeQueue(items);\n }\n return addPrToQueue(pullRequest, queuePosition);\n};\n\nconst 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], label => removeLabelIfExists(label, pullRequest.number));\n const {\n data: { items }\n } = await getQueuedPrData();\n await updateMergeQueue(items);\n }\n};\n\nconst addPrToQueue = (pullRequest: PullRequest, queuePosition: number) => {\n const isFirstQueuePosition = queuePosition === 1 || pullRequest.labels.find(label => label.name === FIRST_QUEUED_PR_LABEL);\n return Promise.all([\n octokit.issues.addLabels({\n labels: [`${QUEUED_FOR_MERGE_PREFIX} #${queuePosition}`],\n issue_number: context.issue.number,\n ...context.repo\n }),\n 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};\n\nconst getQueuedPrData = () => {\n const { repo, owner } = context.repo;\n return octokit.search.issuesAndPullRequests({\n q: `org:${owner}+repo:${repo}+is:pr+is:open+label:\"${READY_FOR_MERGE_PR_LABEL}\"`\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 { context } from '@actions/github';\nimport { octokit } from '../octokit';\n\ninterface RemoveLabel {\n label: string;\n}\n\nexport const removeLabel = ({ label }: RemoveLabel) => removeLabelIfExists(label, context.issue.number);\n\nexport const removeLabelIfExists = (labelName: string, issue_number: number) =>\n octokit.issues\n .removeLabel({\n name: labelName,\n issue_number,\n ...context.repo\n })\n .catch(error => {\n if (error.status === 404) {\n core.info('Label is not present on PR.');\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 { PipelineState } from '../types';\nimport { context as githubContext } from '@actions/github';\nimport { map } from 'bluebird';\nimport { octokit } from '../octokit';\n\ninterface SetCommitStatus {\n sha: string;\n context: string;\n state: PipelineState;\n description?: string;\n target_url?: string;\n}\n\nexport const setCommitStatus = ({ sha, context, state, description, target_url }: SetCommitStatus) =>\n map(context.split('\\n'), context =>\n octokit.repos.createCommitStatus({\n sha,\n context,\n state,\n description,\n target_url,\n ...githubContext.repo\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 { Octokit } from '@octokit/rest';\nimport { getOctokit } from '@actions/github';\n\nexport const octokit = getOctokit(core.getInput('github_token', { required: true })).rest as unknown as Octokit;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AAAA;;;;;;;;;;;AAWA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;A;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AChCA;;;;;;;;;;;AAWA;;;;;;;;;;AAEA;AAEA;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;AACA;AACA;AAEA;AACA;AAKA;AACA;AACA;;;ACnEA;;;;;;;;;;;AAWA;;;;;;;;;;AAEA;AACA;AAQA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AACA;AAEA;;AACA;AACA;AACA;AACA;AAGA;AACA;AACA;AAEA;AACA;AACA;AACA;AAKA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;;;A;;;;;;;;;;;;;;;;AC7EA;;;;;;;;;;;AAWA;AAEA;AACA;AACA;AAMA;AAEA,qJAEA;AAKA;AACA;AACA;AACA;AACA;;;A;;;;;;;;;;;;;;;AClCA;;;;;;;;;;;AAWA;AAGA;AACA;AACA;AAUA;AAIA;AACA;AACA;AACA;;;A;;;;;;;;;;;;;ACjCA;;;;;;;;;;;AAWA;AAEA;AAEA;AAEA;;;A;;;A","sourceRoot":""} \ No newline at end of file diff --git a/package.json b/package.json index 1e3223090..c36f62f07 100644 --- a/package.json +++ b/package.json @@ -52,7 +52,8 @@ "transform": { "^.+\\.(j|t)sx?$": "ts-jest" }, - "testRegex": "(/__tests__/.*|\\.(test|spec))\\.(ts|tsx|js)$" + "testRegex": "(/__tests__/.*|\\.(test|spec))\\.(ts|tsx|js)$", + "clearMocks": true }, "scripts": { "build": "tsc", diff --git a/src/helpers/manage-merge-queue.ts b/src/helpers/manage-merge-queue.ts index 8ff730bed..72254f459 100644 --- a/src/helpers/manage-merge-queue.ts +++ b/src/helpers/manage-merge-queue.ts @@ -12,7 +12,13 @@ limitations under the License. */ import * as core from '@actions/core'; -import { FIRST_QUEUED_PR_LABEL, MERGE_QUEUE_STATUS, QUEUED_FOR_MERGE_PREFIX, READY_FOR_MERGE_PR_LABEL } from '../constants'; +import { + FIRST_QUEUED_PR_LABEL, + JUMP_THE_QUEUE_PR_LABEL, + MERGE_QUEUE_STATUS, + QUEUED_FOR_MERGE_PREFIX, + READY_FOR_MERGE_PR_LABEL +} from '../constants'; import { PullRequest } from '../types'; import { context } from '@actions/github'; import { map } from 'bluebird'; @@ -22,21 +28,37 @@ import { setCommitStatus } from './set-commit-status'; import { updateMergeQueue } from '../utils/update-merge-queue'; export const manageMergeQueue = async () => { - const issue_number = context.issue.number; - const { data: pullRequest } = await octokit.pulls.get({ pull_number: issue_number, ...context.repo }); + const { data: pullRequest } = await octokit.pulls.get({ pull_number: context.issue.number, ...context.repo }); if (pullRequest.merged || !pullRequest.labels.find(label => label.name === READY_FOR_MERGE_PR_LABEL)) { core.info('This PR is not in the merge queue.'); - return removePRFromQueue(pullRequest); + return removePrFromQueue(pullRequest); } - const { - data: { total_count: queuePosition } + data: { items, total_count: queuePosition } } = await getQueuedPrData(); + if (pullRequest.labels.find(label => label.name === JUMP_THE_QUEUE_PR_LABEL)) { + return updateMergeQueue(items); + } + return addPrToQueue(pullRequest, queuePosition); +}; + +const removePrFromQueue = async (pullRequest: PullRequest) => { + const queueLabel = pullRequest.labels.find(label => label.name?.startsWith(QUEUED_FOR_MERGE_PREFIX))?.name; + if (queueLabel) { + await map([READY_FOR_MERGE_PR_LABEL, queueLabel], label => removeLabelIfExists(label, pullRequest.number)); + const { + data: { items } + } = await getQueuedPrData(); + await updateMergeQueue(items); + } +}; + +const addPrToQueue = (pullRequest: PullRequest, queuePosition: number) => { const isFirstQueuePosition = queuePosition === 1 || pullRequest.labels.find(label => label.name === FIRST_QUEUED_PR_LABEL); return Promise.all([ octokit.issues.addLabels({ labels: [`${QUEUED_FOR_MERGE_PREFIX} #${queuePosition}`], - issue_number, + issue_number: context.issue.number, ...context.repo }), setCommitStatus({ @@ -48,17 +70,6 @@ export const manageMergeQueue = async () => { ]); }; -const removePRFromQueue = async (pullRequest: PullRequest) => { - const queueLabel = pullRequest.labels.find(label => label.name?.startsWith(QUEUED_FOR_MERGE_PREFIX))?.name; - if (queueLabel) { - await map([READY_FOR_MERGE_PR_LABEL, queueLabel], label => removeLabelIfExists(label, pullRequest.number)); - const { - data: { items } - } = await getQueuedPrData(); - await updateMergeQueue(items); - } -}; - const getQueuedPrData = () => { const { repo, owner } = context.repo; return octokit.search.issuesAndPullRequests({ diff --git a/src/utils/update-merge-queue.ts b/src/utils/update-merge-queue.ts index 46650947c..cd6f3ea78 100644 --- a/src/utils/update-merge-queue.ts +++ b/src/utils/update-merge-queue.ts @@ -11,7 +11,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { MERGE_QUEUE_STATUS, QUEUED_FOR_MERGE_PREFIX } from '../constants'; +import { JUMP_THE_QUEUE_PR_LABEL, MERGE_QUEUE_STATUS, QUEUED_FOR_MERGE_PREFIX } from '../constants'; import { PullRequestSearchResults } from '../types'; import { context } from '@actions/github'; import { map } from 'bluebird'; @@ -20,40 +20,55 @@ import { removeLabelIfExists } from '../helpers/remove-label'; import { setCommitStatus } from '../helpers/set-commit-status'; export const updateMergeQueue = (queuedPrs: PullRequestSearchResults) => { - const prsSortedByQueuePosition = queuedPrs + const sortedPrs = sortPrsByQueuePosition(queuedPrs); + return map(sortedPrs, updateQueuePosition); +}; + +const sortPrsByQueuePosition = (queuedPrs: PullRequestSearchResults): QueuedPr[] => + queuedPrs .map(pr => { const label = pr.labels.find(label => label.name?.startsWith(QUEUED_FOR_MERGE_PREFIX))?.name; - const queuePosition = Number(label?.split('#')?.[1]); + const isJumpingTheQueue = Boolean(pr.labels.find(label => label.name === JUMP_THE_QUEUE_PR_LABEL)); + const queuePosition = isJumpingTheQueue ? 0 : Number(label?.split('#')?.[1]); return { - pull_number: pr.number, + number: pr.number, label, queuePosition }; }) .sort((pr1, pr2) => pr1.queuePosition - pr2.queuePosition); - return map(prsSortedByQueuePosition, async (pr, index) => { - const { pull_number, label, queuePosition } = pr; - const newQueuePosition = index + 1; - if (!label || queuePosition === newQueuePosition) { - return; - } - if (newQueuePosition === 1) { - const { data: pullRequest } = await octokit.pulls.get({ pull_number, ...context.repo }); - await setCommitStatus({ + +const updateQueuePosition = async (pr: QueuedPr, index: number) => { + const { number, label, queuePosition } = pr; + const newQueuePosition = index + 1; + if (!label || queuePosition === newQueuePosition) { + return; + } + if (newQueuePosition === 1) { + const { data: pullRequest } = await octokit.pulls.get({ pull_number: number, ...context.repo }); + await Promise.all([ + setCommitStatus({ sha: pullRequest.head.sha, context: MERGE_QUEUE_STATUS, state: 'success', description: 'This PR is next to merge.' - }); - } - - return Promise.all([ - octokit.issues.addLabels({ - labels: [`${QUEUED_FOR_MERGE_PREFIX} #${newQueuePosition}`], - issue_number: pull_number, - ...context.repo }), - removeLabelIfExists(label, pull_number) + removeLabelIfExists(JUMP_THE_QUEUE_PR_LABEL, number) ]); - }); + } + + return Promise.all([ + octokit.issues.addLabels({ + labels: [`${QUEUED_FOR_MERGE_PREFIX} #${newQueuePosition}`], + issue_number: number, + ...context.repo + }), + removeLabelIfExists(label, number) + ]); +}; + +type QueuedPr = { + number: number; + label?: string; + queuePosition: number; }; diff --git a/test/helpers/add-pr-approval-label.test.ts b/test/helpers/add-pr-approval-label.test.ts index 6b51c70ad..da84b048f 100644 --- a/test/helpers/add-pr-approval-label.test.ts +++ b/test/helpers/add-pr-approval-label.test.ts @@ -29,10 +29,6 @@ jest.mock('../../src/utils/get-core-member-logins'); const teams = 'team1\nteam2'; describe('addPrApprovalLabel', () => { - afterEach(() => { - jest.clearAllMocks(); - }); - describe('core approver case', () => { const login = 'user1'; diff --git a/test/helpers/assign-pr-reviewers.test.ts b/test/helpers/assign-pr-reviewers.test.ts index 7f220bd1a..f0458d057 100644 --- a/test/helpers/assign-pr-reviewers.test.ts +++ b/test/helpers/assign-pr-reviewers.test.ts @@ -37,10 +37,6 @@ describe('assignPrReviewer', () => { const teams = 'team1\nteam2'; const pull_number = 123; - afterEach(() => { - jest.clearAllMocks(); - }); - describe('login provided', () => { describe('core member case', () => { const login = 'user1'; diff --git a/test/helpers/check-pr-title.test.ts b/test/helpers/check-pr-title.test.ts index cab923278..90291a6ff 100644 --- a/test/helpers/check-pr-title.test.ts +++ b/test/helpers/check-pr-title.test.ts @@ -22,10 +22,6 @@ jest.mock('@actions/github', () => ({ })); describe('checkPrTitle', () => { - afterEach(() => { - jest.resetAllMocks(); - }); - it('should pass as the PR title conforms to the regex', async () => { (octokit.pulls.get as unknown as Mocktokit).mockImplementation(async () => ({ data: { diff --git a/test/helpers/filter-paths.test.ts b/test/helpers/filter-paths.test.ts index 24a71cb7b..c3638850d 100644 --- a/test/helpers/filter-paths.test.ts +++ b/test/helpers/filter-paths.test.ts @@ -25,10 +25,6 @@ describe('filterPaths', () => { const paths = 'file/path/1\nfile/path/2'; const globs = '**/*.md\nsomething/**/file1.txt'; - afterEach(() => { - jest.resetAllMocks(); - }); - it('should return true if one of the file paths match the file paths that octokit returns', async () => { (octokit.pulls.listFiles as unknown as Mocktokit).mockImplementation(async () => ({ data: [ diff --git a/test/helpers/manage-merge-queue.test.ts b/test/helpers/manage-merge-queue.test.ts index b99ed48f2..dc3ad8108 100644 --- a/test/helpers/manage-merge-queue.test.ts +++ b/test/helpers/manage-merge-queue.test.ts @@ -11,7 +11,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { MERGE_QUEUE_STATUS, READY_FOR_MERGE_PR_LABEL } from '../../src/constants'; +import { JUMP_THE_QUEUE_PR_LABEL, MERGE_QUEUE_STATUS, READY_FOR_MERGE_PR_LABEL } from '../../src/constants'; import { Mocktokit } from '../types'; import { context } from '@actions/github'; import { manageMergeQueue } from '../../src/helpers/manage-merge-queue'; @@ -51,7 +51,7 @@ describe('manageMergeQueue', () => { data: { merged: true, number: 123, - labels: [{ name: 'QUEUED FOR MERGE #1' }] + labels: [{ name: READY_FOR_MERGE_PR_LABEL }, { name: 'QUEUED FOR MERGE #1' }] } })); await manageMergeQueue(); @@ -190,4 +190,33 @@ describe('manageMergeQueue', () => { }); }); }); + + describe('jump the queue case', () => { + beforeEach(async () => { + (octokit.search.issuesAndPullRequests as unknown as Mocktokit).mockImplementation(async () => ({ + data: { + total_count: 5, + items + } + })); + (octokit.pulls.get as unknown as Mocktokit).mockImplementation(async () => ({ + data: { + merged: false, + head: { sha: 'sha' }, + labels: [{ name: READY_FOR_MERGE_PR_LABEL }, { name: JUMP_THE_QUEUE_PR_LABEL }, { name: 'QUEUED FOR MERGE #5' }] + } + })); + await manageMergeQueue(); + }); + + it('should call issuesAndPullRequests search with correct params', () => { + expect(octokit.search.issuesAndPullRequests).toHaveBeenCalledWith({ + q: `org:owner+repo:repo+is:pr+is:open+label:"${READY_FOR_MERGE_PR_LABEL}"` + }); + }); + + it('should call updateMergeQueue with correct params', () => { + expect(updateMergeQueue).toHaveBeenCalledWith(items); + }); + }); }); diff --git a/test/helpers/prepare-queued-pr-for-merge.test.ts b/test/helpers/prepare-queued-pr-for-merge.test.ts index 3413f3d51..67355eb88 100644 --- a/test/helpers/prepare-queued-pr-for-merge.test.ts +++ b/test/helpers/prepare-queued-pr-for-merge.test.ts @@ -277,8 +277,4 @@ describe('prepareQueuedPrForMerge', () => { expect(octokit.issues.createComment).not.toHaveBeenCalled(); }); }); - - afterEach(() => { - jest.clearAllMocks(); - }); }); diff --git a/test/helpers/set-deployment-status.test.ts b/test/helpers/set-deployment-status.test.ts index 45fbb6b8a..cd0fe1035 100644 --- a/test/helpers/set-deployment-status.test.ts +++ b/test/helpers/set-deployment-status.test.ts @@ -149,8 +149,4 @@ describe('setDeploymentStatus', () => { }); }); }); - - afterEach(() => { - jest.clearAllMocks(); - }); }); diff --git a/test/utils/update-merge-queue.test.ts b/test/utils/update-merge-queue.test.ts index 4d1cdc813..87b26ca1f 100644 --- a/test/utils/update-merge-queue.test.ts +++ b/test/utils/update-merge-queue.test.ts @@ -11,7 +11,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { MERGE_QUEUE_STATUS } from '../../src/constants'; +import { JUMP_THE_QUEUE_PR_LABEL, MERGE_QUEUE_STATUS } from '../../src/constants'; import { Mocktokit } from '../types'; import { PullRequestSearchResults } from '../../src/types'; import { context } from '@actions/github'; @@ -118,7 +118,45 @@ describe('updateMergeQueue', () => { }); }); - afterEach(() => { - jest.clearAllMocks(); + describe('pr jumping the queue case', () => { + const queuedPrs = [ + { + number: 123, + labels: [{ name: JUMP_THE_QUEUE_PR_LABEL }, { name: 'QUEUED FOR MERGE #5' }] + }, + { + number: 456, + labels: [{ name: 'QUEUED FOR MERGE #1' }] + } + ]; + beforeEach(async () => { + await updateMergeQueue(queuedPrs as PullRequestSearchResults); + }); + + it('should call add labels with correct params', () => { + expect(octokit.issues.addLabels).toHaveBeenCalledWith({ + labels: ['QUEUED FOR MERGE #1'], + issue_number: 123, + ...context.repo + }); + expect(setCommitStatus).toHaveBeenCalledWith({ + sha: 'sha123', + context: MERGE_QUEUE_STATUS, + state: 'success', + description: 'This PR is next to merge.' + }); + expect(octokit.issues.addLabels).toHaveBeenCalledWith({ + labels: ['QUEUED FOR MERGE #2'], + issue_number: 456, + ...context.repo + }); + }); + + it('should call remove label with correct params', () => { + expect(removeLabelIfExists).toHaveBeenCalledWith(JUMP_THE_QUEUE_PR_LABEL, 123); + expect(removeLabelIfExists).toHaveBeenCalledWith('QUEUED FOR MERGE #5', 123); + expect(removeLabelIfExists).not.toHaveBeenCalledWith(JUMP_THE_QUEUE_PR_LABEL, 456); + expect(removeLabelIfExists).toHaveBeenCalledWith('QUEUED FOR MERGE #1', 456); + }); }); });