Git钩子脚本如何检查性能门禁

wen 实用脚本 1

本文目录导读:

Git钩子脚本如何检查性能门禁

  1. 性能门禁的核心思路
  2. 常用工具与集成方式
  3. Git 钩子检查性能门禁示例
  4. 钩子脚本放置与权限
  5. 使用 Husky(前端项目推荐)
  6. 性能门禁的常见阈值参考
  7. 注意事项

Git 钩子脚本可以通过在代码提交前或提交后运行性能检查工具,来实施“性能门禁”,以下是一些常见的方法和示例脚本,涵盖前端(JS/TS)和后端(Java/Python/Go)等场景。


性能门禁的核心思路

性能门禁通常检查以下维度:

  • 代码体积/资源体积:避免提交过大的文件或打包产物
  • 执行时间:防止引入明显变慢的函数或算法
  • 内存/CPU 使用:检测内存泄漏或高消耗操作
  • 网络请求:检查 API 调用次数、payload 大小
  • 构建速度:防止退化导致构建过慢

常用工具与集成方式

检查类型 推荐工具 适用场景
代码复杂度 ESLint (复杂度规则)、Plato、Radon JS/TS/Python
文件体积 dusize-limitwebpack-bundle-analyzer 前端资源
打包体积 size-limitbundlesize 前端构建
函数执行时间 自定义 Benchmark 脚本 后端关键逻辑
内存泄漏 Node.js heap snapshot、Valgrind 后端/服务端
API 性能 Lighthouse CI (lighthouse-ci) 前端性能评分
构建时间 计时脚本 CI/CD 环境

Git 钩子检查性能门禁示例

检查文件大小上限(pre-commit 钩子)

#!/bin/bash
# .git/hooks/pre-commit
# 设置最大文件大小(单位:字节,这里设为 1MB)
MAX_SIZE=$((1 * 1024 * 1024))
# 获取本次提交的改动文件(仅暂存区)
changed_files=$(git diff --cached --name-only --diff-filter=ACM)
threshold_exceeded=false
for file in $changed_files; do
    if [ -f "$file" ]; then
        file_size=$(stat --format=%s "$file")
        if [ "$file_size" -gt "$MAX_SIZE" ]; then
            echo "❌ 性能门禁失败: $file 大小为 $(numfmt --to=iec $file_size),超出限制 $(numfmt --to=iec $MAX_SIZE)"
            threshold_exceeded=true
        fi
    fi
done
if [ "$threshold_exceeded" = true ]; then
    echo "请优化大文件后再提交。"
    exit 1
fi
exit 0

检查前端打包体积(pre-push 钩子,需要 Node.js)

// .git/hooks/pre-push (先设置可执行权限)
// 需要 Node.js 环境
#!/usr/bin/env node
const { execSync } = require('child_process');
const path = require('path');
const fs = require('fs');
// 配置:最大允许的打包体积(字节)
const MAX_BUNDLE_SIZE = 200 * 1024; // 200KB
try {
    // 运行构建(仅用于检查,不覆盖dist)
    execSync('npm run build -- --profile', {
        cwd: process.cwd(),
        stdio: 'pipe',
        timeout: 120000 // 2分钟超时
    });
    // 找到构建产物(以 dist 为例)
    const distDir = path.join(process.cwd(), 'dist');
    if (!fs.existsSync(distDir)) {
        console.log('⚠️ 未找到 dist 目录,跳过打包体积检查');
        process.exit(0);
    }
    const files = fs.readdirSync(distDir, { recursive: true });
    let totalSize = 0;
    for (const file of files) {
        const fullPath = path.join(distDir, file);
        if (fs.statSync(fullPath).isFile()) {
            totalSize += fs.statSync(fullPath).size;
        }
    }
    if (totalSize > MAX_BUNDLE_SIZE) {
        console.error(`❌ 性能门禁失败: 打包总大小 ${(totalSize / 1024).toFixed(2)}KB,超过限制 ${(MAX_BUNDLE_SIZE / 1024).toFixed(2)}KB`);
        process.exit(1);
    } else {
        console.log(`✅ 打包体积检查通过: ${(totalSize / 1024).toFixed(2)}KB`);
    }
} catch (error) {
    console.error('❌ 构建或体积检查出错:', error.message);
    process.exit(1);
}

