build: 安装 commit-msg 规范钩子(ai-code-style-kit v1.0)
Co-authored-by: GLM-5.3
This commit is contained in:
1 parent
0fbb023375
commit
9a61e69f2f
1 file changed
+112
Executable
+112
@@ -0,0 +1,112 @@
|
||||
#!/bin/sh
|
||||
# commit-msg hook — ai-code-style-kit
|
||||
# 规则源:skills/commit-style/SKILL.md(v1.0)§2/§3.5/§7.1
|
||||
# 同规则的权威实现见 extension/rules.cjs(两侧口径必须一致,tests 负责对照)
|
||||
# 跨平台:Linux / macOS 原生 sh;Windows 经 Git for Windows 自带 sh。零依赖。
|
||||
#
|
||||
# 安装:hooks/install.sh(或 install.ps1),或手动:
|
||||
# mkdir -p .githooks && cp hooks/commit-msg .githooks/ && git config core.hooksPath .githooks
|
||||
|
||||
msg_file="$1"
|
||||
[ -f "$msg_file" ] || exit 0
|
||||
|
||||
# 首行(去首尾空白与 CR,与 rules.cjs 的 trim 对齐)
|
||||
first=$(sed -n '1p' "$msg_file" | tr -d '\r' | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')
|
||||
|
||||
# 豁免:git 自动生成的 merge / revert 消息(SKILL.md §3.5)
|
||||
case "$first" in
|
||||
"Merge "*|'Revert "'*) exit 0 ;;
|
||||
esac
|
||||
|
||||
errors=0
|
||||
fail() {
|
||||
echo "commit-msg: $*" >&2
|
||||
errors=$((errors + 1))
|
||||
}
|
||||
|
||||
TYPE_RE='(feat|fix|perf|refactor|test|docs|build|ci|chore|style|revert)'
|
||||
|
||||
# header 格式:type(scope)!: 主题
|
||||
if ! printf '%s' "$first" | grep -Eq "^${TYPE_RE}(\\([a-z0-9_.-]+\\))?!?: .+"; then
|
||||
echo "commit-msg: header 不符合 type(scope)!: 主题 格式(type ∈ feat/fix/perf/refactor/test/docs/build/ci/chore/style/revert):$first" >&2
|
||||
echo "commit-msg: 提交被拒绝。规范全文见 skills/commit-style/SKILL.md" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
subject=$(printf '%s' "$first" | sed -E "s/^${TYPE_RE}(\\([a-z0-9_.-]+\\))?!?: //" | sed -E 's/^[[:space:]]+//')
|
||||
hdr_type=$(printf '%s' "$first" | sed -E "s/^(${TYPE_RE}).*/\1/")
|
||||
|
||||
# 主题结尾不加句号("v1.0" 这类数字点结尾不算)
|
||||
case "$subject" in
|
||||
*。) fail "主题结尾不加句号" ;;
|
||||
esac
|
||||
if printf '%s' "$subject" | grep -Eq '[^0-9]\.$'; then
|
||||
fail "主题结尾不加句号"
|
||||
fi
|
||||
|
||||
# 显示宽度:全角记 2 / 半角记 1,上限 100(约 50 个汉字)
|
||||
# 探测 wc -m 是否按码点计数(UTF-8 locale);不行则依次尝试常见 UTF-8 locale;
|
||||
# 都不行(无 UTF-8 环境的极端情况)跳过宽度检查(echo 0),其余规则仍生效。
|
||||
dw_width() {
|
||||
_probe=$(printf '\344\275\240' | wc -m)
|
||||
if [ "$_probe" -eq 1 ] 2>/dev/null; then
|
||||
_c=$(printf '%s' "$1" | wc -m); _b=$(printf '%s' "$1" | wc -c)
|
||||
echo $(( _c + (_b - _c) / 2 )); return
|
||||
fi
|
||||
for _l in C.UTF-8 en_US.UTF-8; do
|
||||
_probe=$(printf '\344\275\240' | LC_ALL="$_l" wc -m)
|
||||
if [ "$_probe" -eq 1 ] 2>/dev/null; then
|
||||
_c=$(printf '%s' "$1" | LC_ALL="$_l" wc -m); _b=$(printf '%s' "$1" | wc -c)
|
||||
echo $(( _c + (_b - _c) / 2 )); return
|
||||
fi
|
||||
done
|
||||
echo 0
|
||||
}
|
||||
width=$(dw_width "$subject")
|
||||
if [ "$width" -gt 100 ] 2>/dev/null; then
|
||||
fail "主题显示宽度 $width > 100(全角记 2/半角记 1,约 50 个汉字)"
|
||||
fi
|
||||
|
||||
# 空泛词封闭匹配(与 extension/rules.cjs 一致):黑名单词单独,或仅带泛称宾语
|
||||
# en 归一化与 JS 的 trim+小写+折叠空白对齐(tr 只做小写与压缩空格会漏多空格/tab 形态)
|
||||
zh=$(printf '%s' "$subject" | tr -d '[:space:]')
|
||||
for _w in 更新 优化 修复 完善 调整 修改 重构 小改动; do
|
||||
for _g in "" 代码 性能 bug BUG Bug 功能 问题 文件 逻辑 一些问题; do
|
||||
if [ "$zh" = "$_w$_g" ]; then
|
||||
fail "主题为空泛词(单独或仅带泛称宾语):\"$subject\" —— 宾语须具体到模块/现象/数字"
|
||||
break 2
|
||||
fi
|
||||
done
|
||||
done
|
||||
en=$(printf '%s' "$subject" | tr '[:upper:]' '[:lower:]' | sed -E 's/[[:space:]]+/ /g; s/^ //; s/ $//')
|
||||
for _w in update fix optimize refactor cleanup tweak improve wip; do
|
||||
for _g in "" code codes performance perf bug bugs file files logic stuff things; do
|
||||
if [ -z "$_g" ]; then _pat="$_w"; else _pat="$_w $_g"; fi
|
||||
if [ "$en" = "$_pat" ]; then
|
||||
fail "主题为空泛词(单独或仅带泛称宾语):\"$subject\" —— 宾语须具体到模块/现象/数字"
|
||||
break 2
|
||||
fi
|
||||
done
|
||||
done
|
||||
|
||||
# 验证行:feat/fix/perf 必须有(token 双语:验证: / Verified:)
|
||||
case "$hdr_type" in
|
||||
feat|fix|perf)
|
||||
if ! grep -Eq '^验证:[[:blank:]]*[^[:space:]]' "$msg_file" && \
|
||||
! grep -Eq '^[Vv]erified:[[:blank:]]*[^[:space:]]' "$msg_file"; then
|
||||
fail "type 为 $hdr_type,必须有\"验证:\"行(英文项目 Verified:)——只写真实跑过的检查与结果;没跑过就先别提交"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
# BREAKING CHANGE 脚注必须全大写
|
||||
if grep -Eiq '^[[:blank:]]*breaking[[:blank:]]+change[[:blank:]]*:' "$msg_file" && \
|
||||
! grep -Eq '^[[:blank:]]*BREAKING CHANGE[[:blank:]]*:' "$msg_file"; then
|
||||
fail "脚注 BREAKING CHANGE 必须全大写"
|
||||
fi
|
||||
|
||||
if [ "$errors" -gt 0 ]; then
|
||||
echo "commit-msg: 提交被拒绝($errors 个问题)。规范全文见 skills/commit-style/SKILL.md" >&2
|
||||
exit 1
|
||||
fi
|
||||
exit 0
|
||||
Reference in new issue
Block a user