Skip to content

Commit ca336bb

Browse files
authored
Add PR title and labels workflows (#60)
Signed-off-by: Gray Campbell <[email protected]>
1 parent 52d53d7 commit ca336bb

File tree

4 files changed

+141
-0
lines changed

4 files changed

+141
-0
lines changed

.github/scripts/validate-pr-title.js

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
const nlp = require('compromise')
2+
const title = process.env.PR_TITLE || ''
3+
4+
let isValidTitle = true
5+
6+
function logSuccess(message) {
7+
console.log(`✅ ${message}`)
8+
}
9+
10+
function logFailure(message) {
11+
isValidTitle = false
12+
console.error(`❌ ${message}`)
13+
}
14+
15+
function capitalized(string) {
16+
if (!string) return '';
17+
return string[0].toUpperCase() + string.substring(1);
18+
}
19+
20+
// Rule 1: PR title must not be empty
21+
if (title) {
22+
logSuccess(`PR title is not empty`)
23+
} else {
24+
logFailure(`PR title must not be empty`)
25+
}
26+
27+
// Rule 2: PR title must be 72 characters or less
28+
if (title.length <= 72) {
29+
logSuccess(`PR title is ${title.length} characters`)
30+
} else {
31+
logFailure(`PR title must be 72 characters or less (currently ${title.length} characters)`)
32+
}
33+
34+
// Rule 3: PR title must begin with a capital letter
35+
if (/^[A-Z]/.test(title)) {
36+
logSuccess(`PR title begins with a capital letter`)
37+
} else {
38+
logFailure('PR title must begin with a capital letter')
39+
}
40+
41+
// Rule 4: PR title must end with a letter or number
42+
if (/[A-Za-z0-9]$/.test(title)) {
43+
logSuccess(`PR title ends with a letter or number`)
44+
} else {
45+
logFailure('PR title must end with a letter or number')
46+
}
47+
48+
// Rule 5: PR title must be written in the imperative
49+
const firstWord = title.split(' ')[0]
50+
const firstWordLowercased = firstWord.toLowerCase()
51+
const firstWordCapitalized = capitalized(firstWord)
52+
const firstWordAsImperativeVerb = nlp(firstWord).verbs().toInfinitive().out('text')
53+
const firstWordAsImperativeVerbLowercased = firstWordAsImperativeVerb.toLowerCase()
54+
const firstWordAsImperativeVerbCapitalized = capitalized(firstWordAsImperativeVerb)
55+
56+
if (firstWordLowercased === firstWordAsImperativeVerbLowercased) {
57+
logSuccess(`PR title is written in the imperative`)
58+
} else if (firstWordAsImperativeVerb) {
59+
logFailure(`PR title must be written in the imperative ("${firstWordAsImperativeVerbCapitalized}" instead of "${firstWordCapitalized}")`)
60+
} else {
61+
logFailure(`PR title must begin with a verb and be written in the imperative`)
62+
}
63+
64+
if (!isValidTitle) {
65+
process.exit(1)
66+
}

.github/workflows/ci.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ on:
66
branches:
77
- main
88
pull_request:
9+
types: [opened, reopened, synchronize]
910
branches:
1011
- '*'
1112

.github/workflows/pr-labels.yml

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
name: PR Labels
2+
3+
on:
4+
pull_request:
5+
types: [opened, labeled, unlabeled, reopened, synchronize]
6+
7+
permissions:
8+
contents: read
9+
pull-requests: read
10+
11+
jobs:
12+
check-for-required-labels:
13+
name: Check For Required Labels
14+
runs-on: ubuntu-latest
15+
16+
steps:
17+
- name: Checkout source code
18+
uses: actions/checkout@v4
19+
20+
- name: Validate PR has required labels
21+
env:
22+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
23+
run: |
24+
PR_NUMBER=${{ github.event.pull_request.number }}
25+
REPO=${{ github.repository }}
26+
27+
REQUIRED_LABELS=("bug" "ci/cd" "documentation" "enhancement" "formatting" "refactoring" "testing")
28+
LABELS=$(gh pr view "$PR_NUMBER" --repo "$REPO" --json labels --jq '.labels[].name')
29+
30+
echo "PR labels:"
31+
echo "$LABELS"
32+
33+
for required in "${REQUIRED_LABELS[@]}"; do
34+
if echo "$LABELS" | grep -q "^$required$"; then
35+
echo "✅ Found required label: $required"
36+
exit 0
37+
fi
38+
done
39+
40+
echo "❌ PR is missing a required label."
41+
echo "At least one of the following labels is required:"
42+
printf '%s\n' "${REQUIRED_LABELS[@]}"
43+
exit 1
44+
shell: bash

.github/workflows/pr-title.yml

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
name: PR Title
2+
3+
on:
4+
pull_request:
5+
types: [opened, edited, reopened, synchronize]
6+
7+
permissions:
8+
contents: read
9+
pull-requests: read
10+
11+
jobs:
12+
validate-pr-title:
13+
name: Validate PR Title
14+
runs-on: ubuntu-latest
15+
steps:
16+
- name: Checkout source code
17+
uses: actions/checkout@v4
18+
19+
- name: Set up Node.js
20+
uses: actions/setup-node@v4
21+
with:
22+
node-version: latest
23+
24+
- name: Install dependencies
25+
run: npm install compromise
26+
27+
- name: Validate PR title
28+
run: node .github/scripts/validate-pr-title.js
29+
env:
30+
PR_TITLE: ${{ github.event.pull_request.title }}

0 commit comments

Comments
 (0)