检查代码复杂度(pre-commit,ESLint)

#!/bin/bash
# .git/hooks/pre-commit (需要已安装 eslint)
# 检查是否有 ESLint 配置
if [ ! -f ".eslintrc.js" ] && [ ! -f ".eslintrc.json" ]; then
    echo "⚠️ 未找到 ESLint 配置,跳过复杂度检查"
    exit 0
fi
# 获取被修改的 JS/TS 文件
changed_js_files=$(git diff --cached --name-only --diff-filter=ACM | grep -E '\.(js|jsx|ts|tsx)$')
if [ -z "$changed_js_files" ]; then
    exit 0
fi
# 运行 ESLint 复杂度规则(必须配置了 complexity 规则)
npx eslint --rule 'complexity: ["error", 10]' $changed_js_files 2>&1
if [ $? -ne 0 ]; then
    echo "❌ 性能门禁失败: 存在高复杂度函数,请重构后提交"
    exit 1
fi
exit 0

检查数据库查询/API 性能(后端 pre-commit,自定义脚本)

#!/bin/bash
# .git/hooks/pre-commit
# 检查是否有 SQL 查询或 API 调用的改动
changed_sql_files=$(git diff --cached --name-only --diff-filter=ACM | grep -E '\.(sql|prisma|entity|repository)\.(ts|js|py|java)$')
if [ -z "$changed_sql_files" ]; then
    exit 0
fi
# 演示:检查是否包含 N+1 查询模式(关键字:forEach 内调用 find/findOne)
for file in $changed_sql_files; do
    if grep -qP 'await.*find(One)?\(.*\)\s*\)' "$file" 2>/dev/null; then
        echo "❌ 性能门禁失败: $file 中可能存在 N+1 查询模式(循环内逐个查找)"
        echo "建议使用批量查询(IN 子句)或 JOIN 代替"
        exit 1
    fi
done
echo "✅ SQL 查询模式检查通过"
exit 0

集成 Lighthouse CI 性能评分(pre-push

# .github/workflows/performance-gate.yml (作为 GitHub Actions,但逻辑可移植到钩子)
# 实际钩子脚本:调用 lighthouse-ci 并检查分数
#!/bin/bash
# .git/hooks/pre-push
# 需要先安装 lighthouse-ci
# npm install -g @lhci/cli
# 假设有一个暂存或预览地址
URL="https://your-staging-url.com"
# 运行 Lighthouse 并输出 JSON 结果
lhci collect --url=$URL --numberOfRuns=2
lhci assert --preset=lighthouse:recommended 2>&1 | grep -q "All assertions passed"
if [ $? -ne 0 ]; then
    echo "❌ 性能门禁失败: Lighthouse 分数未达到阈值"
    exit 1
fi
echo "✅ 性能评分通过"
exit 0

钩子脚本放置与权限

# 将脚本放到 .git/hooks/ 下,并赋予可执行权限
chmod +x .git/hooks/pre-commit
chmod +x .git/hooks/pre-push
# 若要全局分发至团队,可使用 git 模板或 husky 管理

使用 Husky(前端项目推荐)

package.json 中添加命令:

npx husky add .husky/pre-commit "npx eslint --rule 'complexity: [\"error\", 10]' --ext .js,.ts --diff"
npx husky add .husky/pre-push "npm run build && node scripts/check-bundle-size.js"

性能门禁的常见阈值参考

检查项 合理阈值
单个文件大小 500KB ~ 2MB(视项目类型)
打包 JS 体积 200KB ~ 1MB(gzip 后更严格)
函数圈复杂度 10 ~ 15
构建时间 2 ~ 5 分钟
Lighthouse 性能评分 ≥ 80 分
单个 API 响应时间 < 200ms

注意事项

  1. 性能门禁不要过于严格,避免阻碍正常开发,可以设置“警告”和“拒绝”两个级别。
  2. 只在特定分支(如 main/release)强制执行,开发分支可以只提示。
  3. 本地钩子可被绕过,CI/CD 管道应保留强制的性能检查。
  4. 避免在钩子中运行耗时过长的检查(如完整构建),可放到 pre-push 或 CI 阶段。

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