用git来管理hexo博客部署

hexo是现在流行的静态博客程序,现在有大量的程序员和设计师使用hexo搭建自己的博客。由于hexo生成的博客全部都是静态文件,所以可以托管在任何支持静态文件的VPS或空间下面。甚至很多同学也直接托管在自己的github pages上面。

本篇文章我们不是讲解如何使用hexo,也不会讲解markdown,这个大家可以在hexo官网这个网站进行学习;我要分享的是在你把博客部署到阿里云或腾讯云的服务器上面时,你如何使用代码仓库来轻松管理自己的博客源码并快速发布自己的博客。

背景介绍

由于github pages服务器在国外,而国内的coding pages也只有银牌以上会员才能绑定域名(而且coding.me为了防止自己域名在微信平台被滥用分享,还在微信平台增加了中间页面跳转,无法直接打开你的博客),所以为了让我的博客访问速度更快, 我的博客是放置在了自己的阿里云服务器上,通过nginx进行托管。
nginx开启了https,配置大概如下:

1
2
3
4
5
6
7
8
9
10
server {
listen 443;
server_name blog.cuiyongjian.com;
ssl on;
ssl_certificate /etc/letsencrypt/live/blog.cuiyongjian.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/blog.cuiyongjian.com/privkey.pem;
ssl_dhparam /etc/ssl/certs/dhparams.pem;
ssl_protocols SSLv3 TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers HIGH:!aNULL:!MD5;
}

我之前的博客写作和发布的方式是这样的:
hexo博客源码放置在我本机的mac上,每次编写博客时我需要在mac上编写,然后执行 hexo g 生成静态文件到 public目录,然后执行 hexo deploy 通过sftp的方式部署到我的远程阿里云机器上。 但是这样工作有一个问题,那就是hexo通过sftp的方式上传到阿里云服务器总是由于网络环境原因可能经常同步失败,因为他每次总是同步全量文件,这在博文变多的时候就变得特别有问题了

所以我的目标是: 在任何地方,拿到一台电脑的情况下,就可以轻松check自己的博客源码跑起来,并且进行博客编写。而且编写完之后可以自动、轻松、快速地将博客发布到自己的阿里云。因此梳理之后,我们要达到以下2点要求:

  1. 有一个云端私密仓库可以存储我的博客源码及其配置 (博客源码里会包含draft草稿的内容,所以最好是放在私密的仓库)
  2. 写完博客能够快速部署到阿里云服务器(最好是增量的部署,避免耗时和卡顿)

思路

第一个问题其实比较好解决,国内的coding.net本身提供了免费的私有仓库。虽然不交钱的话有个数限制,但对于我来说也足够了。但是由于hexo编译后的结果也存放在博客源码目录下,所以你肯定不希望git同步时也把编译结果同步出去,所以我们应该至少在.gitingore文件中添加如下的忽略选项:

1
2
public/
.deploy*/

其中 .deploy*/ 特指要忽略掉hexo部署git时自动生成的 .deploy_git 目录。 另外,如果你的themes主题里面有通过git克隆来的主题,你最好也去里面把它对应的 .git目录删掉,从而移除它的git仓库控制。

另外一个问题就是如何部署到阿里云,如果是hexo部署到gitpages或者coding pages就自然可以直接使用hexo所支持的git部署方式,直接将博客部署到githubpage的仓库即可。 这时hexo同步页面也是基于git增量同步的(因为deploy是通过git push发布的), 但我这里并不希望部署到gitpages或者coding pages,我是要部署到自己的阿里ECS服务器上。

这里我使用了一个非常简单的办法,就是在同一个私有仓库上,我用blogrelease分支来存储hexo编译出来的静态博客文件,然后在我的阿里云服务器上再通过 git pull的方式把博客从git上增量拉取下来。

具体步骤如下:

  • 当我在本地把最新博客编译完成之后,就把hexo的public目录push到coding.net上的blogrelease分支。
  • 然后在阿里云服务器上使用 git pull origin blogrelease 把最新博客拉取下来。

由于git是增量pull和增量push,而且是压缩传输,所以其速度相对于sftp还是要快很多的。

那么 mac本地是如何把public编译结果同步到coding的blogrelease分支的呢?

push编译结果到远程

如果你是使用hexo官方的git部署方式,其实他是自动把public目录下的内容拷贝到隐藏的 ~/Code/blog/.deploy_git 目录,然后push到远程你指定的地址和分支。由于这种方法比较简单我就不赘述了。

下面讲一下我是如何手工把public目录push到远程的blogrelease分支的。这里有2种方法,一种是直接使用hexo官方的hexo-deploy-git插件,另一种方法是我手工将本地博客的public子目录单独掕出来作为一个git仓库映射到远程博客仓库的blogrelease分支上。

假设我的博客源码是在这里: ~/Code/blog , 它是映射到我远程私有仓库 https://git.coding.net/sheldoncui/blog.cuiyongjian.com.git 的master分支, 而我的博客编译结果会放置在~/Code/blog/public 目录下,我会将它映射到同一个远程仓库的 blogrelease 分支。

