1+ name : ' Check Commit Messages'
2+
3+ on :
4+ pull_request :
5+ types : [opened, synchronize, reopened]
6+
7+ jobs :
8+ check-commits :
9+ runs-on : ubuntu-latest
10+ steps :
11+ - uses : actions/checkout@v4
12+ with :
13+ fetch-depth : 0
14+
15+ - name : Check Commits and Update Labels
16+ uses : actions/github-script@v7
17+ with :
18+ github-token : ${{ secrets.GITHUB_TOKEN }}
19+ script : |
20+ const { issue: { number: issue_number }, repo: { owner, repo } } = context;
21+
22+ // Get commits in the PR
23+ const { data: commits } = await github.rest.pulls.listCommits({
24+ owner,
25+ repo,
26+ pull_number: issue_number
27+ });
28+
29+ // Define valid types and their corresponding labels
30+ const typeToLabel = {
31+ 'feat': 'type: new feature',
32+ 'fix': 'type: bug',
33+ 'docs': 'type: doc',
34+ 'test': 'type: test',
35+ 'chore': 'type: chore',
36+ 'enhance': 'type: enhancement',
37+ 'amend': 'type: amend',
38+ 'style': 'type: chore',
39+ 'refactor': 'type: enhancement',
40+ 'perf': 'type: enhancement',
41+ 'build': 'type: chore'
42+ };
43+
44+ const validTypes = Object.keys(typeToLabel);
45+ const pattern = new RegExp(`^(${validTypes.join('|')})(\(.+\))?!?: .+`);
46+
47+ let isValid = true;
48+ let invalidCommits = [];
49+
50+ // Check each commit
51+ for (const commit of commits) {
52+ const message = commit.commit.message.split('\n')[0]; // Get first line
53+ if (!pattern.test(message)) {
54+ isValid = false;
55+ invalidCommits.push(message);
56+ }
57+ }
58+
59+ // Get the first valid commit to determine the type
60+ const firstValidCommit = commits.find(commit => {
61+ const message = commit.commit.message.split('\n')[0];
62+ return pattern.test(message);
63+ });
64+
65+ // Remove existing type labels
66+ const { data: currentLabels } = await github.rest.issues.listLabelsOnIssue({
67+ owner,
68+ repo,
69+ issue_number
70+ });
71+
72+ for (const label of currentLabels) {
73+ if (label.name.startsWith('type:')) {
74+ await github.rest.issues.removeLabel({
75+ owner,
76+ repo,
77+ issue_number,
78+ name: label.name
79+ });
80+ }
81+ }
82+
83+ if (isValid && firstValidCommit) {
84+ // Extract type from first valid commit
85+ const type = firstValidCommit.commit.message.match(pattern)[1];
86+ const labelToAdd = typeToLabel[type];
87+
88+ // Add appropriate label
89+ await github.rest.issues.addLabels({
90+ owner,
91+ repo,
92+ issue_number,
93+ labels: [labelToAdd]
94+ });
95+
96+ // Add success comment
97+ await github.rest.issues.createComment({
98+ owner,
99+ repo,
100+ issue_number,
101+ body: '✅ All commit messages follow the conventional format.'
102+ });
103+ } else {
104+ // Create error comment
105+ const errorMessage = `
106+ ❌ Some commit messages don't follow the conventional format.
107+
108+ Invalid commits:
109+ ${invalidCommits.map(msg => `- \`${msg}\``).join('\n')}
110+
111+ Please update your commits to follow the format:
112+ \`\`\`
113+ type: description
114+
115+ Valid types:
116+ - feat: New feature (type: new feature)
117+ - fix: Bug fix (type: bug)
118+ - docs: Documentation changes (type: doc)
119+ - test: Adding/updating tests (type: test)
120+ - chore: Maintenance tasks (type: chore)
121+ - enhance: Enhancement to existing features (type: enhancement)
122+ - amend: Small amendments (type: amend)
123+ - style: Code formatting (type: chore)
124+ - refactor: Code restructuring (type: enhancement)
125+ - perf: Performance improvements (type: enhancement)
126+ - build: Build system changes (type: chore)
127+ \`\`\`
128+
129+ You can update your commit messages using:
130+ \`\`\`bash
131+ git rebase -i HEAD~n # where n is the number of commits to edit
132+ # Change 'pick' to 'reword' for commits you want to edit
133+ \`\`\`
134+ `;
135+
136+ await github.rest.issues.createComment({
137+ owner,
138+ repo,
139+ issue_number,
140+ body: errorMessage
141+ });
142+ }
0 commit comments