pre-commit 代码检查改为委托 _backend-lint / _versionctl-lint / _frontend-check,新增 LFS 指针校验;commit-msg 新增多行空行格式校验和模板注释忽略,移除 CJK/Python 字符集检测;新增 prepare-commit-msg 提交信息模板;hooks-install 增加 source 文件存在性校验;前端 check 补入 tsc -b 类型检查并修复暴露的类型错误
274 lines
9.6 KiB
Bash
Executable File
274 lines
9.6 KiB
Bash
Executable File
#!/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 \
|
|
"$TMP_DIR/lfs-pointer-fixture" \
|
|
"$TMP_DIR/lfs-bad-fixture"
|
|
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"
|
|
}
|
|
|
|
write_conflict() {
|
|
file=$1
|
|
less7=$(printf '<%.0s' $(seq 7))
|
|
eq7=$(printf '=%.0s' $(seq 7))
|
|
gt7=$(printf '>%.0s' $(seq 7))
|
|
printf '%s\n' "${less7} HEAD" '' "${eq7}" '' "${gt7} branch" > "$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
|
|
}
|
|
|
|
run_hooks_install_missing_source() {
|
|
install_repo=$TMP_DIR/hooks-install-missing
|
|
rm -rf "$install_repo"
|
|
mkdir -p "$install_repo/scripts/git-hooks"
|
|
cp Makefile "$install_repo/Makefile"
|
|
cp scripts/git-hooks/pre-commit "$install_repo/scripts/git-hooks/pre-commit"
|
|
cp scripts/git-hooks/commit-msg "$install_repo/scripts/git-hooks/commit-msg"
|
|
git -C "$install_repo" init >/dev/null 2>&1
|
|
(cd "$install_repo" && make hooks-install)
|
|
}
|
|
|
|
MSG_FILE=$TMP_DIR/commit-msg.txt
|
|
|
|
# ============================================
|
|
# commit-msg 测试
|
|
# ============================================
|
|
|
|
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_success 'commit-msg accepts English-only description without CJK enforcement' scripts/git-hooks/commit-msg "$MSG_FILE"
|
|
|
|
write_msg "$MSG_FILE" 'fix: 修复 auth 模块 bug'
|
|
expect_success 'commit-msg accepts Chinese with English technical terms' scripts/git-hooks/commit-msg "$MSG_FILE"
|
|
|
|
write_msg "$MSG_FILE" 'docs: ajouter une fonctionnalité'
|
|
expect_success 'commit-msg accepts non-CJK unicode description without CJK enforcement' 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"
|
|
|
|
write_msg "$MSG_FILE" 'feat: 添加新功能
|
|
'
|
|
expect_success 'commit-msg accepts single line with trailing newline' scripts/git-hooks/commit-msg "$MSG_FILE"
|
|
|
|
printf 'feat: 添加新功能\n\n详细描述内容\n' > "$MSG_FILE"
|
|
expect_success 'commit-msg accepts multi-line with blank line separator' scripts/git-hooks/commit-msg "$MSG_FILE"
|
|
|
|
printf 'feat: 添加新功能\n缺少空行\n详细描述\n' > "$MSG_FILE"
|
|
expect_failure 'commit-msg rejects multi-line without blank line separator' scripts/git-hooks/commit-msg "$MSG_FILE"
|
|
|
|
printf 'feat: 添加新功能\n\n' > "$MSG_FILE"
|
|
expect_success 'commit-msg accepts two lines with blank line 2' scripts/git-hooks/commit-msg "$MSG_FILE"
|
|
|
|
printf 'feat: 添加新功能\n非空行\n' > "$MSG_FILE"
|
|
expect_success 'commit-msg accepts two lines without body (no line 3)' scripts/git-hooks/commit-msg "$MSG_FILE"
|
|
|
|
printf 'feat: 添加模板测试\n# <类型>: <简短中文描述>\n#\n# <详细说明>\n' > "$MSG_FILE"
|
|
expect_success 'commit-msg ignores template comments after subject' scripts/git-hooks/commit-msg "$MSG_FILE"
|
|
|
|
printf '# <类型>: <简短中文描述>\n#\nfeat: 添加模板测试\n' > "$MSG_FILE"
|
|
expect_success 'commit-msg ignores leading template comments' scripts/git-hooks/commit-msg "$MSG_FILE"
|
|
|
|
printf 'feat: 添加新功能\n缺少空行\n# 模板注释\n详细描述\n' > "$MSG_FILE"
|
|
expect_failure 'commit-msg rejects non-blank separator with intervening comments' scripts/git-hooks/commit-msg "$MSG_FILE"
|
|
|
|
# ============================================
|
|
# prepare-commit-msg 测试
|
|
# ============================================
|
|
|
|
prepare_msg_file="$TMP_DIR/prepare-msg.txt"
|
|
rm -f "$prepare_msg_file"
|
|
touch "$prepare_msg_file"
|
|
expect_success 'prepare-commit-msg writes template for empty commit' scripts/git-hooks/prepare-commit-msg "$prepare_msg_file" ""
|
|
|
|
if grep -q '<类型>' "$prepare_msg_file" && grep -q 'feat / fix / refactor' "$prepare_msg_file"; then
|
|
pass 'prepare-commit-msg template contains format guidance'
|
|
else
|
|
fail 'prepare-commit-msg template contains format guidance'
|
|
fi
|
|
|
|
printf '\n# Please enter the commit message for your changes.\n# On branch main\n' > "$prepare_msg_file"
|
|
expect_success 'prepare-commit-msg writes template before git comments' scripts/git-hooks/prepare-commit-msg "$prepare_msg_file" ""
|
|
if grep -q '<类型>' "$prepare_msg_file" && grep -q 'Please enter the commit message' "$prepare_msg_file"; then
|
|
pass 'prepare-commit-msg preserves git comments after template'
|
|
else
|
|
fail 'prepare-commit-msg preserves git comments after template'
|
|
fi
|
|
|
|
write_msg "$prepare_msg_file" 'existing content'
|
|
expect_success 'prepare-commit-msg skips when file has content' scripts/git-hooks/prepare-commit-msg "$prepare_msg_file" ""
|
|
if printf '%s\n' "$(cat "$prepare_msg_file")" | grep -q '^existing content$'; then
|
|
pass 'prepare-commit-msg does not overwrite existing content'
|
|
else
|
|
fail 'prepare-commit-msg does not overwrite existing content'
|
|
fi
|
|
|
|
rm -f "$prepare_msg_file"
|
|
touch "$prepare_msg_file"
|
|
expect_success 'prepare-commit-msg skips for merge' scripts/git-hooks/prepare-commit-msg "$prepare_msg_file" "merge"
|
|
if [ ! -s "$prepare_msg_file" ]; then
|
|
pass 'prepare-commit-msg skips template for merge'
|
|
else
|
|
fail 'prepare-commit-msg skips template for merge'
|
|
fi
|
|
|
|
# ============================================
|
|
# hooks-install 测试
|
|
# ============================================
|
|
|
|
expect_failure 'hooks-install rejects missing source hook' run_hooks_install_missing_source
|
|
|
|
# ============================================
|
|
# pre-commit 测试
|
|
# ============================================
|
|
|
|
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 (delegated to _backend-lint)' 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 (delegated to _frontend-check)' 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 (delegated to _frontend-check)' 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
|
|
|
|
write_conflict docs/hook-conflict-fixture.md
|
|
expect_failure 'pre-commit rejects conflict markers' run_precommit_for docs/hook-conflict-fixture.md
|
|
rm -f docs/hook-conflict-fixture.md
|
|
|
|
index=$TMP_DIR/index
|
|
rm -f "$index"
|
|
GIT_INDEX_FILE=$index git read-tree HEAD
|
|
write_conflict "$TMP_DIR/hook-conflict-fixture.sh"
|
|
hash=$(git hash-object -w "$TMP_DIR/hook-conflict-fixture.sh")
|
|
rm -f "$TMP_DIR/hook-conflict-fixture.sh"
|
|
GIT_INDEX_FILE=$index git update-index --add --cacheinfo 100644 "$hash" "scripts/git-hooks/hook-conflict-fixture.sh"
|
|
expect_failure 'pre-commit rejects conflict markers in hook scripts' env GIT_INDEX_FILE=$index make _hooks-pre-commit
|
|
|
|
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
|
|
|
|
# LFS pointer 校验
|
|
lfs_pointer='version https://git-lfs.github.com/spec/v1
|
|
oid sha256:abc123
|
|
size 100
|
|
'
|
|
printf '%s\n' "$lfs_pointer" > "$TMP_DIR/lfs-pointer-fixture"
|
|
hash=$(git hash-object -w "$TMP_DIR/lfs-pointer-fixture")
|
|
index=$TMP_DIR/index
|
|
rm -f "$index"
|
|
GIT_INDEX_FILE=$index git read-tree HEAD
|
|
GIT_INDEX_FILE=$index git update-index --add --cacheinfo 100644 "$hash" "assets/test-lfs-fixture.png"
|
|
if GIT_INDEX_FILE=$index make _hooks-pre-commit > "$TMP_DIR/out" 2>&1; then
|
|
pass 'pre-commit allows LFS pointer files'
|
|
else
|
|
cat "$TMP_DIR/out" >&2
|
|
fail 'pre-commit allows LFS pointer files'
|
|
fi
|
|
|
|
printf 'fake binary content\n' > "$TMP_DIR/lfs-bad-fixture"
|
|
hash=$(git hash-object -w "$TMP_DIR/lfs-bad-fixture")
|
|
rm -f "$index"
|
|
GIT_INDEX_FILE=$index git read-tree HEAD
|
|
GIT_INDEX_FILE=$index git update-index --add --cacheinfo 100644 "$hash" "assets/test-lfs-bad-fixture.png"
|
|
if GIT_INDEX_FILE=$index make _hooks-pre-commit > "$TMP_DIR/out" 2>&1; then
|
|
cat "$TMP_DIR/out" >&2
|
|
fail 'pre-commit rejects non-pointer LFS files'
|
|
fi
|
|
pass 'pre-commit rejects non-pointer LFS files'
|