Git Webhooks 自动化部署详解¶
本文详细介绍了如何实现本地 Git Push 到 Gitee 仓库后,触发 Gitee Webhook,并在服务器端执行相应的 Shell 脚本来实现自动化部署。
准备工作¶
服务器端¶
- 安装 Node.js 环境,确保服务器能够运行 Node.js 应用程序
- 确保服务器有适当的权限执行 Shell 脚本
本地环境¶
- 安装并配置 Git,确保可以与远程 Gitee 仓库进行交互
- 配置 SSH 密钥以便安全地与远程仓库通信
服务器端配置¶
创建 Node.js 项目¶
初始化一个新的 Node.js 项目,并安装必要的依赖包(如 express、body-parser、crypto 和 winston)。
创建一个名为 index.js 的文件,用于监听来自 Gitee 的 Webhook 请求。
const { exec } = require('child_process');
const express = require('express');
const bodyParser = require('body-parser');
const crypto = require('crypto');
const winston = require('winston');
// 配置日志记录
const logger = winston.createLogger({
level: 'info',
format: winston.format.combine(
winston.format.timestamp(),
winston.format.json()
),
transports: [
new winston.transports.Console(),
new winston.transports.File({ filename: 'error.log', level: 'error' }),
new winston.transports.File({ filename: 'combined.log' })
]
});
const app = express();
// 使用环境变量存储密钥
const secret = process.env.GITEE_WEBHOOK_SECRET;
if (!secret) throw new Error('缺少 GITEE_WEBHOOK_SECRET 环境变量');
// 配置 body-parser 中间件,用于解析请求体
app.use(bodyParser.json({ verify: (req, res, buf) => { req.rawBody = buf; } }));
// 生成签名密钥,签名算法为 sha256,官网算法说明:https://gitee.com/help/articles/4290
function generateSignature(secret, timestamp) {
const stringToSign = `${timestamp}\n${secret}`;
const hmac = crypto.createHmac('sha256', secret);
return hmac.update(stringToSign).digest('base64');
}
// 验证签名
function verifySignature(req) {
// 获取请求头中的时间戳
const timestamp = req.headers['x-gitee-timestamp'];
// 获取请求头中的签名和 token
const token = req.headers['x-gitee-token'];
if (!timestamp || !token) {
return false;
}
const expectedToken = generateSignature(secret, timestamp);
return expectedToken === token;
}
// 处理 POST 请求
app.post('/update', (req, res) => {
try {
// 检查请求头中是否包含 git-oschina-hook,判断是否是 Gitee 的请求
if (req.headers['user-agent']?.includes('git-oschina-hook') && verifySignature(req)) {
// 执行 shell 脚本
exec('cd /home/ && ls', (error, stdout, stderr) => {
if (error) {
console.error(`执行错误: ${error}`);
logger.error(`执行错误: ${error}`);
}
console.log(`输出: ${stdout}`);
// 返回更新结果,通知 gitee 验证结果
res.status(200).send(`更新成功: ${stdout}`);
});
} else {
res.status(403).send('Webhook 验证失败');
}
} catch (error) {
logger.error(`服务器错误: ${error.message}`);
res.status(500).send('服务器内部错误');
}
});
// 启动服务器
const PORT = process.env.PORT || 5330;
app.listen(PORT, () => {
logger.info(`Server is running on port ${PORT}`);
});
代码解释¶
- 日志记录配置:使用 winston 库配置日志记录,以便更好地跟踪和调试服务器行为
- 签名验证:通过 crypto 库生成签名,并验证来自 Gitee 的请求是否合法
- 处理 POST 请求:当收到 POST 请求时,检查请求头中的 user-agent 字段以确认请求来源,并验证签名。如果验证通过,则执行指定的 Shell 命令(例如列出项目目录下的文件),并将结果返回给 Gitee
启动服务器¶
使用以下命令启动服务器,监听地址为 http://localhost:5330/update,仅接受 POST 请求。
Gitee 网页端配置¶
创建仓库¶
登录 Gitee,创建一个新的仓库用于存放项目代码。
配置 Webhooks¶
- 进入仓库设置页面,找到 Webhooks 选项
- 配置 Payload URL 为服务器监听的地址(如
http://your-server-ip:5330/update) - 设置签名密钥,确保与服务器端配置的 secret 一致
- 选择触发事件为 push,即当有新的提交推送到仓库时触发
- 激活 Webhook,并点击添加完成配置
本地配置¶
安装并配置 Git¶
确保本地已安装 Git,并正确配置用户名和邮箱。
添加 SSH 密钥¶
生成 SSH 密钥对,并将公钥添加到 Gitee 账户中,以便安全地与远程仓库通信。
创建本地项目¶
在本地创建一个新的项目目录,并初始化 Git 仓库。
关联远程仓库¶
将本地仓库与 Gitee 上的远程仓库关联起来,使用以下命令:
提交并推送代码¶
修改本地项目内容,提交更改到本地仓库,然后使用以下命令将代码推送到远程 Gitee 仓库,触发 Webhook 执行:
安全注意事项¶
- 环境变量:将密钥存储在环境变量中,而不是硬编码在代码中
- 端口安全:确保 Webhook 端口(如 5330)在生产环境中受到适当的防火墙保护
- 权限控制:限制执行 Shell 脚本的权限,避免安全风险
- 请求验证:始终验证 Webhook 请求的签名和来源
- 日志监控:定期检查日志文件以监控 Webhook 的使用情况
通过以上步骤,可以成功实现从本地推送代码到 Gitee 仓库,并触发服务器端的自动化操作。