-
Notifications
You must be signed in to change notification settings - Fork 5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: github action to check if PR has requested labels before being …
…merged (#19984) * feat(action): check if pr includes requested labels * fix(action): add missing QA label * fix(action): check if no label prevents merging * fix(action): remove QA label check * fix(action): add missing reopened condition * fix(action): increase list of labels which prevent merges
- Loading branch information
1 parent
1a21871
commit 0c25193
Showing
4 changed files
with
137 additions
and
18 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
import * as core from '@actions/core'; | ||
import { context, getOctokit } from '@actions/github'; | ||
import { GitHub } from '@actions/github/lib/utils'; | ||
|
||
main().catch((error: Error): void => { | ||
console.error(error); | ||
process.exit(1); | ||
}); | ||
|
||
async function main(): Promise<void> { | ||
// "GITHUB_TOKEN" is an automatically generated, repository-specific access token provided by GitHub Actions. | ||
const githubToken = process.env.GITHUB_TOKEN; | ||
if (!githubToken) { | ||
core.setFailed('GITHUB_TOKEN not found'); | ||
process.exit(1); | ||
} | ||
|
||
// Initialise octokit, required to call Github GraphQL API | ||
const octokit: InstanceType<typeof GitHub> = getOctokit(githubToken); | ||
|
||
// Retrieve pull request info from context | ||
const prRepoOwner = context.repo.owner; | ||
const prRepoName = context.repo.repo; | ||
const prNumber = context.payload.pull_request?.number; | ||
if (!prNumber) { | ||
core.setFailed('Pull request number not found'); | ||
process.exit(1); | ||
} | ||
|
||
// Retrieve pull request labels | ||
const prLabels = await retrievePullRequestLabels(octokit, prRepoOwner, prRepoName, prNumber); | ||
|
||
const preventMergeLabels = ["needs-qa", "QA'd but questions", "issues-found", "need-ux-ds-review", "blocked", "stale", "DO-NOT-MERGE"]; | ||
|
||
let hasTeamLabel = false; | ||
|
||
// Check pull request has at least required QA label and team label | ||
for (const label of prLabels) { | ||
if (label.startsWith("team-") || label === "external-contributor") { | ||
console.log(`PR contains a team label as expected: ${label}`); | ||
hasTeamLabel = true; | ||
} | ||
if (preventMergeLabels.includes(label)) { | ||
throw new Error(`PR cannot be merged because it still contains this label: ${label}`); | ||
} | ||
if (hasTeamLabel) { | ||
return; | ||
} | ||
} | ||
|
||
// Otherwise, throw an arror to prevent from merging | ||
let errorMessage = ''; | ||
if (!hasTeamLabel) { | ||
errorMessage += 'No team labels found on the PR. '; | ||
} | ||
errorMessage += 'Please add the required label(s) before merging the PR.'; | ||
throw new Error(errorMessage); | ||
|
||
} | ||
|
||
// This function retrieves the pull request on a specific repo | ||
async function retrievePullRequestLabels(octokit: InstanceType<typeof GitHub>, repoOwner: string, repoName: string, prNumber: number): Promise<string[]> { | ||
|
||
const retrievePullRequestLabelsQuery = ` | ||
query RetrievePullRequestLabels($repoOwner: String!, $repoName: String!, $prNumber: Int!) { | ||
repository(owner: $repoOwner, name: $repoName) { | ||
pullRequest(number: $prNumber) { | ||
labels(first: 100) { | ||
nodes { | ||
name | ||
} | ||
} | ||
} | ||
} | ||
} | ||
`; | ||
|
||
const retrievePullRequestLabelsResult: { | ||
repository: { | ||
pullRequest: { | ||
labels: { | ||
nodes: { | ||
name: string; | ||
}[]; | ||
} | ||
}; | ||
}; | ||
} = await octokit.graphql(retrievePullRequestLabelsQuery, { | ||
repoOwner, | ||
repoName, | ||
prNumber, | ||
}); | ||
|
||
const pullRequestLabels = retrievePullRequestLabelsResult?.repository?.pullRequest?.labels?.nodes?.map(labelObject => labelObject?.name); | ||
|
||
return pullRequestLabels || []; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
name: "Check PR has required labels" | ||
on: | ||
pull_request: | ||
branches: | ||
- main | ||
types: | ||
- opened | ||
- reopened | ||
- synchronize | ||
- labeled | ||
- unlabeled | ||
|
||
jobs: | ||
check-pr-labels: | ||
runs-on: ubuntu-latest | ||
permissions: | ||
pull-requests: read | ||
|
||
steps: | ||
- name: Checkout repository | ||
uses: actions/checkout@v3 | ||
with: | ||
fetch-depth: 0 # This is needed to checkout all branches | ||
|
||
- name: Set up Node.js | ||
uses: actions/setup-node@v3 | ||
with: | ||
node-version-file: '.nvmrc' | ||
cache: yarn | ||
|
||
- name: Install dependencies | ||
run: yarn --immutable | ||
|
||
- name: Check PR has required labels | ||
id: check-pr-has-required-labels | ||
env: | ||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
run: npm run check-pr-has-required-labels |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters