diff --git a/README.md b/README.md index 1e41165..0ba08ea 100644 --- a/README.md +++ b/README.md @@ -48,6 +48,8 @@ Members of the API Working Group must indicate their approval by leaving a comme If a PR has passed its minimum open time and has the requisite number of approvals with no outstanding requests for changes, the bot will then switch `api-review/requested 🗳` to `api-review/approved ✅`, and the PR is free to be merged. If outstanding change requests persist, then the group will initiate consensus-seeking procedures and ultimately choose to approve or decline the PR. If the decision is made to decline, the API WG chair will then comment on the PR with `API Declined` and the bot will update `api-review/requested 🗳` to `api-review/declined ❌`. +For PRs that need to land faster than the minimum open time (e.g. to respond to OS or Chromium updates), the minimum open time can be bypassed by adding a `api-review/skip-delay ⏰` label to the PR. This label may be added to a PR if at least two members of the API WG representing two different employers approve fast-tracking the PR. + ## Deprecation Review The bot controls the deprecation review lifecycle on behalf of the [Releases Working Group](https://github.com/electron/governance/tree/main/wg-releases). diff --git a/spec/api-review-state.spec.ts b/spec/api-review-state.spec.ts index 04ad2eb..3505a77 100644 --- a/spec/api-review-state.spec.ts +++ b/spec/api-review-state.spec.ts @@ -122,6 +122,18 @@ describe('api review', () => { expect(readyDate).toEqual(expectedDate); }); + it('correctly returns PR ready date when skip-timeout label is found', async () => { + const payload = loadFixture('api-review-state/pull_request.api-skip-delay_label.json'); + + // Set created_at to yesterday. + payload.created_at = new Date(+new Date() - 1000 * 60 * 60 * 24 * 2); + + const expectedDate = payload.created_at.toISOString().split('T')[0]; + const readyDate = getPRReadyDate(payload); + + expect(readyDate).toEqual(expectedDate); + }); + it('should reset the check when PR does not have an API review label on a base PR', async () => { let { pull_request } = loadFixture('api-review-state/pull_request.no_review_label.json'); diff --git a/spec/fixtures/api-review-state/pull_request.api-skip-delay_label.json b/spec/fixtures/api-review-state/pull_request.api-skip-delay_label.json new file mode 100644 index 0000000..3108d5d --- /dev/null +++ b/spec/fixtures/api-review-state/pull_request.api-skip-delay_label.json @@ -0,0 +1,70 @@ +{ + "url": "https://api.github.com/repos/electron/electron/pulls/26876", + "id": 534054584, + "node_id": "MDExOlB1bGxSZXF1ZXN0NTM0MDU0NTg0", + "issue_url": "https://api.github.com/repos/electron/electron/issues/26876", + "number": 26876, + "state": "closed", + "locked": false, + "title": "build: fix JS linting", + "user": { + "login": "MarshallOfSound" + }, + "labels": [ + { + "id": 1034512799, + "node_id": "MDU6TGFiZWwxMDM0NTEyNzk5", + "url": "https://api.github.com/repos/electron/electron/labels/api-review/skip-delay%20%E2%8F%B0", + "name": "api-review/skip-delay ⏰", + "color": "6ac2dd", + "default": false, + "description": "skip the default API approval delay" + } + ], + "body": "* Ensure --fix output is actually written to disk\r\n* Cache bust on lint.js file changes\r\n* Ensure CI does not use the linting cache\r\n\r\nNotes: no-notes", + "created_at": "2020-12-08T01:24:55Z", + "updated_at": "2020-12-10T18:57:11Z", + "closed_at": "2020-12-10T18:57:07Z", + "merged_at": "2020-12-10T18:57:07Z", + "merge_commit_sha": "51db2a6b34792c99a9a685bdfbfe87a7343631b9", + "assignee": null, + "milestone": null, + "draft": false, + "commits_url": "https://api.github.com/repos/electron/electron/pulls/26876/commits", + "review_comments_url": "https://api.github.com/repos/electron/electron/pulls/26876/comments", + "review_comment_url": "https://api.github.com/repos/electron/electron/pulls/comments{/number}", + "comments_url": "https://api.github.com/repos/electron/electron/issues/26876/comments", + "statuses_url": "https://api.github.com/repos/electron/electron/statuses/c6b1b7168ab850a47f856c4a30f7a441bede1117", + "head": { + "label": "electron:fix-lint-js", + "ref": "fix-lint-js", + "sha": "c6b1b7168ab850a47f856c4a30f7a441bede1117", + "user": { + "login": "electron", + "id": 13409222, + "node_id": "MDEyOk9yZ2FuaXphdGlvbjEzNDA5MjIy", + "avatar_url": "https://avatars.githubusercontent.com/u/13409222?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/electron" + }, + "repo": { + "id": 9384267, + "node_id": "MDEwOlJlcG9zaXRvcnk5Mzg0MjY3", + "name": "electron", + "full_name": "electron/electron", + "private": false, + "owner": { + "login": "electron", + "id": 13409222, + "url": "https://api.github.com/users/electron" + }, + "license": { + "key": "mit", + "name": "MIT License", + "spdx_id": "MIT", + "url": "https://api.github.com/licenses/mit", + "node_id": "MDc6TGljZW5zZTEz" + } + } + } +} diff --git a/src/api-review-state.ts b/src/api-review-state.ts index cc0e052..2a55ab7 100644 --- a/src/api-review-state.ts +++ b/src/api-review-state.ts @@ -2,6 +2,7 @@ import { Context, Probot } from 'probot'; import { log } from './utils/log-util'; import { API_REVIEW_CHECK_NAME, + API_SKIP_DELAY_LABEL, API_WORKING_GROUP, EXCLUDE_LABELS, MINIMUM_MINOR_OPEN_TIME, @@ -10,7 +11,6 @@ import { OWNER, REPO, REVIEW_LABELS, - REVIEW_STATUS, SEMVER_LABELS, } from './constants'; import { CheckRunStatus, LogLevel } from './enums'; @@ -44,9 +44,11 @@ export const isSemverMajorMinorLabel = (label: string) => */ export const getPRReadyDate = (pr: PullRequest) => { let readyTime = new Date(pr.created_at).getTime(); - const isMajorMinor = pr.labels.some((l: any) => isSemverMajorMinorLabel(l.name)); - readyTime += isMajorMinor ? MINIMUM_MINOR_OPEN_TIME : MINIMUM_PATCH_OPEN_TIME; + if (!pr.labels.some((l: any) => l.name === API_SKIP_DELAY_LABEL)) { + const isMajorMinor = pr.labels.some((l: any) => isSemverMajorMinorLabel(l.name)); + readyTime += isMajorMinor ? MINIMUM_MINOR_OPEN_TIME : MINIMUM_PATCH_OPEN_TIME; + } return new Date(readyTime).toISOString().split('T')[0]; }; diff --git a/src/constants.ts b/src/constants.ts index 8202f1c..7d2d577 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -39,6 +39,7 @@ export const REVIEW_LABELS = { APPROVED: 'api-review/approved ✅', DECLINED: 'api-review/declined ❌', }; +export const API_SKIP_DELAY_LABEL = 'api-review/skip-delay ⏰'; export const DEPRECATION_REVIEW_LABELS = { REQUESTED: 'deprecation-review/requested 📝',