最近发现博客的图片和文章数量增长后,hexo生成和发布的时间越来越长,而且换了电脑后hexo的环境又要倒腾一遍,实在是消耗时间。

但是又不想换简书这种单纯的写作平台,毕竟博客还是需要一点个性化的元素。

那能不能我写完markdown然后commit之后就可以自动生成静态页面自动发布呢?当然懒人们有很多种办法。研究了一下一般都是用webhook来实现,目前大概有几种方式:

hexo源文章仓库托管在github上然后编成静态文件分发到github和coding.net的pages服务上。一般用Travis CI来做集成,转一张kcen做的时序图
travis_github.jpg
流程简化就是本地push源文章到github,触发webhook然后traivs帮你自动构建生成静态文件然后推送到page服务上。

为了追求速度,我把hexo文章仓库托管在coding.net的上,而且coding的私有项目不收费,也提供了webhook的功能.

在coding上也有像travis一样的国内服务,开始收费的flow.ci不予考虑, 看到这篇博客daocloud好像是个不错的选择,但是在部署的时候还是要暴露私钥给第三方平台,也没有选择这种方案。

正好手上有一台闲置的vps,就选择了云主机上搭建一个hexo的环境,push的时候触发coding的webhook,回调云主机上用nginx反代的node服务,然后执行一个shell脚本拉代码自动编译发布
travis_github.jpg

基本流程没有变,只是把第三方的服务换成了自己的vps,把需要的密钥换成了部署公钥。

coding的部署公钥在此部署
travis_github.jpg

webhook在此配置
travis_github.jpg

vps上的项目目录,hexo是clone下来的博客源码,webhook下的是node server,部署公钥放最顶层

├── blog_rsa
├── blog_rsa.pub #部署公钥
├── hexo #博客源码
│   ├── deploy.sh
│   ├── source
└── webhook # node服务
├── config.js
├── index.js
├── logs

因为部署公钥没有放.ssh目录下所以要在~/.ssh/config下指定一下

Host git.coding.net
User xxx@email.com
PreferredAuthentications publickey
IdentityFile /data/publish/blog_rsa
IdentitiesOnly yes
Host github.com
User xxx@email.com
PreferredAuthentications publickey
IdentityFile /data/publish/blog_rsa
IdentitiesOnly yes

node服务代码很简单,判定一下coding的hook是不是push请求,友情提示,启动node服务的时候可以用forever模块启动forever start index.js
index.js

var http = require('http');
var exec = require('child_process').exec;
var config = require('./config.js');
var fs = require('fs');
var util = require('util');
var log4js = require('log4js');
var logger = log4js.getLogger("hook");
log4js.configure({
appenders: [{
type: 'console',
level: 'DEBUG'
}, {
type: 'file',
filename: 'logs/hook.log',
category: 'hook',
level: 'INFO',
maxLogSize: 20480,
backups: 10,
}]
});
var now = function() {
return new Date().toLocaleDateString() + " " + nowDate.toLocaleTimeString();
}
var main = function(req, res, data) {
if (data.commits) {
console.log(data.token , config.token);
if (data.token != config.token){
logger.error("error token!!!!!!!");
res.end();
return;
}
var project_name = data.repository.name,
commit_user = data.commits[0].committer.name,
commit_user_email = data.commits[0].committer.email,
commit_message = data.commits[0].short_message;
let pro_item = config.projects[project_name];
if (!pro_item) {
logger.warn("push project not in config");
res.end();
return;
}
logger.info(util.format("commit_message:%s, project_name:%s, commit_user:%s, commit_user_email:%s", commit_message, project_name, commit_user, commit_user_email));
exec(util.format("sh %s", config.projects.shell_name), {
maxBuffer: 400 * 1024,
cwd: pro_item.path
}, function(err, stdout, stderr) {
cmd_result = err ? stderr : stdout;
logger.info(cmd_result);
});
res.end();
logger.debug("push trigger end");
} else if (data.zen) {
//ping
res.end();
}else {
res.end();
}
};
var server = http.createServer(function(req, res) {
var POST = '';
req.on('data', function(chunk) {
POST += chunk;
});
req.on('end', function() {
try {
POST = JSON.parse(POST);
} catch (Error) {
POST = {};
}
main(req, res, POST);
});
});
server.listen(config.port);
logger.info("Server runing at port: " + config.port + ".");

config.js

#启动端口
var port = xxx;
#与coding的token配置一致
var token = 'xxx';
var projects = {
hexo: {
path: '/data/publish/hexo/',
url: 'git@git.coding.net:farwmarth/webhook.git'
},
//push请求要执行的脚本
shell_name: "deploy.sh"
};
var hook_log = 'hook.log';
module.exports = {
projects: projects,
port: port,
token: token,
hook_log: hook_log,
};

deploy.sh

git pull origin master
#为了拉主题
git submodule update --recursive --init
hexo clean
hexo generate
hexo deploy

部署好服务,配置好webhook后,可以愉快地写完文章直接commit,而不用再等待deploy了.

安全性总结

  • 用travis记得将私钥加密
  • 如果要用密钥最好用项目部署公钥,并控制好读写权限
  • webhook最好设置token