-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathvalidate_commit_header.py
More file actions
executable file
·77 lines (57 loc) · 1.6 KB
/
validate_commit_header.py
File metadata and controls
executable file
·77 lines (57 loc) · 1.6 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
#!/usr/bin/env python3
"""Validate an opinionated Conventional Commit header."""
import re
import sys
TYPES = (
"feat",
"fix",
"refactor",
"perf",
"style",
"test",
"docs",
"build",
"ops",
"chore",
)
HEADER_RE = re.compile(
rf"^({'|'.join(TYPES)})(\(([^()\n]+)\))?(!)?: ([^\n.].*[^\n.]|[^\n.])$"
)
MERGE_RE = re.compile(r"^Merge branch '.+'$")
REVERT_RE = re.compile(r'^Revert ".+"$')
def validate(header: str) -> list[str]:
if MERGE_RE.match(header) or REVERT_RE.match(header):
return []
match = HEADER_RE.match(header)
errors: list[str] = []
if not match:
errors.append(
"header must be '<type>(<optional scope>)<optional !>: <description>'"
)
return errors
scope = match.group(3)
description = match.group(5)
if scope and re.search(r"(#\d+|[A-Z]+-\d+)", scope):
errors.append("scope must not be an issue identifier")
if description[0].isupper():
errors.append("description must start with a lowercase letter")
if description.endswith("."):
errors.append("description must not end with a period")
return errors
def main() -> int:
if len(sys.argv) > 1:
header = " ".join(sys.argv[1:]).strip()
else:
header = sys.stdin.read().strip()
if not header:
print("missing commit header", file=sys.stderr)
return 2
errors = validate(header)
if errors:
for error in errors:
print(error, file=sys.stderr)
return 1
print("valid")
return 0
if __name__ == "__main__":
raise SystemExit(main())