From 9a61e69f2f9f44b468fce700b888bb75a78f12ee Mon Sep 17 00:00:00 2001 From: wait4xx Date: Sat, 24 Oct 2026 21:48:28 +0800 Subject: [PATCH] =?UTF-8?q?build:=20=E5=AE=89=E8=A3=85=20commit-msg=20?= =?UTF-8?q?=E8=A7=84=E8=8C=83=E9=92=A9=E5=AD=90=EF=BC=88ai-code-style-kit?= =?UTF-8?q?=20v1.0=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: GLM-5.3 --- .githooks/commit-msg | 112 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 112 insertions(+) create mode 100755 .githooks/commit-msg diff --git a/.githooks/commit-msg b/.githooks/commit-msg new file mode 100755 index 0000000..0b15b4f --- /dev/null +++ b/.githooks/commit-msg @@ -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