使用webhook实现自动部署博客

本地push一些文件倒不是什么难事,然而把 blogrelease push之后,我们需要手工登到阿里云进行pull操作还是不够简便,而通过webhook就可以简化为:

“一旦我本地将博客编译结果push到远程,则自动触发阿里云服务器去自动执行git pull操作”。

首先,我在阿里云服务器上创建了自己的webhook远程服务(基于express),对于我的博客,可以Post到 http://webhook.cuiyongjian.com/blog 进行调用

然后,到存放博客文件的coding.net平台上设置git仓库的webhook钩子,指向刚刚创建的webhook地址 webhook.cuiyongjian.com/blog

添加之后,尝试本地进行博客源码提交,即可看到我的阿里云 webhook 钩子已经收到post的消息, 通过日志可以看到其http头大致如下:

1
2
3
4
5
6
7
8
9
10
{ 'x-real-ip': '120.132.59.96',
'x-forwarded-for': '120.132.59.96',
host: 'webhook.cuiyongjian.com',
'x-nginx_proxy': 'true',
connection: 'close',
'content-length': '1087',
'user-agent': 'Coding.net Hook',
'x-coding-event': 'push',
'content-type': 'application/json; charset=UTF-8',
'accept-encoding': 'gzip,deflate' }

其中x-nginx_proxy是我自己在 nginx 层加的,而通过x-coding-event可以判断是否是 coding.net 服务发来的 git 事件类型。 也可以判断 ‘user-agent’ 是否等于 'Coding.net Hook'.

webhook 所收到的 http 请求的 body 体如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
{ ref: 'refs/heads/master',
before: 'ee417dd72ac59f129bc250bed7bb6276bea3ebdf',
commits:
[ { committer: [Object],
web_url: 'https://coding.net/u/sheldoncui/p/blog.cuiyongjian.com/git/commit/768f91d066cd70b066c27b034b1c14058c5d374c',
short_message: 'add blog\n',
sha: '768f91d066cd70b066c27b034b1c14058c5d374c' } ],
after: '768f91d066cd70b066c27b034b1c14058c5d374c',
event: 'push',
repository:
{ owner:
{ path: '/u/sheldoncui',
web_url: 'https://coding.net/u/sheldoncui',
global_key: 'sheldoncui',
name: 'sheldoncui',
avatar: '/static/fruit_avatar/Fruit-16.png' },
https_url: 'https://git.coding.net/sheldoncui/blog.cuiyongjian.com.git',
web_url: 'https://coding.net/u/sheldoncui/p/blog.cuiyongjian.com',
project_id: '1268146',
ssh_url: 'git@git.coding.net:sheldoncui/blog.cuiyongjian.com.git',
name: 'blog.cuiyongjian.com',
description: 'hexo博客静态代码' },
user:
{ path: '/u/sheldoncui',
web_url: 'https://coding.net/u/sheldoncui',
global_key: 'sheldoncui',
name: 'sheldoncui',
avatar: '/static/fruit_avatar/Fruit-16.png' },
token: '****' }

coding平台推送来的body就是一个JSON串,通过其 token 字段可以进行鉴权。通过event, ref 可以判断事件类型和分支类型等,这样我们就可以在阿里云服务端进行自动拉取博客的最新静态资源了。 如果是编译后的博客资源,由于我放在了 blogrelease 分支,那么服务器收到的 webhook 请求的 ref 就是 refs/heads/blogrelease.

然后,由于我们要使用webhook自动拉取git仓库的代码,所以我需要将阿里云上的公钥放置在coding的git仓库中,以实现免密克隆;如果不进行公钥的配置,即使你使用ssh的方式拉取代码也会爆出没有权限的错误。 公钥配置的具体步骤可参照 官方指引

如果你的仓库之前是使用 https 方式拉取的,那么可以通过如下方式修改为ssh的方式:

1
git remote set-url origin git@git.coding.net:sheldoncui/blog.cuiyongjian.com.git

想法

在写作本文的过程中,我忽然想到,其实我们可以更进一步:

  1. 基于git我们是可以在阿里云服务器上创建一个自己的私有仓库的,这样就没必要使用coding平台了。
  2. 另一方面,其实我们使用了webhook之后可以更进一步,直接让阿里云服务器自动拉取我们的博客源码,自动执行 hexo g,免去我们需要自己主动在本地编译的烦恼,这就有点 CI 持续集成的意思了。(其实开源社区的 travis-ci 等工具应该可以做到这一点,比如 Coding.net 的 webhook 我们设置为某个 CI 平台,把编译工作交给 CI 平台,部署时让 CI 通过 rsync 等方式同步到我的阿里云。)
  3. 服务器上实现自动拉取和编译之后,我们的nginx可以直接指向服务器上的hexo->public目录,每次本地提交博客源码,自然就能访问到最新的博客了
  4. 能否直接让编辑器vscode保存后自动push到远程仓库呢? 貌似并没有这个必要,毕竟写博客也没有立即发布的需求,而且vscode本身管理git仓库就是要有严格的操作路径的,否则还要git的那些stage、commit、push作甚。