Node.js で gitHub の Webhook を動かして快適なデプロイ生活を送らせて頂いてるのですが、自分だけ快適というのもあれなので、感謝の気持ちを込めて github にパブリックドメインで公開しました。発展途上でもあるので自己責任とバグ報告とかよろしくということで。
GitHub: node-git-webhook
conf.js
module.exports={
"example.net": {
about: "github webhook for example.net",
secret: "Your シークレット for example.net"
}
}
git-hook.js
'use strict';
/*
github リポジトリへのpushなどだけでサーバー側に自動デプロイする。
これは、例えば example.netというレポジトリのdevブランチへのpushをすると
サーバー側の /pathTo/example.net 以下の内容を git からpull して更新する
@see https://github.com/toshirot/node-git-webhook
*/
// import
const fs = require('fs');
const https = require('https');
const crypto = require('crypto');
const exec = require('child_process').exec;
// https server setting
const PORT = '1234';
const HOST='example.net';
const pemPath='/etc/letsencrypt/live/'+HOST;// path of letsencript pem
const CERT = fs.readFileSync(pemPath+'/fullchain.pem');
const KEY = fs.readFileSync(pemPath+'/privkey.pem');
// config
const conf=require(__dirname+'/conf');
const SECRET = conf[HOST].secret;
// git settings
const BRANCHName='dev';// 'master'|'dev'
const targetBRANCH = 'refs/heads/'+BRANCHName;//'refs/heads/master'|'refs/heads/dev'
const targetRepositoryDir='/home/hoge/'+HOST; // HOST名/public_html などが多いかな?
const pullStr='sudo git pull origin '+BRANCHName;
const REPOSITORY_NAME = HOST;
// log file
const logFilePath = '/home/hoge/webhook/github-webhook-'+HOST+'.log';
// ----------------------------------------------------------------------------
// HTTPSサーバー
//
const option={
cert: CERT,
key: KEY,
};
const server = new https.createServer(option, function (req, res){
let payload='';
req.on('data', function(chunk) {
payload+=chunk.toString();
// chk SECRET
if(!chkSECRET(req, payload))return;
// parse payload
try{
payload=JSON.parse(payload);
} catch(e){
console.log('parse err', e)
}
// chk repositoryName
if(!chkRepositoryName(payload))return;
// chk target BRANCH
if(!chkBranchName(payload))return;
// do pull
exec(pullStr, { cwd: targetRepositoryDir }, (error, stdout, stderr) => {
if (error) {
writeLog(`exec error: ${error}`);
return;
}
writeLog(`3 stdout: ${stdout}`);
writeLog(`3 stderr: ${stderr}`);
});
});
res.end();
}).listen(PORT);
// ----------------------------------------------------------------------------
// シークレットのチェック
// @return true|false
//
function chkSECRET(req, data){
// console.log(444,'chkSECRET')
let sig = ''
+ 'sha1='
+ crypto
.createHmac('sha1', SECRET)
.update(data)
.digest('hex');
if (req.headers['x-hub-signature'] !== sig){
return false;//シークレットが違えばパス
} else {
return true;
}
}
// ----------------------------------------------------------------------------
// リポジトリ名のチェック
// @return true|false
//
function chkRepositoryName(payload){
let repositoryName = payload.repository.name;
console.log(555,'chkRepositoryName', REPOSITORY_NAME===repositoryName, REPOSITORY_NAME, repositoryName)
if(REPOSITORY_NAME!==repositoryName){
return false;//リポジトリ名が違えばパス
} else {
return true;
}
}
// ----------------------------------------------------------------------------
// ブランチ名のチェック
// @return true|false
//
function chkBranchName(payload){
console.log(666, 'chkBranchName', payload.ref===targetBRANCH, payload.ref, targetBRANCH)
if(payload.ref!==targetBRANCH){
return false;//ブランチ名が違えばパス
} else {
return true;
}
}
// ----------------------------------------------------------------------------
// Date関連
//
function getTimesInt(str, date){
/* e.g.
getTimesInt('Y');//2017
var H = getTimesInt('H');
var Mi= getTimesInt('Mi');
var HM= H+':'+Mi; //'8:30'
var W = getTimesInt('W');
var week = new Array('日', '月', '火', '水', '木', '金', '土');
week[W];
*/
var curr = (date)?new Date(date):new Date();
var Y = +curr.getFullYear()
var M = +curr.getMonth()+1
var D = +curr.getDate()
var H = +curr.getHours()
var Mi = +curr.getMinutes()
var S = +curr.getSeconds()
var Ms = +curr.getMilliseconds()
var W = +curr.getDay()
switch(str){
case 'Y': return Y;break;
case 'M': return M;break;
case 'D': return D;break;
case 'H': return H;break;
case 'Mi': return Mi;break;
case 'S': return S;break;
case 'Ms': return Ms;break;
case 'W': return W;break;
default: return curr;
}
str= date=curr=Y=M=D=H=Mi=S=Ms=W=null;
}
function zero(num){//e.g. 3->'03'
return num<10?'0'+num:''+num;
}
function getYMDHMiS(date){
var Y = getTimesInt('Y',date);
var M= getTimesInt('M',date);
var D= getTimesInt('D',date);
var H = getTimesInt('H',date);
var Mi= getTimesInt('Mi',date);
var S= getTimesInt('S',date);
return Y+'-'+zero(M)+'-'+zero(D)+' '+zero(H)+':'+zero(Mi)+' '+zero(S); //'2017-01-04 08:30 05'
}
// ----------------------------------------------------------------------------
// logの出力
//
const writeLog = function (data){
ifNotExistMkNewFile(logFilePath);
console.log(222, getYMDHMiS() + ' ' +data);
fs.appendFile(logFilePath, getYMDHMiS() +' '+ data+'\n', function (err) {
if (err) {
console.log(getYMDHMiS() +'error:'+ err);
writeLog(getYMDHMiS() +'error:'+ err+'\n');
throw err;
}
});
}
// ----------------------------------------------------------------------------
// logFilePathファイルが存在しなければ作る for log
//
function ifNotExistMkNewFile(file){
if(!isExistFile(logFilePath)){
fs.writeFile(logFilePath, '', function (err) {
if (err) {
throw err;
}
})
}
}
// ----------------------------------------------------------------------------
// ファイルの存在チェック for log
// @return true|false
//
function isExistFile(file) {
try {
fs.statSync(file);
return true
} catch(err) {
if(err.code === 'ENOENT') return false
}
}
概要
github リポジトリへのpushなどだけでサーバー側に自動デプロイする。
例えば example.netというレポジトリのdev-2fブランチへのpushをするとサーバー側の /pathTo/example.net 以下の内容を git からpull して更新する。
設置例
pathTo/
├── git-hook.js
└── conf.js
設定手順例
対象リポジトリ名
example.net
対象ブラインチ名
dev-2f
対象サーバーのパス例
pathTo/
└── example.net
├── html
├── server
└── etc..
githubの設定
まず、githubリポジトリで Settings > Webhooks に入り、「Add Webhook」ボタンを押すとGithubのパスワード入力が求められるので入力すると、下図のような「Webhooks /Add webhook」ページが現れます。
なお、Webhookのドキュメントは下記にあります。
GitHub Developer Webhooks
「Webhooks /Add webhook」ページの入力手順は下記の通り
- github > settings > WebHook の Payload URL に
https://<HOST名>:<PORT名>
- github > settings > WebHook の Content type
application/json
- github > settings > WebHook の Secret
conf.js に書いたのと同じシークレット
- github > settings > WebHook の SSL verification
今時は、✔️Enable SSL verification
※要SSL設定 例えばLet's Encriptなどで。
- github > settings > WebHook の Which events would you like to trigger this webhook?
このWebhookをトリガーしたいイベントはどれですか?
✔️Just the push event.
- github > settings > WebHook の Active
✔️Active
対象デプロイサーバー側の設定
- ssh-keygenで秘密鍵、公開鍵を作る
sudo ssh-keygen -t rsa -b 4096 -C "Your@e-mail" -f /root/.ssh/id_rsa_github_example.net
これで
/root/.ssh/id_rsa_github_example.net 秘密鍵
/root/.ssh/id_rsa_github_example.net.pub 公開鍵
が生成される
(※ ここでは、 https の ssl の鍵に root権限 が必要なので sudo で動かすことを前提にしています。
sudo 以外で動かすときはその部分をそれぞれの権限用に読み替えてください。)
この公開鍵を github settings > Deploy keys へ追加する
- プライベートリポジトリの場合は、.ssh/configに鍵のパスを登録しておく
sudo vi /root/.ssh/config
# example.net
Host example.net
HostName github.com
Port 7890
IdentityFile /root/.ssh/id_rsa_github_example.net
User git
IdentitiesOnly yes
- その他、gitにパスワード を毎回聞かれる場合問題
git パスワード を毎回聞かれる問題の解決方法
git remote set-url origin git@github.com:<アカウント名>/<リポジトリ名>.git
足りないモジュールを読み込んでおく
npm i crypto --save
暗号関連のメジャーなモジュールです。シークレットのチェックに使っています。
対象デプロイサーバー側での起動とデーモン
pm2やforeverなどでgit-hook.jsを起動します。
sudo pm2 start /pathTo/git-hook.js
参考:pm2やnodeをrootで動かすには(Ubuntu で NVMを使うケース)
//////////////////////////////////////////////
nvmでnode.jsインストール
sudo apt install git-core
git clone git://github.com/creationix/nvm.git ~/nvm
. ~/nvm/nvm.sh
echo ". ~/nvm/nvm.sh" >> ~/.bashrc
//////////////////////////////////////////////
node.jsインストール
ここではv10の最新を入れる
$ nvm install v10
//////////////////////////////////////////////
pm2 インストール
npm install pm2 -g
sudo pm2 start hoge.js したあと自動起動するには
sudo pm2 startup ubuntu
sudo pm2 save
//////////////////////////////////////////////
$ which node
/home/hoge/nvm/versions/node/v10.15.1/bin/node
$ which pm2
/home/hoge/nvm/versions/node/v10.15.1/bin/pm2
wssをrootで実行するためにリンクを張っておく
sudo ln -s /home/hoge/nvm/versions/node/v10.15.1/bin/node /usr/bin/node
sudo ln -s /home/hoge/nvm/versions/node/v10.15.1/bin/pm2 /usr/bin/pm2
//////////////////////////////////////////////
g++ or c++ などが不足してることもあるので
sudo apt install build-essential
sudo apt install libssl-dev
License
Public domain
参考サイト:
Node.jsサーバーでGitHubhook受け取ってpushだけで簡単にページ反映させる https://qiita.com/kokoyoshi/items/c1c6050e8ef09aa78132 #Qiita