Node + Git + Webhook 自動化部署

前言

以前斷斷續續在重構一個項目,而後發現功能一開始設計太多了,可能須要花大量時間來增長,可是核心功能基本完成,因而想着能不能半上線狀態,而後經過更新提交git,讓服務器部署自動更新。這以前接觸過git hook是能夠實現的,所以這裏記錄一篇文章邊搗鼓邊寫。php

正文

準備

首先肯定須要完成的內容,明確需求:html

1.監聽指定 git 提交
2.執行指定多個腳本

並且由於我這邊是須要分別對clientserver 目錄分別執行部署命令。因此須要特別處理。前端

首先去域名管理那邊增長一條A記錄指向新的項目名稱.由於Webhooks是須要外網域名的,所以先提早加一條。new.xxx.comnode

而後就是須要對服務器上Nginx作轉發配置。nginx

個人nginx是好久以前配置的git

etc/nginx/conf/vhost裏增長一個文件,裏面寫入以下內容:github

server
        {
                listen 80;
                server_name new.xxx.com;
                index index.html index.htm index.php default.html default.htm default.php;

                location / {

                        proxy_pass http://127.0.0.1:8801;
                }
                access_log off;

        }

將本地的8801轉發出去。web

而後重啓一下Nginx nginx -s reloadmongodb

而後git clone 倉庫typescript

由於項目用了mongodb數據庫:
須要建立一個對應的數據庫並添加權限。

1. 切換數據庫到 abc
2. 指定了數據庫 abc ,擁有權限: userAdmin
 db.createUser(
   {
     user: "123",
     pwd: "123",
     roles: [ { role: "userAdmin", db: "abc" } ]
   }
 )
 3. 驗證下上面建立的帳號 123

 db.auth('123','123')
 => 1

配置webhook

這個實際上是最簡單的,只要在你的github對應的項目倉庫右側選擇settings

而後選擇webhooks

imgn

選擇add webhook

而後按照以下配置便可:

imgn

這裏須要記住你本身設置的secret 以及你定義的推送動做,我這裏是pushCode

開始寫腳本

寫以前先來看下最終的目錄結構:

├── README.md
├── clean.sh // 清理緩存,而且執行git命令
├── client
│   ├── autoClient.sh // client端自動監聽
│   ├── build
│   ├── config
│   ├── package.json
│   ├── public
│   ├── scripts
│   ├── src
│   ├── tsconfig.json
│   ├── tsconfig.test.json
│   ├── tslint.json
│   ├── www
│   ├── yarn.lock
├── deploy
│   └── index.js // 監聽webhook事件而後依次執行 clean.sh autoClient.sh autoServer.sh
├── package.json
├── server
│   ├── autoServer.sh // server端自動監聽
│   ├── dist
│   ├── package.json
│   ├── src
│   ├── tsconfig.json
│   ├── tslint.json
│   └── yarn.lock

由於項目緣由,腳本還須要作一些定製。
首先是client端,由於前端是用了typescript + React全家桶 因此打包起來特別慢,當在服務器build的時候,由於阿里雲內存給的不夠,因此會很卡。基於這個考慮,是打算本地bulid完以後,推送到git上,服務器去git pull

並且前端還有個考慮是用什麼跑前端代碼。由於服務器沒裝相似服務,所以打算用node框架koa起一個HTTP服務來跑。

代碼以下:

www目錄中

app.js

const Koa = require('koa')
const morgan = require('koa-morgan')
const path = require('path');
const static = require('koa-static')
const fs = require('fs')
const app = new Koa();
// logger
app.use(morgan(':remote-addr - :remote-user [:date[clf]] ":method :url HTTP/:http-version" :status :res[content-length] :response-time ms'));
// static assets
app.use(static(path.join(__dirname,'../build')));
//異步讀取文件的形式
// app.use(async (ctx,next) =>{
//     ctx.type = 'html';
//     ctx.body = await fs.createReadStream(path.resolve(__dirname, '..', './build', 'index.html'));
// })
module.exports = app;
index.js

'use strict';
const app = require('./app');
const PORT = process.env.PORT || 8801;
console.log('client start')
app.listen(PORT, () => {
  console.log(`App listening on port ${PORT}!`);
});

這樣經過命令node ./www/index.js可以監聽同級build目錄。

固然這比較粗暴,還須要慢慢改進。

咱們須要在client目錄下創建autoClient.sh

#! /bin/bash
cd ./
echo 'client build'
kill -9 $(lsof -i:8801 |awk '{print $2}' | tail -n 2) 
node ./www/index.js

用來自動執行監聽動做。

由於屢次推送監聽的前端端口一致,若是不處理會報錯。須要先根據端口號8801結束進程而後從新開啓服務。

而後是server端,由於整個後端是用koa完成的,項目比較小,在服務端即時編譯花費不了多少時間,所以直接執行yarn start( "start": "yarn run build && yarn run watch",)用來編譯和監聽。

server目錄裏創建autoServer.sh

#! /bin/bash
cd ./
echo 'server start'
kill -9 $(lsof -i:8866 |awk '{print $2}' | tail -n 2) 
yarn run start

一樣咱們須要在執行監聽以前結束上一個端口的進程。

而後咱們來看clean.sh 這個shell腳本是用來清理client目錄下build文件夾。

#! /bin/bash
rm rf ./client/build
git reset --hard origin/master
git clean -f
git pull

能夠看到 先清理了緩存而後再向服務器拉取代碼.

最後咱們來看部署的腳本deploy/index.js

var spawn = require('child_process').spawn
var http = require('http')
var spawn = require('child_process').spawn
var createHandler = require('github-webhook-handler')
var handler = createHandler({ path: '/pushCode', secret: 'xxx' }) // 根據git上webhook的配置填寫
http.createServer(function (req, res) {
  handler(req, res, function (err) {
    res.statusCode = 404;
    res.end('no such location')
  })
}).listen(7777)

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

// 監聽 push 事件
handler.on('push', function (event) {
  console.log('Received a push event for %s to %s',
    event.payload.repository.name,
    event.payload.ref)
    init() // 每次拉取都從新監聽
}
)
function rumCommand( cmd, args, cwd, callback ) {
  var child = spawn( cmd, args, {cwd: cwd} )
  var response = ''
  child.stdout.on('data', function( buffer ){ response += buffer.toString(); })
  child.stdout.on('end', function(){ callback( response ) })
}

function init() {
  rumCommand('sh', ['../clean.sh'], './' ,function( result ) { // 清理緩存
    console.log(result)
  })

  rumCommand('sh', ['../server/autoServer.sh'], '../server' ,function( result ) { // cLient端更新
    console.log(result)
  })
  
  rumCommand('sh', ['../client/autoClient.sh'], '../client' ,function( result ) { // server端更新
    console.log(result)
  })

}

init() // 腳本運行第一次默認指向一次

這裏須要聲明的是由於多目錄下執行腳本須要對應的環境。所以才把腳本都分開放。

部署

部署咱們只須要對pm2 start deploy/index.js

而後就能夠在本地開發完以後推送,服務器就能自動拉取代碼而且部署。

相關文章
相關標籤/搜索