脚本中Pre-commit钩子如何做

wen 实用脚本 3

本文目录导读:

脚本中Pre-commit钩子如何做

  1. 基础 Pre-commit 钩子脚本
  2. 语言特定的 Pre-commit 钩子
  3. 完整的 Pre-commit 钩子框架
  4. 使用 pre-commit 框架
  5. 高级功能
  6. 最佳实践

我来详细说明如何在脚本中实现 Pre-commit 钩子:

基础 Pre-commit 钩子脚本

基础示例(Shell脚本)

#!/bin/bash
# .git/hooks/pre-commit
echo "Running pre-commit checks..."
# 获取暂存区文件
STAGED_FILES=$(git diff --cached --name-only --diff-filter=ACM)
if [ -z "$STAGED_FILES" ]; then
    echo "No files staged for commit"
    exit 0
fi
# 检查是否有调试代码
for FILE in $STAGED_FILES; do
    if grep -n "console.log\|debugger\|TODO" "$FILE" 2>/dev/null; then
        echo "Warning: Found debug code in $FILE"
        exit 1
    fi
done
# 检查文件大小
for FILE in $STAGED_FILES; do
    if [ -f "$FILE" ]; then
        FILESIZE=$(stat -c%s "$FILE" 2>/dev/null || stat -f%z "$FILE")
        if [ $FILESIZE -gt 5242880 ]; then  # 5MB
            echo "Error: $FILE is too large ($FILESIZE bytes)"
            exit 1
        fi
    fi
done
echo "All checks passed!"
exit 0

语言特定的 Pre-commit 钩子

Python 项目

#!/bin/bash
# .git/hooks/pre-commit
# Python代码检查
echo "Running Python checks..."
STAGED_PY_FILES=$(git diff --cached --name-only --diff-filter=ACM | grep '\.py$')
if [ -n "$STAGED_PY_FILES" ]; then
    # 语法检查
    python -m py_compile $STAGED_PY_FILES 2>&1
    if [ $? -ne 0 ]; then
        echo "Python syntax check failed"
        exit 1
    fi
    # Flake8代码风格检查
    flake8 $STAGED_PY_FILES
    if [ $? -ne 0 ]; then
        echo "Flake8 style check failed"
        exit 1
    fi
    # 运行测试
    python -m pytest tests/ --quick
fi

JavaScript/Node.js 项目

#!/bin/bash
# .git/hooks/pre-commit
echo "Running JavaScript checks..."
STAGED_JS_FILES=$(git diff --cached --name-only --diff-filter=ACM | grep '\.js$')
if [ -n "$STAGED_JS_FILES" ]; then
    # ESLint检查
    npx eslint $STAGED_JS_FILES
    if [ $? -ne 0 ]; then
        echo "ESLint check failed"
        exit 1
    fi
    # Prettier格式化检查
    npx prettier --check $STAGED_JS_FILES
    if [ $? -ne 0 ]; then
        echo "Prettier formatting check failed"
        exit 1
    fi
fi

完整的 Pre-commit 钩子框架

