feat: 原生 Git hooks 方案,增强版本升迁工作流
This commit is contained in:
42
scripts/git-hooks/commit-msg
Executable file
42
scripts/git-hooks/commit-msg
Executable file
@@ -0,0 +1,42 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
MSG_FILE=$1
|
||||
|
||||
if [ ! -f "$MSG_FILE" ]; then
|
||||
printf '%s\n' '提交信息文件不存在。' >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
IFS= read -r FIRST_LINE < "$MSG_FILE" || FIRST_LINE=
|
||||
|
||||
case "$FIRST_LINE" in
|
||||
Merge*)
|
||||
exit 0
|
||||
;;
|
||||
esac
|
||||
|
||||
if ! printf '%s\n' "$FIRST_LINE" | grep -Eq '^(feat|fix|refactor|docs|style|test|chore): .+$'; then
|
||||
cat >&2 <<'EOF'
|
||||
提交信息格式错误。
|
||||
|
||||
格式: <类型>: <简短描述>
|
||||
类型: feat / fix / refactor / docs / style / test / chore
|
||||
|
||||
示例:
|
||||
feat: 添加供应商批量管理功能
|
||||
fix: 修复流式响应断连问题
|
||||
chore: 版本升迁 v0.2.0
|
||||
EOF
|
||||
exit 1
|
||||
fi
|
||||
|
||||
DESCRIPTION=${FIRST_LINE#*: }
|
||||
if printf '%s\n' "$DESCRIPTION" | LC_ALL=C grep -Eq '^[ -~]+$'; then
|
||||
printf '%s\n' '提交描述需使用中文。' >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ${#FIRST_LINE} -gt 72 ]; then
|
||||
printf '%s\n' '警告: 提交信息首行超过 72 个字符,建议保持简短。' >&2
|
||||
fi
|
||||
12
scripts/git-hooks/pre-commit
Executable file
12
scripts/git-hooks/pre-commit
Executable file
@@ -0,0 +1,12 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
ROOT_DIR=$(git rev-parse --show-toplevel)
|
||||
cd "$ROOT_DIR"
|
||||
|
||||
command -v make >/dev/null 2>&1 || {
|
||||
printf '%s\n' '缺少 make 命令,请先安装 Make 或使用 Git Bash/MINGW64 环境。' >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
exec make _hooks-pre-commit
|
||||
134
scripts/git-hooks/test-hooks.sh
Executable file
134
scripts/git-hooks/test-hooks.sh
Executable file
@@ -0,0 +1,134 @@
|
||||
#!/bin/sh
|
||||
set -eu
|
||||
|
||||
ROOT_DIR=$(git rev-parse --show-toplevel)
|
||||
cd "$ROOT_DIR"
|
||||
|
||||
TMP_DIR=${TMPDIR:-/tmp}/nex-hooks-test.$$
|
||||
mkdir -p "$TMP_DIR"
|
||||
|
||||
cleanup() {
|
||||
rm -f \
|
||||
backend/pkg/buildinfo/hook_bad_test_fixture.go \
|
||||
frontend/src/hook_bad_fixture.ts \
|
||||
frontend/src/hook_format_fixture.ts \
|
||||
docs/hook-doc-fixture.md \
|
||||
docs/hook-conflict-fixture.md \
|
||||
docs/hook-large-fixture.txt
|
||||
rm -rf "$TMP_DIR"
|
||||
}
|
||||
|
||||
trap cleanup EXIT HUP INT TERM
|
||||
|
||||
pass() {
|
||||
printf 'OK: %s\n' "$1"
|
||||
}
|
||||
|
||||
fail() {
|
||||
printf 'FAIL: %s\n' "$1" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
write_msg() {
|
||||
file=$1
|
||||
shift
|
||||
printf '%s\n' "$*" > "$file"
|
||||
}
|
||||
|
||||
expect_success() {
|
||||
name=$1
|
||||
shift
|
||||
if "$@" > "$TMP_DIR/out" 2>&1; then
|
||||
pass "$name"
|
||||
else
|
||||
cat "$TMP_DIR/out" >&2
|
||||
fail "$name"
|
||||
fi
|
||||
}
|
||||
|
||||
expect_failure() {
|
||||
name=$1
|
||||
shift
|
||||
if "$@" > "$TMP_DIR/out" 2>&1; then
|
||||
cat "$TMP_DIR/out" >&2
|
||||
fail "$name"
|
||||
fi
|
||||
pass "$name"
|
||||
}
|
||||
|
||||
run_precommit_for() {
|
||||
index=$TMP_DIR/index
|
||||
rm -f "$index"
|
||||
GIT_INDEX_FILE=$index git read-tree HEAD
|
||||
for file in "$@"; do
|
||||
GIT_INDEX_FILE=$index git add -f "$file"
|
||||
done
|
||||
GIT_INDEX_FILE=$index make _hooks-pre-commit
|
||||
}
|
||||
|
||||
MSG_FILE=$TMP_DIR/commit-msg.txt
|
||||
write_msg "$MSG_FILE" 'feat: 添加 hook 测试'
|
||||
expect_success 'commit-msg accepts Chinese description' scripts/git-hooks/commit-msg "$MSG_FILE"
|
||||
|
||||
write_msg "$MSG_FILE" 'feat: add hook tests'
|
||||
expect_failure 'commit-msg rejects English-only description' scripts/git-hooks/commit-msg "$MSG_FILE"
|
||||
|
||||
write_msg "$MSG_FILE" 'update: 添加 hook 测试'
|
||||
expect_failure 'commit-msg rejects invalid type' scripts/git-hooks/commit-msg "$MSG_FILE"
|
||||
|
||||
write_msg "$MSG_FILE" 'Merge branch feature'
|
||||
expect_success 'commit-msg accepts merge commits' scripts/git-hooks/commit-msg "$MSG_FILE"
|
||||
|
||||
cat > backend/pkg/buildinfo/hook_bad_test_fixture.go <<'EOF'
|
||||
package buildinfo
|
||||
|
||||
import "fmt"
|
||||
|
||||
func hookBadTestFixture() {
|
||||
fmt.Println("bad")
|
||||
}
|
||||
EOF
|
||||
expect_failure 'pre-commit rejects Go lint errors' run_precommit_for backend/pkg/buildinfo/hook_bad_test_fixture.go
|
||||
rm -f backend/pkg/buildinfo/hook_bad_test_fixture.go
|
||||
|
||||
cat > frontend/src/hook_bad_fixture.ts <<'EOF'
|
||||
console.log('bad')
|
||||
EOF
|
||||
expect_failure 'pre-commit rejects frontend lint errors' run_precommit_for frontend/src/hook_bad_fixture.ts
|
||||
rm -f frontend/src/hook_bad_fixture.ts
|
||||
|
||||
cat > frontend/src/hook_format_fixture.ts <<'EOF'
|
||||
const hookFormatFixture={foo:"bar"}
|
||||
export { hookFormatFixture }
|
||||
EOF
|
||||
expect_failure 'pre-commit rejects frontend format errors' run_precommit_for frontend/src/hook_format_fixture.ts
|
||||
rm -f frontend/src/hook_format_fixture.ts
|
||||
|
||||
cat > docs/hook-doc-fixture.md <<'EOF'
|
||||
hook doc fixture
|
||||
EOF
|
||||
expect_success 'pre-commit skips non-code staged files' run_precommit_for docs/hook-doc-fixture.md
|
||||
rm -f docs/hook-doc-fixture.md
|
||||
|
||||
cat > docs/hook-conflict-fixture.md <<'EOF'
|
||||
<<<<<<< HEAD
|
||||
conflict
|
||||
=======
|
||||
other
|
||||
>>>>>>> branch
|
||||
EOF
|
||||
expect_failure 'pre-commit rejects conflict markers' run_precommit_for docs/hook-conflict-fixture.md
|
||||
rm -f docs/hook-conflict-fixture.md
|
||||
|
||||
i=0
|
||||
while [ "$i" -lt 40000 ]; do
|
||||
printf 'large hook fixture line\n'
|
||||
i=$((i + 1))
|
||||
done > docs/hook-large-fixture.txt
|
||||
if run_precommit_for docs/hook-large-fixture.txt > "$TMP_DIR/out" 2>&1 && grep -q 'Warning: large staged text file' "$TMP_DIR/out"; then
|
||||
pass 'pre-commit warns for large text files'
|
||||
else
|
||||
cat "$TMP_DIR/out" >&2
|
||||
fail 'pre-commit warns for large text files'
|
||||
fi
|
||||
rm -f docs/hook-large-fixture.txt
|
||||
Reference in New Issue
Block a user