跳转至

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)。

npm init -y
npm install express body-parser 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 请求。

node index.js

Gitee 网页端配置

创建仓库

登录 Gitee,创建一个新的仓库用于存放项目代码。

配置 Webhooks

  1. 进入仓库设置页面,找到 Webhooks 选项
  2. 配置 Payload URL 为服务器监听的地址(如 http://your-server-ip:5330/update)
  3. 设置签名密钥,确保与服务器端配置的 secret 一致
  4. 选择触发事件为 push,即当有新的提交推送到仓库时触发
  5. 激活 Webhook,并点击添加完成配置

本地配置

安装并配置 Git

确保本地已安装 Git,并正确配置用户名和邮箱。

git config --global user.name "Your Name"
git config --global user.email "your.email@example.com"

添加 SSH 密钥

生成 SSH 密钥对,并将公钥添加到 Gitee 账户中,以便安全地与远程仓库通信。

ssh-keygen -t rsa -b 4096 -C "your.email@example.com"

创建本地项目

在本地创建一个新的项目目录,并初始化 Git 仓库。

mkdir my-project
cd my-project
git init

关联远程仓库

将本地仓库与 Gitee 上的远程仓库关联起来,使用以下命令:

git remote add origin <远程仓库URL>

提交并推送代码

修改本地项目内容,提交更改到本地仓库,然后使用以下命令将代码推送到远程 Gitee 仓库,触发 Webhook 执行:

git add .
git commit -m "Initial commit"
git push origin main

安全注意事项

  1. 环境变量:将密钥存储在环境变量中,而不是硬编码在代码中
  2. 端口安全:确保 Webhook 端口(如 5330)在生产环境中受到适当的防火墙保护
  3. 权限控制:限制执行 Shell 脚本的权限,避免安全风险
  4. 请求验证:始终验证 Webhook 请求的签名和来源
  5. 日志监控:定期检查日志文件以监控 Webhook 的使用情况

通过以上步骤,可以成功实现从本地推送代码到 Gitee 仓库,并触发服务器端的自动化操作。