Skip to content

Commit

Permalink
feat: add support for jump the queue label (#50)
Browse files Browse the repository at this point in the history
  • Loading branch information
danadajian authored Jan 3, 2022
1 parent 5bd85bc commit f01f07e
Show file tree
Hide file tree
Showing 13 changed files with 196 additions and 115 deletions.
97 changes: 54 additions & 43 deletions dist/473.index.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion dist/473.index.js.map

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
47 changes: 29 additions & 18 deletions src/helpers/manage-merge-queue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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({
Expand All @@ -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({
Expand Down
61 changes: 38 additions & 23 deletions src/utils/update-merge-queue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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;
};
4 changes: 0 additions & 4 deletions test/helpers/add-pr-approval-label.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down
4 changes: 0 additions & 4 deletions test/helpers/assign-pr-reviewers.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down
4 changes: 0 additions & 4 deletions test/helpers/check-pr-title.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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: {
Expand Down
4 changes: 0 additions & 4 deletions test/helpers/filter-paths.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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: [
Expand Down
33 changes: 31 additions & 2 deletions test/helpers/manage-merge-queue.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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);
});
});
});
Loading

0 comments on commit f01f07e

Please sign in to comment.