-
Notifications
You must be signed in to change notification settings - Fork 4.8k
Commit
…system-typography-consts-with-enums
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,173 @@ | ||
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> { | ||
const token = process.env.GITHUB_TOKEN; | ||
|
||
if (!token) { | ||
core.setFailed('GITHUB_TOKEN not found'); | ||
process.exit(1); | ||
} | ||
|
||
const octokit = getOctokit(token); | ||
|
||
const headRef = context.payload.pull_request?.head.ref || ''; | ||
|
||
let issueNumber = await getIssueNumberFromPullRequestBody(); | ||
if (issueNumber === "") { | ||
bailIfIsBranchNameInvalid(headRef); | ||
bailIfIsNotFeatureBranch(headRef); | ||
issueNumber = getIssueNumberFromBranchName(headRef); | ||
} | ||
|
||
await updateLabels(octokit, issueNumber); | ||
} | ||
|
||
async function getIssueNumberFromPullRequestBody(): Promise<string> { | ||
console.log("Checking if the PR's body references an issue..."); | ||
|
||
let ISSUE_LINK_IN_PR_DESCRIPTION_REGEX = | ||
/(close|closes|closed|fix|fixes|fixed|resolve|resolves|resolved)\s#\d+/gi; | ||
|
||
const prBody = await getPullRequestBody(); | ||
|
||
let matches = prBody.match(ISSUE_LINK_IN_PR_DESCRIPTION_REGEX); | ||
if (!matches || matches?.length === 0) { | ||
console.log( | ||
'No direct link can be drawn between the PR and an issue from the PR body because no issue number was referenced.', | ||
); | ||
return ""; | ||
} | ||
|
||
if (matches?.length > 1) { | ||
console.log( | ||
'No direct link can be drawn between the PR and an issue from the PR body because more than one issue number was referenced.', | ||
); | ||
return ""; | ||
} | ||
|
||
const ISSUE_NUMBER_REGEX = /\d+/; | ||
const issueNumber = matches[0].match(ISSUE_NUMBER_REGEX)?.[0] || ''; | ||
|
||
console.log(`Found issue number ${issueNumber} in PR body.`); | ||
|
||
return issueNumber; | ||
} | ||
|
||
async function getPullRequestBody(): Promise<string> { | ||
if (context.eventName !== 'pull_request') { | ||
console.log('This action should only run on pull_request events.'); | ||
process.exit(1); | ||
} | ||
|
||
const prBody = context.payload.pull_request?.body || ''; | ||
return prBody; | ||
} | ||
|
||
function bailIfIsBranchNameInvalid(branchName: string): void { | ||
const BRANCH_REGEX = | ||
/^(main|develop|(ci|chore|docs|feat|feature|fix|perf|refactor|revert|style)\/\d*(?:[-](?![-])\w*)*|Version-v\d+\.\d+\.\d+)$/; | ||
const isValidBranchName = new RegExp(BRANCH_REGEX).test(branchName); | ||
|
||
if (!isValidBranchName) { | ||
console.log('This branch name does not follow the convention.'); | ||
console.log( | ||
'Here are some example branch names that are accepted: "fix/123-description", "feat/123-longer-description", "feature/123", "main", "develop", "Version-v10.24.2".', | ||
); | ||
console.log( | ||
'No issue could be linked to this PR, so no labels were copied', | ||
); | ||
|
||
process.exit(0); | ||
} | ||
} | ||
|
||
function bailIfIsNotFeatureBranch(branchName: string): void { | ||
if ( | ||
branchName === 'main' || | ||
branchName === 'develop' || | ||
branchName.startsWith('Version-v') | ||
) { | ||
console.log(`${branchName} is not a feature branch.`); | ||
console.log( | ||
'No issue could be linked to this PR, so no labels were copied', | ||
); | ||
process.exit(0); | ||
} | ||
} | ||
|
||
async function updateLabels(octokit: InstanceType<typeof GitHub>, issueNumber: string): Promise<void> { | ||
interface ILabel { | ||
name: string; | ||
}; | ||
|
||
const owner = context.repo.owner; | ||
const repo = context.repo.repo; | ||
|
||
const issue = await octokit.rest.issues.get({ | ||
owner: owner, | ||
repo: repo, | ||
issue_number: Number(issueNumber), | ||
}); | ||
|
||
const getNameFromLabel = (label: ILabel): string => label.name | ||
|
||
const issueLabels = issue.data.labels.map(label => getNameFromLabel(label as ILabel)); | ||
|
||
const prNumber = context.payload.number; | ||
|
||
const pr = await octokit.rest.issues.get({ | ||
owner: owner, | ||
repo: repo, | ||
issue_number: prNumber, | ||
}); | ||
|
||
const startingPRLabels = pr.data.labels.map(label => getNameFromLabel(label as ILabel)); | ||
|
||
const dedupedFinalPRLabels = [ | ||
...new Set([...startingPRLabels, ...issueLabels]), | ||
]; | ||
|
||
const hasIssueAdditionalLabels = !sortedArrayEqual( | ||
startingPRLabels, | ||
dedupedFinalPRLabels, | ||
); | ||
if (hasIssueAdditionalLabels) { | ||
await octokit.rest.issues.update({ | ||
owner, | ||
repo, | ||
issue_number: prNumber, | ||
labels: dedupedFinalPRLabels, | ||
}); | ||
} | ||
} | ||
|
||
function getIssueNumberFromBranchName(branchName: string): string { | ||
console.log('Checking if the branch name references an issue...'); | ||
|
||
let issueNumber: string; | ||
if (branchName.split('/').length > 1) { | ||
issueNumber = branchName.split('/')[1].split('-')[0]; | ||
} else { | ||
issueNumber = branchName.split('-')[0]; | ||
} | ||
|
||
console.log(`Found issue number ${issueNumber} in branch name.`); | ||
|
||
return issueNumber; | ||
} | ||
|
||
function sortedArrayEqual(array1: string[], array2: string[]): boolean { | ||
const lengthsAreEqual = array1.length === array2.length; | ||
const everyElementMatchesByIndex = array1.every( | ||
(value: string, index: number): boolean => value === array2[index], | ||
); | ||
|
||
return lengthsAreEqual && everyElementMatchesByIndex; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
name: Label PR | ||
|
||
on: | ||
pull_request: | ||
types: [assigned, opened, edited, synchronize, reopened] | ||
|
||
jobs: | ||
label-pr: | ||
runs-on: ubuntu-latest | ||
permissions: | ||
issues: write | ||
pull-requests: write | ||
|
||
steps: | ||
- name: Checkout code | ||
uses: actions/checkout@v3 | ||
|
||
- name: Use Node.js | ||
uses: actions/setup-node@v3 | ||
with: | ||
node-version: '16' | ||
|
||
- name: Install Yarn | ||
run: npm install -g yarn | ||
|
||
- name: Install dependencies | ||
run: yarn | ||
|
||
- name: Run PR labelling script | ||
run: npm run label-prs | ||
env: | ||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
#!/usr/bin/env sh | ||
. "$(dirname -- "$0")/_/husky.sh" | ||
|
||
yarn validate-branch-name |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
const BRANCH_REGEX = | ||
/^(main|develop|(ci|chore|docs|feat|feature|fix|perf|refactor|revert|style)\/\d*(?:[-](?![-])\w*)*|Version-v\d+\.\d+\.\d+)$/; | ||
|
||
const ERROR_MSG = | ||
'This branch name does not follow our conventions.' + | ||
'\n' + | ||
'Rename it with "git branch -m <current-name> <new-name>"' + | ||
'\n' + | ||
'Here are some example branch names that are accepted: "fix/123-description", "feat/123-longer-description", "feature/123", "main", "develop", "Version-v10.24.2".'; | ||
|
||
module.exports = { pattern: BRANCH_REGEX, errorMsg: ERROR_MSG }; |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.