利用 Github 網絡鈎子實現自動化部署


GitHub 的網絡鈎子(webhook)功能,可以很方便的實現自動化部署。本文記錄了使用 Node.js 的開發部署過程,當項目的 master 分支被推時,將在服務器進行自動部署,完整代碼見 GitHub

添加網絡鈎子

  1. 在 GitHub 的相應項目首頁,點擊右上角菜單 Setting, 點擊左側菜單 Webhooks,點擊右上角按鈕 Add webhook

  2. 設置 Payload URL 為接收事件的地址,Content type 建議選擇 applicaiton/jsonSecret 可選填任意字符串,Which events would you like to trigger this webhook? 設為 Just the push event.,勾選 Active,點擊下方的 Add webhook 按鈕

開發處理請求

接收請求

使用 Node.js 建立一個 http 服務器,接收 POST 請求並處理其提交數據

const { createServer } = require('http');
const port = process.env.GITHUB_WEBHOOK_PORT || '3000';

const server = createServer((req, res) => {
  if('POST' === req.method){
    let body = '';
    req.on('data', chunk => {
      body += chunk.toString();
    });
    req.on('end', () => {
    });
  }
})

server.listen(port, () => {
  console.log(`Listening on ${port}`);
});

如果需要更改默認端口 3000,可以先運行以下命令添加環境變量(NUMBER 為任意端口)

export GITHUB_WEBHOOK_PORT=NUMBER

解析 Body

在 req 的 end 事件處理器中,把字符串 body 解析成對象

    req.on('end', () => {
      try{
        body = JSON.parse(decodeURIComponent(body).replace(/^payload=/, ''));
      }catch(e){
        console.log(e)
      }

如果 Content type 設置為 applicaiton/json,只需要 body = JSON.parse(body) 即可,以上代碼兼容了 Content type 設置為 application/x-www-form-urlencoded 的情況

拉取更新

根據 body 的 push 負載,提取項目和分支信息,如果是 master 分支,則執行進入對應項目,拉取分支的命令

      if('object' === typeof body){
        if('refs/heads/master' === body.ref){
          const { exec } = require('child_process');
          const command = `cd ../${body.repository.name} && git pull origin master`;
          exec(command, (error, stdout, stderr) => {
          });

注意這里的項目所在的目錄,與此應用所在的目錄,是在同一個父目錄下的,如果不是可以相應調整命令的進入路徑

驗證密鑰

以上步驟已經實現了自動拉取更新,不過存在安全性的問題,因為不僅僅 GitHub 可以發送這樣的請求,所以最好設置 Secret 以進行安全驗證

const secret = process.env.GITHUB_WEBHOOK_SECRET || '';
...
    req.on('end', () => {
      if('' !== secret){
        const { createHmac } = require('crypto');
        let signature = createHmac('sha1', secret).update(body).digest('hex');
        if(req.headers['x-hub-signature'] !== `sha1=${signature}`){
          console.log('Signature Error');
          res.statusCode = 403;
          res.end();
          return;
        }
      }

運行應用前,先運行以下命令增加密鑰變量(STRING 為任意字符串)

export GITHUB_WEBHOOK_SECRET=STRING

  • 設置了 Secret 后,GitHub 在發送請求時,會在請求頭增加 x-hub-signature 為 sha1=SIGNATURE, 其中 SIGNATURE 為 body 的 密鑰為 Secret,算法為 sha1 的 HMAC 16 進制值
  • 通過對 Secret 的檢驗,可以確保只有知道了 Secret,才能發送正確的帶 x-hub-signature 頭的請求,否則將拒絕請求
  • 以上代碼兼容了不設置 Secret 的情況,即如果沒有增加變量 GITHUB_WEBHOOK_SECRET,則按原有邏輯處理,不會進行檢驗

本地鈎子構建

如果項目在拉取更新后需要構建,那么可以 command 變量后面加上構建命令,例如 && npm run build,但是不同項目的構建命令有可能是不一樣的,而且有的項目的構建命令可能還比較復雜,這些情況下可以通過設置 git 的本地鈎子進行處理

cd /PATH/TO/PROJECT/.git/hooks
nano post-merge

#!/bin/sh
SHELL_SCRIPT

chmod +x post-merge

  • 其中 /PATH/TO/PROJECT/ 為項目的目錄位置,SHELL_SCRIPT 可以為任意 Shell 腳本
  • 因為 git pull 是 git fetch 和 git merge 的組合,所以拉取更新會觸發 post-merge 鈎子
  • 默認新增的文件是沒有執行權限的,所以需要通過 chmod 增加 x

部署應用上線

應用部署上線需要實現持久化和自動化,即項目應該一直在運行,如果服務器重啟,項目應該自動啟動

變量自動創建

/etc/profile.d/ 里的變量創建腳本會在服務器重啟時自動運行,所以添加一個設置腳本進去

nono /etc/profile.d/github-webhook.sh

export GITHUB_WEBHOOK_PORT=NUMBER
export GITHUB_WEBHOOK_SECRET=STRING

運行以下命令可以使變量創建馬上生效

source /etc/profile

pm2 運行應用

pm2 可以確保 Node 應用的持續運行,並可通過配置實現監控和熱更新等功能

npm install pm2 -g
pm2 start app.js --name github-webhook

重啟自動運行

pm2 還內置支持配置自啟動原有應用,通過以下命令實現

pm2 startup
pm2 save

pm2 startup 會創建並開啟開機自動運行的服務, pm2 save 會保存當前的 pm2 運行應用,作為重啟后的恢復內容

總結

在基於 GitHub webhook 的自動化部署中,主要使用了以下技術:

  • Node.js 的 http,child_process 和 crypto 模塊
  • Git 的 post-merge Shell 鈎子
  • profile 的自動變量設置和 pm2 工具


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM