Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings
This repository was archived by the owner on Feb 28, 2020. It is now read-only.

Commit 6663a6a

Browse files
feat: 第一版本的实现
实现了以下检查: MERGE: 检测到时Merge commit的话,因为是git自动生成的,跳过检查 EMPTY_MESSAGE: 空 message EMPTY_HEADER: 空 header BAD_HEADER_FORMAT: header 格式错误 WRONG_TYPE: type 错误 BODY_MISSING: 缺少 body (仅当 BODY_REQUIRED 为 True 时) NO_BLANK_LINE_BEFORE_BODY: body 前缺少空行 LINE_OVERLONG 行过长
1 parent b11f4d5 commit 6663a6a

File tree

1 file changed

+195
-0
lines changed

1 file changed

+195
-0
lines changed

‎validate.py

Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
#!/usr/bin/python3
2+
# -*- coding:utf-8 -*-
3+
4+
import os
5+
import re
6+
import sys
7+
from enum import Enum
8+
9+
# constant
10+
LINE_LIMIT = 100
11+
BODY_REQUIRED = False
12+
13+
TYPE_LIST = [
14+
'feat', # 新功能(feature)
15+
'fix', # 修补bug
16+
'docs', # 文档(documentation)
17+
'style', # 格式(不影响代码运行的变动)
18+
'refactor', # 重构(既不是新增功能,也不是修改bug的代码变动)
19+
'perf', # 提升性能(performance)
20+
'test', # 增加测试
21+
'chore', # 构建过程或辅助工具的变动'
22+
'revert' # 撤销以前的 commit
23+
]
24+
25+
# Error Enum
26+
ErrorEnum = Enum('ErrorEnum',
27+
['VALIDATED',
28+
'MERGE',
29+
'ARG_MISSING',
30+
'FILE_MISSING',
31+
'EMPTY_MESSAGE',
32+
'EMPTY_HEADER',
33+
'BAD_HEADER_FORMAT',
34+
'WRONG_TYPE',
35+
'BODY_MISSING',
36+
'NO_BLANK_LINE_BEFORE_BODY',
37+
'LINE_OVERLONG'],
38+
module=__name__)
39+
40+
# error message
41+
ERROR_MESSAGES = {
42+
# Normal case
43+
ErrorEnum.VALIDATED: '{errorname}:commit message 符合规范。',
44+
ErrorEnum.MERGE: '{errorname}:检测到 merge commit,跳过规范检查。',
45+
# File error
46+
ErrorEnum.ARG_MISSING: '错误 {errorname}:缺少 commit message 文件参数。',
47+
ErrorEnum.FILE_MISSING: '错误 {errorname}:文件 {filepath} 不存在。',
48+
# Empty content
49+
ErrorEnum.EMPTY_MESSAGE: '错误 {errorname}:commit message 没有内容或只有空白字符。',
50+
ErrorEnum.EMPTY_HEADER: '错误 {errorname}:header (首行) 没有内容或只有空白字符。',
51+
# Header error
52+
ErrorEnum.BAD_HEADER_FORMAT: '错误 {errorname}:header (首行) 不符合规范:\n{header}\n如果检查没有发现错误,请确认是否使用了中文冒号,以及冒号后面漏了空格。',
53+
ErrorEnum.WRONG_TYPE: '错误 {errorname}:{type} 不是以下关键字之一:\n%s' % (', '.join(TYPE_LIST)),
54+
# Body error
55+
ErrorEnum.BODY_MISSING: '错误 {errorname}:body 没有内容或只有空白字符。', # 仅 BODY_REQUIRED 为 True时生效
56+
ErrorEnum.NO_BLANK_LINE_BEFORE_BODY: '错误 {errorname}:header 和 body 之间没有空一行。',
57+
# Common error
58+
ErrorEnum.LINE_OVERLONG: '错误 {errorname}:单行内容长度为{length},超过了%d个字符:\n{line}' % (LINE_LIMIT)
59+
}
60+
61+
NON_FORMAT_ERROR = (
62+
ErrorEnum.VALIDATED,
63+
ErrorEnum.MERGE,
64+
ErrorEnum.ARG_MISSING,
65+
ErrorEnum.FILE_MISSING
66+
)
67+
68+
RULE_MESSAGE = '''
69+
Commit message 的格式要求如下:
70+
<type>(<scope>): <subject>
71+
// 空一行
72+
<body>
73+
// 空一行
74+
<footer>
75+
76+
其中 (<scope>) <body> 和 <footer> 可选
77+
<type> 必须是 %s 中的一个
78+
更详细的格式要求说明,请参考 http://192.168.19.127:3000/jayce/git-hook-commit-msg''' % (', '.join(TYPE_LIST))
79+
80+
MERGE_PATTEN = r'^Merge '
81+
# 弱匹配,只检查基本的格式,各个部分允许为空,留到match.group(x)部分检查,以提供更详细的报错信息
82+
HEADER_PATTEN = r'^((fixup! |squash! )?(\w+)(?:\(([^\)\s]+)\))?: (.+))(?:\n|$)'
83+
84+
85+
# 这三种header需要在原header上添加关键字,会使原本不超字数的header超出字数
86+
# SPECIAL_HEADER_PATTEN = r'^(fixup! |squash! |revert:)'
87+
88+
89+
def print_error_msg(state, **kwargs):
90+
kwargs['errorname'] = state.name
91+
print(ERROR_MESSAGES[state].format(**kwargs))
92+
if state not in NON_FORMAT_ERROR:
93+
print(RULE_MESSAGE)
94+
95+
96+
def check_header(header):
97+
if not header.strip():
98+
print_error_msg(ErrorEnum.EMPTY_HEADER)
99+
return False
100+
101+
match = re.match(HEADER_PATTEN, header)
102+
if not match:
103+
print_error_msg(ErrorEnum.BAD_HEADER_FORMAT, header=header)
104+
return False
105+
106+
fixup_or_squash = bool(match.group(1))
107+
type_ = match.group(3)
108+
# scope = match.group(4) # TODO: 根据配置对scope检查
109+
# subject = bool(match.group(5)) # TODO: 根据规则对subject检查
110+
111+
if type_ not in TYPE_LIST:
112+
print_error_msg(ErrorEnum.WRONG_TYPE, type=type_)
113+
114+
# print(match.group(0, 1, 2, 3, 4, 5))
115+
116+
length = len(header)
117+
if length > LINE_LIMIT and not (fixup_or_squash or type_ == 'revert'):
118+
print_error_msg(ErrorEnum.LINE_OVERLONG, length=length, line=header)
119+
return False
120+
121+
return True
122+
123+
124+
def check_body(body):
125+
# body missing
126+
if not body.strip():
127+
if BODY_REQUIRED:
128+
print_error_msg(ErrorEnum.BODY_MISSING)
129+
return False
130+
else:
131+
return True
132+
133+
if body.split('\n', maxsplit=1)[0]:
134+
print_error_msg(ErrorEnum.NO_BLANK_LINE_BEFORE_BODY)
135+
return False
136+
137+
for line in body.splitlines():
138+
length = len(line)
139+
if length > LINE_LIMIT:
140+
print_error_msg(ErrorEnum.LINE_OVERLONG, length=length, line=line)
141+
return False
142+
143+
return True
144+
145+
146+
def validate_commit_message(message):
147+
"""
148+
Validate the git commit message.
149+
:param message: the commit message to be validated.
150+
:return: True if the message meet the rule, False otherwise.
151+
"""
152+
if not message.strip():
153+
print_error_msg(ErrorEnum.EMPTY_MESSAGE)
154+
return False
155+
156+
if re.match(MERGE_PATTEN, message):
157+
print_error_msg(ErrorEnum.MERGE)
158+
return True
159+
160+
header, body = message.split('\n', maxsplit=1)
161+
162+
if not check_header(header):
163+
return False
164+
165+
if not check_body(body):
166+
return False
167+
168+
print_error_msg(ErrorEnum.VALIDATED)
169+
return True
170+
171+
172+
def main():
173+
"""
174+
Main function
175+
"""
176+
file_path = sys.argv[1] if len(sys.argv) > 1 else None
177+
if not file_path:
178+
print_error_msg(ErrorEnum.ARG_MISSING)
179+
sys.exit(1)
180+
181+
if not os.path.exists(file_path):
182+
print_error_msg(ErrorEnum.FILE_MISSING, filepath=file_path)
183+
sys.exit(1)
184+
185+
with open(file_path, 'r', encoding='utf-8') as message_file:
186+
commit_msg = message_file.read()
187+
188+
if not validate_commit_message(commit_msg):
189+
sys.exit(1)
190+
191+
sys.exit(0)
192+
193+
194+
if __name__ == "__main__":
195+
main()

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /