Skip to content

Commit 62e155f

Browse files
authored
chore(ci): auto-label PR on semantic title (#403)
1 parent a08bf76 commit 62e155f

File tree

2 files changed

+112
-0
lines changed

2 files changed

+112
-0
lines changed
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
name: Label PR based on title
2+
3+
# pull_request_target event sends an admin GH token to forks
4+
# this however depends on another workflow so it all runs within the base repo safely
5+
# "Record PR number" workflow safely captures PR title and number
6+
# This workflow uses this information to label the PR based on its semantic title
7+
on:
8+
workflow_run:
9+
workflows: ["Record PR number"]
10+
types:
11+
- completed
12+
13+
jobs:
14+
label_pr:
15+
runs-on: ubuntu-latest
16+
# Guardrails to only ever run if PR recording workflow was indeed
17+
# run in a PR event and ran successfully
18+
if: >
19+
${{ github.event.workflow_run.event == 'pull_request' &&
20+
github.event.workflow_run.conclusion == 'success' }}
21+
steps:
22+
- name: 'Download artifact'
23+
uses: actions/github-script@v5
24+
# For security, we only download artifacts tied to the successful PR recording workflow
25+
with:
26+
script: |
27+
const fs = require('fs');
28+
29+
const artifacts = await github.rest.actions.listWorkflowRunArtifacts({
30+
owner: context.repo.owner,
31+
repo: context.repo.repo,
32+
run_id: ${{github.event.workflow_run.id }},
33+
});
34+
35+
const matchArtifact = artifacts.data.artifacts.filter(artifact => artifact.name == "pr")[0];
36+
37+
const artifact = await github.rest.actions.downloadArtifact({
38+
owner: context.repo.owner,
39+
repo: context.repo.repo,
40+
artifact_id: matchArtifact.id,
41+
archive_format: 'zip',
42+
});
43+
44+
fs.writeFileSync('${{github.workspace}}/pr.zip', Buffer.from(artifact.data));
45+
# NodeJS standard library doesn't provide ZIP capabilities; use system `unzip` command instead
46+
- run: unzip pr.zip
47+
48+
- name: 'Label PR based on title'
49+
uses: actions/github-script@v5
50+
with:
51+
github-token: ${{ secrets.GITHUB_TOKEN }}
52+
# This safely runs in our base repo, not on fork
53+
# thus allowing us to provide a write access token to label based on PR title
54+
# and label PR based on semantic title accordingly
55+
script: |
56+
const fs = require('fs');
57+
const pr_number = Number(fs.readFileSync('./number'));
58+
const pr_title = fs.readFileSync('./title', 'utf-8').trim();
59+
60+
const FEAT_REGEX = /feat(\((\w+)\))?(\:.+)/
61+
const BUG_REGEX = /(fix|bug)(\((\w+)\))?(\:.+)/
62+
const DOCS_REGEX = /(docs|doc)(\((\w+)\))?(\:.+)/
63+
const CHORE_REGEX = /(chore)(\((\w+)\))?(\:.+)/
64+
const DEPRECATED_REGEX = /(deprecated)(\((\w+)\))?(\:.+)/
65+
const REFACTOR_REGEX = /(refactor)(\((\w+)\))?(\:.+)/
66+
67+
const labels = {
68+
"feature": FEAT_REGEX,
69+
"bug": BUG_REGEX,
70+
"documentation": DOCS_REGEX,
71+
"internal": CHORE_REGEX,
72+
"enhancement": REFACTOR_REGEX,
73+
"deprecated": DEPRECATED_REGEX,
74+
}
75+
76+
for (const label in labels) {
77+
const matcher = new RegExp(labels[label])
78+
const isMatch = matcher.exec(pr_title)
79+
if (isMatch != null) {
80+
console.info(`Auto-labeling PR ${pr_number} with ${label}`)
81+
82+
await github.rest.issues.addLabels({
83+
issue_number: pr_number,
84+
owner: context.repo.owner,
85+
repo: context.repo.repo,
86+
labels: [label]
87+
})
88+
89+
break
90+
}
91+
}

.github/workflows/record_pr.yml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
name: Record PR number
2+
3+
on:
4+
pull_request:
5+
types: [opened, edited]
6+
7+
jobs:
8+
record:
9+
runs-on: ubuntu-latest
10+
11+
steps:
12+
- uses: actions/checkout@v2
13+
- name: Save PR number
14+
run: |
15+
mkdir -p ./pr
16+
echo ${{ github.event.number }} > ./pr/number
17+
echo "${{ github.event.pull_request.title }}" > ./pr/title
18+
- uses: actions/upload-artifact@v2
19+
with:
20+
name: pr
21+
path: pr/

0 commit comments

Comments
 (0)