#!/bin/bash
# .git/hooks/pre-commit
set -e
# 颜色输出
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
# 日志函数
log_info() {
    echo -e "${GREEN}[INFO]${NC} $1"
}
log_warn() {
    echo -e "${YELLOW}[WARNING]${NC} $1"
}
log_error() {
    echo -e "${RED}[ERROR]${NC} $1"
}
# 配置
MAX_LINE_LENGTH=120
BANNED_PATTERNS=("console\.log" "debugger" "TODO" "FIXME" "xxx")
BANNED_EXTENSIONS=(".min.js" ".min.css" ".map")
# 获取暂存区文件
get_staged_files() {
    git diff --cached --name-only --diff-filter=ACM
}
# 检查禁止的模式
check_banned_patterns() {
    local files=$1
    local has_error=0
    for FILE in $files; do
        if [ -f "$FILE" ]; then
            for pattern in "${BANNED_PATTERNS[@]}"; do
                if grep -qn "$pattern" "$FILE" 2>/dev/null; then
                    log_error "Found '$pattern' in $FILE"
                    has_error=1
                fi
            done
        fi
    done
    return $has_error
}
# 检查文件扩展名
check_extension() {
    local files=$1
    local has_error=0
    for FILE in $files; do
        for ext in "${BANNED_EXTENSIONS[@]}"; do
            if [[ "$FILE" == *"$ext" ]]; then
                log_error "Banned extension $ext found: $FILE"
                has_error=1
            fi
        done
    done
    return $has_error
}
# 检查行长度
check_line_length() {
    local files=$1
    local has_error=0
    for FILE in $files; do
        if [[ "$FILE" =~ \.(py|js|ts|java|rb|go)$ ]]; then
            local long_lines=$(awk "length > $MAX_LINE_LENGTH" "$FILE" 2>/dev/null | wc -l)
            if [ $long_lines -gt 0 ]; then
                log_warn "$FILE has $long_lines lines exceeding $MAX_LINE_LENGTH characters"
            fi
        fi
    done
    return 0  # 只是警告,不阻止提交
}
# 检查大文件
check_large_files() {
    local files=$1
    local max_size_mb=10
    local has_error=0
    for FILE in $files; do
        if [ -f "$FILE" ]; then
            local size_kb=$(du -k "$FILE" | cut -f1)
            if [ $size_kb -gt $(($max_size_mb * 1024)) ]; then
                log_error "$FILE is larger than ${max_size_mb}MB"
                has_error=1
            fi
        fi
    done
    return $has_error
}
# 主函数
main() {
    local staged_files=$(get_staged_files)
    if [ -z "$staged_files" ]; then
        log_info "No files staged for commit"
        exit 0
    fi
    log_info "Running pre-commit checks on $(echo "$staged_files" | wc -l) files..."
    # 运行所有检查
    check_extension "$staged_files" || exit 1
    check_banned_patterns "$staged_files" || exit 1
    check_line_length "$staged_files"
    check_large_files "$staged_files" || exit 1
    # 运行语言特定的检查
    local py_files=$(echo "$staged_files" | grep '\.py$')
    local js_files=$(echo "$staged_files" | grep '\.js$')
    if [ -n "$py_files" ]; then
        log_info "Running Python checks..."
        python -m py_compile $py_files 2>&1 || exit 1
        flake8 $py_files 2>/dev/null || true  # 只是警告
    fi
    if [ -n "$js_files" ]; then
        log_info "Running JavaScript checks..."
        npx eslint $js_files 2>/dev/null || exit 1
    fi
    log_info "All pre-commit checks passed!"
    exit 0
}
# 执行主函数
main

使用 pre-commit 框架

安装和配置

# .pre-commit-config.yaml
repos:
  - repo: https://github.com/pre-commit/pre-commit-hooks
    rev: v4.4.0
    hooks:
      - id: trailing-whitespace
      - id: end-of-file-fixer
      - id: check-yaml
      - id: check-added-large-files
      - id: check-merge-conflict
      - id: check-case-conflict
  - repo: https://github.com/psf/black
    rev: 23.3.0
    hooks:
      - id: black
  - repo: https://github.com/pycqa/flake8
    rev: 6.0.0
    hooks:
      - id: flake8

安装框架钩子

# 安装 pre-commit
pip install pre-commit
# 或者使用 npm
npm install -g pre-commit
# 在项目中安装钩子
pre-commit install
# 手动运行检查
pre-commit run --all-files
# 更新钩子版本
pre-commit autoupdate

高级功能

跳过某些检查

# 使用环境变量跳过检查
git commit -m "message" --no-verify
# 或者设置环境变量
SKIP=flake8 git commit -m "message"

阶段性检查

#!/bin/bash
# .git/hooks/pre-commit
# 只检查修改的部分(不是整个文件)
check_staged_lines() {
    local file=$1
    local staged_lines=$(git diff --cached -U0 "$file" | grep "^@@" | sed 's/.*+\([0-9]*\).*/\1/')
    for line_num in $staged_lines; do
        local content=$(sed -n "${line_num}p" "$file")
        if echo "$content" | grep -q "console.log"; then
            echo "Debug code found at line $line_num in $file"
            return 1
        fi
    done
}

自定义检查脚本

#!/bin/bash
# scripts/pre-commit-custom.sh
run_custom_checks() {
    local files=$1
    # 检查文件命名规范
    for file in $files; do
        if [[ "$file" =~ [A-Z] ]] && [[ "$file" != *"Config"* ]]; then
            echo "Warning: $file contains uppercase characters"
        fi
    done
    # 检查提交信息格式
    local commit_msg=$(git log --format=%B -n1 HEAD)
    if [[ ! "$commit_msg" =~ ^(feat|fix|docs|style|refactor|test|chore): ]]; then
        echo "Commit message should follow conventional commit format"
        exit 1
    fi
}

最佳实践

  1. 快速执行:钩子应该快速执行,不要运行完整的测试套件
  2. 清晰反馈:提供明确的错误信息和修复建议
  3. 可跳过:允许紧急情况下跳过检查
  4. 逐步增强:先添加宽松检查,逐步严格
  5. 团队共享:将钩子脚本保存在项目中
  6. 自动修复:尽可能自动修复格式问题

这些脚本可以根据项目需求进行调整和扩展,建议先在测试环境中验证钩子,然后再应用到生产环境。

抱歉,评论功能暂时关闭!