Skip to content

Commit a6087e0

Browse files
authored
ci: add conventional commits enforcement (#483)
1 parent 71ef072 commit a6087e0

File tree

2 files changed

+135
-0
lines changed

2 files changed

+135
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
"use strict";
2+
3+
const fs = require("fs");
4+
5+
const TITLE_PATTERN =
6+
/^(?<prefix>[^:!(]+)(?<package>\([^)]+\))?(?<breaking>[!])?:.+$/;
7+
const RELEASE_AS_DIRECTIVE = /^\s*Release-As:/im;
8+
const BREAKING_CHANGE_DIRECTIVE = /^\s*BREAKING[ \t]+CHANGE:/im;
9+
10+
const ALLOWED_CONVENTIONAL_COMMIT_PREFIXES = [
11+
"revert",
12+
"feat",
13+
"fix",
14+
"ci",
15+
"docs",
16+
"chore",
17+
"style",
18+
"test",
19+
"refactor",
20+
];
21+
22+
const object = process.argv[2];
23+
const payload = JSON.parse(fs.readFileSync(process.stdin.fd, "utf-8"));
24+
25+
let validate = [];
26+
27+
if (object === "pr") {
28+
validate.push({
29+
title: payload.pull_request.title,
30+
content: payload.pull_request.body,
31+
});
32+
} else if (object === "push") {
33+
validate.push(
34+
...payload.commits
35+
.map((commit) => ({
36+
title: commit.message.split("\n")[0],
37+
content: commit.message,
38+
}))
39+
.filter(({ title }) => !title.startsWith("Merge branch ") && !title.startsWith("Revert ")),
40+
);
41+
} else {
42+
console.error(
43+
`Unknown object for first argument "${object}", use 'pr' or 'push'.`,
44+
);
45+
process.exit(0);
46+
}
47+
48+
let failed = false;
49+
50+
validate.forEach((payload) => {
51+
if (payload.title) {
52+
const match = payload.title.match(TITLE_PATTERN);
53+
if (!match) {
54+
return
55+
}
56+
57+
const { groups } = match
58+
59+
if (groups) {
60+
if (
61+
!ALLOWED_CONVENTIONAL_COMMIT_PREFIXES.find(
62+
(prefix) => prefix === groups.prefix,
63+
)
64+
) {
65+
console.error(
66+
`PR (or a commit in it) is using a disallowed conventional commit prefix ("${groups.prefix}"). Only ${ALLOWED_CONVENTIONAL_COMMIT_PREFIXES.join(", ")} are allowed. Make sure the prefix is lowercase!`,
67+
);
68+
failed = true;
69+
}
70+
} else {
71+
console.error(
72+
"PR or commit title must match conventional commit structure.",
73+
);
74+
failed = true;
75+
}
76+
}
77+
78+
if (payload.content) {
79+
if (payload.content.match(RELEASE_AS_DIRECTIVE)) {
80+
console.error(
81+
"PR descriptions or commit messages must not contain Release-As conventional commit directives.",
82+
);
83+
failed = true;
84+
}
85+
}
86+
});
87+
88+
if (failed) {
89+
process.exit(1);
90+
}
91+
92+
process.exit(0);
+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
name: Check pull requests
2+
3+
on:
4+
push:
5+
branches-ignore: # Run the checks on all branches but the protected ones
6+
- master
7+
- release/*
8+
9+
pull_request_target:
10+
branches:
11+
- master
12+
- release/*
13+
types:
14+
- opened
15+
- edited
16+
- reopened
17+
- ready_for_review
18+
19+
jobs:
20+
check-conventional-commits:
21+
runs-on: ubuntu-latest
22+
23+
steps:
24+
- uses: actions/checkout@v4
25+
with:
26+
sparse-checkout: |
27+
.github
28+
29+
- if: ${{ github.event_name == 'pull_request_target' }}
30+
run: |
31+
set -ex
32+
33+
node .github/workflows/conventional-commits-lint.js pr <<EOF
34+
${{ toJSON(github.event) }}
35+
EOF
36+
37+
- if: ${{ github.event_name == 'push' }}
38+
run: |
39+
set -ex
40+
41+
node .github/workflows/conventional-commits-lint.js push <<EOF
42+
${{ toJSON(github.event) }}
43+
EOF

0 commit comments

Comments
 (0)