使用Web Hooks搭建自动化工作流

首先要介绍一下背景:公司的项目部署还在使用SVN,而我们前端组已经转到Git了,因此Git如何与SVN同步是一个问题。

早前也查过git-svn这个工具,但是对于我们这样只需要单向同步而且每天Git的Commit超过30个的项目来说,速度显得有些不尽人意,于是经过一番Google后,我使用Web Hooks建立了一套自动化的同步流程。

1、什么是Web Hooks

Project web hooks allow you to trigger an URL if new code is pushed or a new issue is created.

You can configure web hooks to listen for specific events like pushes, issues or merge requests. GitLab will send a POST request with data to the web hook URL.

Web hooks can be used to update an external issue tracker, trigger CI builds, update a backup mirror, or even deploy to your production server.

以上取自GitLab官方文档。

简单来说,Web Hooks的作用是在你的Git仓库有任何操作时,向一个远程服务器发送一个POST请求,这个请求包含该操作的一些信息,利用这些信息你可以执行一些任务,从而自动化工作流程。

如下图的设置,你可以跟踪Commit、Tag Push、Issue等操作。

2、如何使用Web Hooks

首先要确定你需要跟踪哪些操作,在我的项目中,由于没有使用GitLab来提issue,也没有开始PR功能,所以我只跟踪了Push events与Tag push events,也就是在有人提交了一个Commit或者推送了一个Tag时才触发。

接下来是搭建一个服务器用来接受请求,我使用的是Express,代码如下

var express = require('express');  
var app = express();  
var bodyParser = require('body-parser');

app.use(bodyParser.json());

app.post('/git-hooks', function(req, res) {  
  res.send('Request received!');
});
app.listen(3000);  
console.log('Listening to port 3000');  

这个只是一个非常基础的服务器,我们把这个服务器启动起来,然后在GitLab的项目中添加一个WebHooks服务器地址,然后Test一下,发现成功了。 添加一个WebHook Test成功!

我们可以看一下这个POST请求的内容,为了避免泄露信息,我贴一下官方文档的内容,可以看到包含了一些仓库及Commit的信息,之后我们会用到一些。

{
  "object_kind": "push",
  "before": "95790bf891e76fee5e1747ab589903a6a1f80f22",
  "after": "da1560886d4f094c3e6c9ef40349f7d38b5d27d7",
  "ref": "refs/heads/master",
  "user_id": 4,
  "user_name": "John Smith",
  "user_email": "john@example.com",
  "project_id": 15,
  "repository": {
    "name": "Diaspora",
    "url": "git@example.com:mike/diasporadiaspora.git",
    "description": "",
    "homepage": "http://example.com/mike/diaspora",
    "git_http_url":"http://example.com/mike/diaspora.git",
    "git_ssh_url":"git@example.com:mike/diaspora.git",
    "visibility_level":0
  },
  "commits": [
    {
      "id": "b6568db1bc1dcd7f8b4d5a946b0b91f9dacd7327",
      "message": "Update Catalan translation to e38cb41.",
      "timestamp": "2011-12-12T14:27:31+02:00",
      "url": "http://example.com/mike/diaspora/commit/b6568db1bc1dcd7f8b4d5a946b0b91f9dacd7327",
      "author": {
        "name": "Jordi Mallach",
        "email": "jordi@softcatala.org"
      }
    },
    {
      "id": "da1560886d4f094c3e6c9ef40349f7d38b5d27d7",
      "message": "fixed readme",
      "timestamp": "2012-01-03T23:36:29+02:00",
      "url": "http://example.com/mike/diaspora/commit/da1560886d4f094c3e6c9ef40349f7d38b5d27d7",
      "author": {
        "name": "GitLab dev user",
        "email": "gitlabdev@dv6700.(none)"
      }
    }
  ],
  "total_commits_count": 4
}

在这台服务器的另一个目录,我放置了一个完整的Git仓库,这里使用Web Hooks是希望在Git接收到一个提交后,能够自动拉取最新代码、跑一次Gulp、然后把所有更改提交到SVN上,另外如果服务器上项目所在的分支与最新Commit的分支不同的话,就不执行任何操作。完整代码如下:

'use strict';

const PORT = 3000;

var express = require('express');  
var app = express();  
var exec = require('child_process').exec;  
var execSync = require('child_process').execSync;  
var bodyParser = require('body-parser');  
var _ = require('lodash');

process.chdir('/home/bruce/TestProject/');

app.use(bodyParser.json());

app.post('/git-hooks', function (req, res) {  
  let ref = execSync('git symbolic-ref HEAD').toString().trim();
  let remoteInfo = req.body;
  console.log((new Date()).toLocaleString());
  console.log(`Before: ${remoteInfo.before}`);
  console.log(`After: ${remoteInfo.after}`);
  console.log(`Commit message: ${remoteInfo.commits[0].message}`);
  console.log(`Author: ${remoteInfo.commits[0].author.name}\n`);

  if (ref === remoteInfo.ref) {
    var modifiedFiles = remoteInfo.commits[0].modified;
    if (_.isArray(modifiedFiles)) {
      var bowerModified = modifiedFiles.some(function (file) {
        return /bower\.json/.test(file);
      });
      bowerModified && execSync('bower install');
      var npmModified = modifiedFiles.some(function (file) {
        return /package\.json/.test(file);
      });
      npmModified && execSync('npm i');
    }
    console.log('Start Build');
    exec('./update.sh', function (error, stdout, stderr) {
      console.log(stdout);
      console.error(stderr);
      if (!error) {
        res.status(200).end();
      } else {
        res.status(500).end();
      }
    });
  } else {
    console.log('分支不匹配, 将不进行构建操作');
  }

  return res.status(200).end({status: 'fail', msg: '分支不匹配'});
});
app.listen(PORT);  
console.log(`Listening to port ${PORT}`);  

在这里我增加了ref的匹配判断,NPM和bower的变化检测(文件变化在最新的GitLab-CE中才有),Shell脚本的运行,这个脚本我就不贴了,大致就是之前说的那些操作,可以按需要进行定制。

3、小结

这样一番简单的部署后,Git与SVN的同步就完全自动化了,完全不需要每次自己登陆服务器输一堆命令,费时费力,同事们都夸我是小机智O(∩_∩)O

工程化是最近非常热门的话题,相信每一个热爱代码的程序员都很喜欢捣鼓工具,我也不例外,所以接下来将会写一系列前端工程化相关的博文,欢迎来踩。

PS:今天刚增加了多说,可以留言了哦。