-
Notifications
You must be signed in to change notification settings - Fork 1
191 lines (167 loc) · 6.88 KB
/
Copy pathpr-conventional-commits.yml
File metadata and controls
191 lines (167 loc) · 6.88 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
name: PR Conventional Commits Check
on:
pull_request:
branches: [main]
types: [opened, synchronize, reopened, edited]
jobs:
validate-commits:
runs-on: ubuntu-latest
# Skip validation for Release Please PRs (auto-generated)
if: |
!startsWith(github.event.pull_request.title, 'chore(main): release') &&
!contains(github.event.pull_request.labels.*.name, 'autorelease: pending')
permissions:
contents: read
pull-requests: write
statuses: write
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Cache npm dependencies
uses: actions/cache@v4
with:
path: ~/.npm
key: ${{ runner.os }}-commitlint-${{ hashFiles('.github/workflows/pr-conventional-commits.yml') }}
restore-keys: |
${{ runner.os }}-commitlint-
- name: Install Commitlint
run: npm install --save-dev @commitlint/config-conventional @commitlint/cli
- name: Create Commitlint Config
run: |
cat > commitlint.config.js << 'EOF'
module.exports = {
extends: ['@commitlint/config-conventional'],
rules: {
'type-enum': [2, 'always', [
'build', 'chore', 'ci', 'docs', 'feat', 'fix',
'perf', 'refactor', 'revert', 'style', 'test'
]],
'subject-case': [2, 'never', ['start-case', 'pascal-case', 'upper-case']],
'subject-max-length': [2, 'always', 100],
'body-max-line-length': [2, 'always', 100],
'footer-max-line-length': [2, 'always', 100]
}
}
EOF
- name: Check PR Title
id: check_title
env:
PR_TITLE: ${{ github.event.pull_request.title }}
run: |
echo "Checking PR title: $PR_TITLE"
if echo "$PR_TITLE" | npx commitlint; then
echo "title_valid=true" >> "$GITHUB_OUTPUT"
else
echo "title_valid=false" >> "$GITHUB_OUTPUT"
fi
- name: Check PR Commits
id: check_commits
run: |
COMMITS=$(git rev-list --reverse ${{ github.event.pull_request.base.sha }}..${{ github.event.pull_request.head.sha }})
COMMIT_COUNT=$(echo "$COMMITS" | wc -l)
VALID=0
INVALID=""
for commit in $COMMITS; do
MSG=$(git log --format=%B -n 1 "$commit" | head -n 1)
if echo "$MSG" | npx commitlint 2>/dev/null; then
VALID=$((VALID + 1))
else
INVALID="$INVALID\n- \`${commit:0:7}\`: $MSG"
fi
done
echo "commit_count=$COMMIT_COUNT" >> "$GITHUB_OUTPUT"
echo "valid_commits=$VALID" >> "$GITHUB_OUTPUT"
if [ "$VALID" -eq "$COMMIT_COUNT" ]; then
echo "commits_valid=true" >> "$GITHUB_OUTPUT"
else
echo "commits_valid=false" >> "$GITHUB_OUTPUT"
{
echo "invalid_commits<<EOF"
echo -e "$INVALID"
echo "EOF"
} >> "$GITHUB_OUTPUT"
fi
- name: Update PR comment
if: always()
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# Pass values via env (data, not code) so special chars like
# backticks in commit messages are never evaluated by the shell.
TITLE_VALID: ${{ steps.check_title.outputs.title_valid }}
COMMITS_VALID: ${{ steps.check_commits.outputs.commits_valid }}
COMMIT_COUNT: ${{ steps.check_commits.outputs.commit_count }}
INVALID_COMMITS: ${{ steps.check_commits.outputs.invalid_commits }}
PR_NUMBER: ${{ github.event.pull_request.number }}
REPO: ${{ github.repository }}
run: |
BODY="## Conventional Commits Validation\n\n"
if [ "$TITLE_VALID" = "true" ]; then
BODY="${BODY}**PR Title**: valid\n"
else
BODY="${BODY}**PR Title**: invalid — please use \`type(scope): description\`\n"
fi
if [ "$COMMITS_VALID" = "true" ]; then
BODY="${BODY}**Commits**: all ${COMMIT_COUNT} follow conventional format\n"
else
BODY="${BODY}**Commits**: some do not follow conventional format\n"
BODY="${BODY}${INVALID_COMMITS}\n"
fi
if [ "$TITLE_VALID" != "true" ] || \
[ "$COMMITS_VALID" != "true" ]; then
BODY="${BODY}\n### Format\n\n\`type(scope): description\`\n\n"
BODY="${BODY}**Types**: \`feat\`, \`fix\`, \`docs\`, \`style\`, \`refactor\`, \`test\`, \`chore\`, \`ci\`, \`build\`, \`perf\`, \`revert\`\n"
fi
COMMENT_ID=$(gh pr view "$PR_NUMBER" --json comments \
--jq '.comments[] | select(.author.login == "github-actions[bot]" and (.body | test("Conventional Commits Validation"))) | .id' \
| head -n 1)
if [ -n "$COMMENT_ID" ]; then
gh api -X PATCH "repos/${REPO}/issues/comments/$COMMENT_ID" \
-f body="$(echo -e "$BODY")"
else
gh pr comment "$PR_NUMBER" --body "$(echo -e "$BODY")"
fi
- name: Set commit status
if: always()
run: |
SHA="${{ github.event.pull_request.head.sha }}"
title_state="success"
title_desc="PR title follows conventional commits"
if [ "${{ steps.check_title.outputs.title_valid }}" != "true" ]; then
title_state="failure"
title_desc="PR title does not follow conventional commits"
fi
commits_state="success"
commits_desc="All commits follow conventional commits"
if [ "${{ steps.check_commits.outputs.commits_valid }}" != "true" ]; then
commits_state="failure"
commits_desc="Some commits do not follow conventional commits"
fi
gh api -X POST "repos/${{ github.repository }}/statuses/$SHA" \
-f state="$title_state" \
-f context="conventional-commits/title" \
-f description="$title_desc"
gh api -X POST "repos/${{ github.repository }}/statuses/$SHA" \
-f state="$commits_state" \
-f context="conventional-commits/commits" \
-f description="$commits_desc"
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Label PR
uses: bcoe/conventional-release-labels@v1
with:
token: ${{ secrets.GITHUB_TOKEN }}
- name: Final validation
run: |
if [ "${{ steps.check_title.outputs.title_valid }}" = "true" ] && \
[ "${{ steps.check_commits.outputs.commits_valid }}" = "true" ]; then
echo "All conventional commits validations passed"
else
echo "Conventional commits validation failed"
exit 1
fi