Github Webhooks与Koa2实现简单的前端自动化部署

Github Webhooks与Koa2实现简单的前端自动化部署

前言

因为博客使用 Gatsbyjs 进行搭建,Markdown 文章写完之后会打包成静态 HTML 文件,文章是存在项目文件中并非数据库中,所以每次进行博客文章更新后,都需要重新进行打包构建。在进行 git push 后,我需要连到云服务器中,然后进入项目目录,执行 git pull / npm run clean / npm run build 等命令。

为了简化该操作,通过使用 Github 的 Webhooks 服务,在服务端监听 git push 事件,然后自动执行编写好的脚本从而实现自动化构建部署。以后 Git push 后就无需再进行后续操作,由脚本完成。

目前主流的自动化构建部署工具可以选择 Jenkins,Jenkins 是一个持续集成管理平台,提供超过 1000 个插件来支持构建、部署、自动化, 满足任何项目的需要。但由于 Jenkins 是基于Java环境,而且功能过于强大,对于个人的项目来说有点大材小用,而且加重个人服务器资源的压力。所以没有采用 Jenkins 进行自动化构建部署,而是采用自己编写的Koa2服务来接收 Github Webhook 来实现简单自动化构建。

配置 Webhooks

  1. 进入自己需要监听 Push 请求的 Github 仓库,点击 Settings => Webhooks => Add webhook Create Webhook
  2. 填写自己服务器请求地址,配置 Secret(用于后面的 Sha1 解码验证),并选择要监听的事件,本次只监听 push Webhook form
  3. 进入到对应 Webhooks 详情,下方可以查看到每个请求记录,点击 Redeliver 可以重新发送该请求。 Redeliver

后端 Koa2 服务

后端的 Koa2 服务是本功能的最重要的环节。其处理流程:

  1. 接收 Github Webhooks 的 Post 请求
  2. 提取请求头与请求 Body 信息,并验证是否缺少必要参数
  3. 使用 Crypto 的 HMACSHA1 算法,将服务端设置的密钥对请求 Body 进行哈希编码,然后判断解码出来的与请求头中的 x-hub-signature 是否匹配,防止有他人对自己的服务器发送了伪造的非法请求或篡改了 Github 原请求。
  4. 针对不同 Git 事件与 Git 仓库使用 Nodejs 的 child_process 执行不同的脚本文件
const Router = require("koa-router");
const Response = require("../utils/response");
const crypto = require("crypto");
const { exec } = require("child_process");

const logger = require("../utils/log");
const { webhookSecret } = require("../config/config");

const r = new Response();
const router = new Router();

// router的路由路径在Github配置webhook时配置,webhook为向该路径发送请求
router.post("/****", async (ctx) => {
  const requestData = ctx.request.body;
  const sig = ctx.headers["x-hub-signature"];
  const event = ctx.headers["x-github-event"];
  const id = ctx.headers["x-github-delivery"];
  if (!sig || !event || !id) {
    ctx.body = r.error(310, "No Github hook headers");
    return;
  }
  if (!["ping", "push"].includes(event)) {
    ctx.body = r.error(311, "Gihub Hook events not allow");
    return;
  }
  const { repository, sender } = requestData;
  if (!repository || !sender) {
    ctx.body = r.error(312, "Missing essential parameters");
    return;
  }
  const { name: repositoryName } = repository;
  logger("接收到Webhook", 1, `event:${event}, respository: ${repositoryName}`);
  const clientSig = `sha1=${crypto
    .createHmac("sha1", webhookSecret)
    .update(JSON.stringify(requestData))
    .digest("hex")}`;
  if (sig !== clientSig) {
    logger("Webhook X-Hub-Signature解码", 0, "解码不匹配");
    ctx.body = r.error(313, "X-Hub-Signature does not match");
    return;
  }
  if (event === "ping") {
    ctx.body = {
      errCode: 200,
      errMsg: "Success",
    };
  } else if (event === "push") {
    ctx.body = {
      errCode: 200,
      errMsg: "Success",
    };
    if (repositoryName === "****") {
      updateBlog();
    }
  }
});

// updateBlog为接收到hook后要执行的操作
const updateBlog = () => {
  exec("****.bat", (err) => {
    if (err) {
      logger("执行****.bat", 0, err);
      return;
    }
    logger("执行****.bat", 1);
  });
};

module.exports = {
  githubWebhookRouter: router,
};

在新建 Webhooks 后,github 会发送一个 ping 事件到目标服务器,所以这里加多了一种 ping 事件的处理(直接返回 200)。

本次我设置了只有 push 事件会发请求,所以只处理了 push 事件,如果设置 Webhooks 监听其他事件,例如 release、issues、star 等,可自行扩展对应功能。

其他说明

  1. 后端可直接使用原生 Nodejs 搭建服务,使用 github-webhook-hanlder 包可快速搭建
  2. 建议设置 Secert 密钥,防止伪造的请求
  3. 本方式适合简单的前端资源自动化部署构建,对于大型的项目还是建议使用 Jenkins 等持续集成工具进行自动化部署
  4. 可监听 Github Webhooks 其他事件,issue、start 等,并通过 Github API 可实现下 Git 仓库机器人等功能
  5. Github Webhooks 请求中有很多有用的信息,例如多人项目中你可以记录是由谁 push 的,或者处理的是哪个分支等,都可以提取出来进行不同的处理。
  6. 脚本可使用 Shell 编写再由 Nodejs 的 child_process 去执行,也可以直接编写 nodejs 命令直接去执行文件操作。社区也提供很多类似shelljsexec-sh等 NPM 包可以更优雅的编写脚本命令。
  7. 这种方式适合将网站自动部署到自己的个人服务器,如果没有服务器可以采用Github Page方式部署,这时候可以利用Github Action去实现,具体后面再写一篇文章说明。

以上内容未经授权请勿随意转载。