logo

使用 Webhook 实现前端项目的自动化部署

Fri Apr 01 2022 Posted 2 years ago

填一下上次部署网站遗留的坑:如何进行前端项目的自动化部署?

自动化部署的解决方案有很多,比如 JenkinsDroneGitLab CI/CDcron 定时任务Webhook等。最开始我尝试使用 Jenkins 进行自动化部署,但是配置到一半我发现 Jenkins 对于我来说太“重”了,绝大部分的功能对我来说都意义不大,毕竟我现在只是想自动化部署一个极其简单的博客,用上 Jenkins 有些买椟还珠了。后来我发现使用 cron 设置定时任务好像还挺简单的,但是有一个显而易见的缺点:无法判断是否需要重新部署,因此会进行很多无意义的操作。最后思来想去还是决定使用 Webhook 来进行自动化部署,它有很多的优点:配置简单,十分轻量,功能不局限于自动化部署。所以还是非常值得学习一下的。

什么是 Webhook? #

Webhook 的概念早在 2007 年就被提出了(但我才刚知道有这么个概念),首先要说明一点,Webhook 并不是一种工具,而是一个概念或者说方法

简单理解的话,Webhook 就是一种反向 API 机制,类似于 trigger。API 的使用场景如下:

  1. 服务器端提供一个 API
  2. 客户端请求这 API
  3. 服务器端接收到请求,并向客户端返回数据
  4. 客户端拿到数据进行对应的处理

而 Webhook 的使用场景则相反:

  1. 客户端给服务端提供一个 Webhook URL
  2. 当某些事件被触发时,服务器端会主动向客户端提供的 URL 推送数据
  3. 客户端拿到数据进行对应的处理

其实就是客户端由原本的主动请求者变为了被动接收者。

image-20220401144700498

Webhook 的应用场景 #

Webhook 的应用场景非常广泛,比如安全性要求非常高的支付功能,第三方平台的鉴权、登录,资源同步等。

想象一下这个场景:客户端现在向服务器端上传了一个比较大的文件,而这个文件需要在服务器端进行处理之后才返回给客户端,恰巧这时候服务器端有很多的文件待处理,需要排队等待。如果按照传统做法,客户端需要不断地轮询服务器来获取文件的处理状态,但如果使用 Webhook 就不需要再进行轮询了,只需要让服务器端在完成处理后向客户端提供的 Webhook URL 发送一个请求并返回处理后的文件就可以了。be like:

image-20220401152951862

使用 Webhook 实现自动化部署 #

简单介绍完 Webhook 之后,我们开始回归正题:自动化部署。

GitHub 本身就提供了 Webhook 的设置,所以我们可以很方便的监听远程仓库的各种事件,比如 push, folk, pull-request 等。所以我们可以大体想到这么一个方案:在服务器上搭建一个 Node 服务用来监听 GitHub 发送过来的信息,每当远程仓库有 commit 时就让 GitHub 向我们的服务器发送一个请求,服务器接收到请求后会自动运行提前准备好的 bash 脚本,完成项目的自动化部署。有了思路之后我们开始一步步进行。

搭建 Node 服务 #

秉承“尽量不要重复造轮子”的原则,我们可以使用一个已经封装好的库进行 Webhook 的处理,这个库的代码并不多,逻辑也比较容易懂,建议读一读。

首先在服务器上创建 Node 服务的根目录,然后在根目录下运行 npm init -y 进行初始化,运行 npm i github-webhook-handler 安装依赖,安装完成后,我们可以在根目录创建一个 index.js 文件,并写入以下内容:

const http = require('http')
const createHandler = require('github-webhook-handler')
const handler = createHandler({ path: '/webhook', secret: 'yourSecret' }) // secret 相当于一个密码,可以自行设置

http.createServer((req, res) => {
  handler(req, res, (err) => {
    res.statusCode = 404
    res.end('no such location')
  })
}).listen(7777, () => console.log('server listening on port 7777'))

handler.on('error', (err) => {
  console.error('Error:', err.message)
})

// 监听 push 事件
handler.on('push', (event) => {
  console.log(`Received a push event from ${event.payload.repository.name} to ${event.payload.ref}`)
})

接着我们可以使用 pm2 启动服务器 pm2 start index.js

启动完成后我们就需要到 GitHub 上设置 Webhook 了

Webhook 设置 #

首先进入到代码所在的 GitHub 仓库,点击 Setting,找到 Webhooks 选项卡,点击 Add webhook 来新建一个 Webhook

image-20220401160047209

新建界面如下:

image-20220401160329887

Payload URL 就是我们的服务器的请求路径,也就是服务器 IP 地址,加上我们上面设置的端口号最后加上我们设置的 path

Content type 一定要选择 application/json,这一点在 github-webhook-handler 的文档中也有提到。

Secret 就是我们之前设置的 secret,保持一致就可以了。

因为我们只需要自动化部署的功能,所以只需要监听 push 事件就可以了。日志也勾上,里面会有很多 Webhook 的配置,可以拿来参考做一些自定义的东西。

创建完成后,GitHub 会自动测试 Webhook 的可用性,如果显示的是绿色对号,那说明我们的配置没有问题,但如果是红色叉号,那就说明我们的配置存在问题,这里有一个小坑,如果你直接去修改这个 Webhook 的配置,即使修改正确这里的状态也不会改变,因为 GitHub 只会在创建 Webhook 的时候进行连接测试,所以如果你想知道自己修改后的配置是否正确,建议删除原来的 Webhook 然后重新创建,这时候就能知道修改后的配置是否正确了。

image-20220401162119764

编写部署脚本 #

现在我们已经可以监听远程仓库的 push 事件了,所以我们只需要在接收事件后运行一下脚本就可以实现自动化部署啦~将 index.js 的内容稍作修改:

const http = require('http')
const spawn = require('child_process').spawn
const createHandler = require('github-webhook-handler')
const handler = createHandler({ path: '/webhook', secret: 'yourSecret' }) // secret 相当于一个密码,可以自行设置

http.createServer((req, res) => {
  handler(req, res, (err) => {
    res.statusCode = 404
    res.end('no such location')
  })
}).listen(7777, () => console.log('server listening on port 7777'))

handler.on('error', (err) => {
  console.error('Error:', err.message)
})

// 监听 push 事件
handler.on('push', (event) => {
  console.log(`Received a push event from ${event.payload.repository.name} to ${event.payload.ref}`)
  runCmd('sh', ['./deploy.sh'], text => console.log(text))
})

// 运行命令
function runCmd(cmd, args, callback) {
  const child = spawn(cmd, args)
  let resMsg = 'Deploy End'

  child.stdout.on('data', buffer => resMsg += buffer.toString())
  child.stdout.on('end', () => { callback (resMsg) })
}

然后在 index.js 的同级目录下新建 deploy.sh 文件:

echo "Start deployment"
cd ~/www/nuxt-blog
echo "stop process"
pm2 stop nuxt-blog
echo "pulling source code..."
git pull
echo "check dependencies"
npm install
echo "build /.nuxt"
npm run build
echo "start process"
pm2 start
echo "Finished."

脚本的内容仅供参考,请自行修改

完成以上步骤之后,我们可以自行测试一下(测试前不要忘记重启服务),本地向远程仓库提交一些修改,比如样式的修改等,然后过一小会去访问网站看一下修改是否生效。

至此,我们已经完成了自动化部署的需求,其实 Webhook 的用途还有很多,比如有人向仓库提了 issue 可以自动给我们发送提醒邮件,或者也可以通过一些第三方的平台进行个性化提醒,例如钉钉的智能机器人。建议有时间的话可以多多探索~