hooks(钩子)是事件触发器的俗称。搭建自动化工作流的核心逻辑是:在特定事件发生时,自动执行预定脚本。以下是生产环境中最实用的三层钩子架构。
1. 本地层:代码提交前拦截(Git Hooks)
在 .git/hooks 目录下(或全局配置 git config --global core.hooksPath),最实用的是 pre-commit 钩子,防止脏代码进入仓库:
#!/bin/sh
# .git/hooks/pre-commit
echo "正在检查代码规范..."
# 仅检查本次修改的文件
STAGED_FILES=$(git diff --cached --name-only --diff-filter=ACM | grep -E '\.(js|ts|vue|py)$')
if [ -z "$STAGED_FILES" ]; then exit 0; fi
# 运行 ESLint(前端)或 Black(Python)
echo "$STAGED_FILES" | xargs npx eslint --fix
# 如果有修改,阻止提交并提示
if ! git diff --cached --quiet; then
echo "检测到自动修复,请重新 git add 并提交"
exit 1
fi
关键配置:团队统一使用 Husky 管理钩子,避免每个人手动配置:
// package.json
{
"husky": {
"hooks": {
"pre-commit": "lint-staged",
"commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
}
}
}
2. 服务端层:代码推送后触发(Webhook)
当代码推送到 GitHub/GitLab 后,通过 Webhook 触发服务器动作。以 GitLab 自动部署为例:
服务器端脚本(/opt/webhook/deploy.sh):
#!/bin/bash
DEPLOY_DIR="/var/www/project"
LOG_FILE="/var/log/deploy.log"
echo "$(date): 收到部署请求" >> $LOG_FILE
cd $DEPLOY_DIR
git pull origin main --ff-only >> $LOG_FILE 2>&1
# 仅当依赖文件变更时才重装
if git diff --name-only HEAD@{1} HEAD | grep -q "package.json"; then
npm ci --production >> $LOG_FILE 2>&1
fi
pm2 reload app --update-env >> $LOG_FILE 2>&1
简易 Webhook 接收器(使用 Node.js 的 github-webhook-handler 或 Python 的 Flask):
# webhook_server.py
from flask import Flask, request
import hmac
import subprocess
app = Flask(__name__)
@app.route('/deploy', methods=['POST'])
def deploy():
# 验证密钥(防止恶意触发)
signature = request.headers.get('X-Hub-Signature-256')
secret = b'your-webhook-secret'
expected = 'sha256=' + hmac.new(secret, request.data, 'sha256').hexdigest()
if not hmac.compare_digest(expected, signature):
return 'Unauthorized', 401
# 异步执行部署(避免请求超时)
subprocess.Popen(['/opt/webhook/deploy.sh'])
return 'Deploying', 200
if __name__ == '__main__':
app.run(host='0.0.0.0', port=9000)
3. CI/CD 层:流水线钩子(GitHub Actions/GitLab CI)
如果使用了 CI 平台,钩子表现为 Pipeline 的触发条件。最实用的模式是仅当特定文件变更时才运行对应任务:
# .github/workflows/deploy.yml
name: Deploy
on:
push:
branches: [main]
paths: # 关键:只在以下文件变更时触发
- 'src/**'
- 'Dockerfile'
- '.github/workflows/**'
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: 构建镜像
run: docker build -t app:${{ github.sha }} .
- name: 部署到测试环境
if: github.ref == 'refs/heads/develop'
run: kubectl set image deployment/test app=app:${{ github.sha }}
- name: 部署到生产环境(带人工审批)
if: github.ref == 'refs/heads/main'
environment: production # 利用 GitHub Environments 强制人工点确认
run: kubectl set image deployment/prod app=app:${{ github.sha }}
4. 真实排坑指南
权限问题:钩子脚本常以 git 用户或 www-data 运行,需提前配置好 SSH 密钥或部署令牌(Deploy Token),切勿使用个人账号密码。
并发冲突:多人同时推送可能触发并发部署。在脚本开头加锁:
exec 200>/var/lock/my_deploy_script.lock
flock -n 200 || { echo "另一个部署正在进行"; exit 1; }
日志追踪:钩子运行在非交互环境,务必重定向所有输出到日志文件,否则报错时你什么都看不到。
测试策略:先在本地用 git hook run pre-commit 或 Postman 模拟 Webhook 请求,确认无误后再部署到服务器。
小结
钩子工作流的本质是分层守门:本地钩子保代码质量,服务端钩子保部署及时,CI 钩子保流程规范。搭建时遵循最小权限原则(钩子只给必要的文件读写权限),并确保每个钩子都有日志输出,否则故障排查将是噩梦。