基于Github Actions实现hexo自动化部署
这里主要介绍了博客建站的过程。
最初的想法
最初建立博客的时候也是基于Hexo发布,网上关于Hexo的教程大多数都是基于github pages,也有一些是部署到个人VPS上。
对于个人而言github pages无法使用自己的域名和服务器实在浪费,同时由于本日常人使用多台电脑,如果换台机器写博客则需要重新搭建hexo环境并从部署hexo的vps拉取代码,每次都需要花很多时间在网络上,也是得不偿失。因此想到通过github存储博客代码,同时每次push的时候代码可以自动化部署到服务器上面。
方案1:WebHook
第一种解决方案是基于WebHook。所谓WebHook,就概念而言可以延伸出很多东西,这里就具体例子而言。如果传统方法搭建网站,那么博客会在每天定时监测github代码是否已经更新,然后定期拉取github的代码;如果采用web hook的方法,当更新博客push到github的时候,github会通知VPS,VPS收到通知后才会去拉取代码。
Github实际上已经实现了WebHook的功能,比较麻烦的是你需要自己在VPS上写一个接口处理Github的通知,这种方法网上教程比较少但也不是没有,有些拿Python写一个单独的后端API接口,有些则由nginx监听推送,然后执行一个自己写的自动化脚本。
方案2: GitHub Actions
GitHub Actions是Github在2018年10月推出的持续集成服务,这里不需要深究持续集成的概念,只需要知道Github Actions运行在Github服务器的虚拟机上,可以通过运行脚本实现登录VPS、执行脚本、自动化部署等操作,而Github中已经有许多前人开发的脚本。
相较之WebHook,Github和VPS不再需要搭建Web服务以及进行各种网络调试,显然更加省心。
具体实施
Hexo的本地建立
建立hexo本地服务器需要node.js和git,自行参考安装教程解决,以下为安装hexo博客的脚本。
1 | # copy from hexo.io |
hexo服务器运行正常之后,去 http://localhost:4000 测试是否可以正常访问。
Hexo部署到Github
这一部分可以参考大多数Hexo建立Github Pages的教程,需要注意Github Pages目前只支持公有仓库,私有仓库要加钱。
第一步,建立Github仓库,如果需要建立GitHub Pages,仓库名必须为 <用户名>.github.io 的固定格式,同时建议设置公有仓库。
第二步,设置Git的账号密码。由于Github在去年ban了账号密码登录,传统的 git config user.name xxx && git config user.email xxx && git config.user.password xxx 已经狗带了,因此这里比较推荐ssh免密登录。
1 | git config --global user.name "xxx" |
然后根据官方指南 Adding a new SSH key to your GitHub account 将你的ssh公钥(后缀为.pub)加入到github的ssh keys中。
此外还有一种方法是通过Github Token登录,git config --global user.password "你的密码" 更改为 git config --global user.password "你申请到的token",申请Token可以参照官方教程 Creating a personal access token。注意Token是有时效和访问限制,如果设置错误或者时效过期只能重新申请。
Hexo设置Git部署
在hexo根目录_config.yml可以找到
1 | deploy: |
注意现在由于黑命贵的影响,现在创建的Github仓库默认改成了main而不是master,很多旧的教程还在用master,以及我的SourceTree也是默认推送到master分支,目前hexo生成的_config.yml默认为main不需要更改,所以使用 hexo deploy 则不需要修改分支。
设置完成之后可以 hexo deploy 测试一下是否成功。
设置GitHub Actions
1 | # main.yml |
这一部分脚本是根据 Zellwk的代码 修改的,很大程度上参考了 他的博客。secrets则是在 仓库>>Setting>>Secrets>>Actions 中建立,本质就是字符串代换,建立的secrets只能替代或删除,但无法查看。由于我之前建立的Github仓库是公有的,因此Github Actions的 main.yml 文件全部都是可见的,因此必须通过secrets隐藏ssh密钥和IP、端口、文件目录等敏感信息,保障了命令的安全性。
GitHub Actions都是运行在GitHub Actions Runner,Runner 是一个开源的虚拟机环境,因此cmd和其它虚拟机没有什么区别,惟一的好处是运行在Github自己的服务器上,有一系列大佬写好的一键部署脚本可供使用。on之后是触发的条件,这里的触发条件是有push到main分支。Jobs则是所需要完成的任务,Jobs之间可以用needs指定前要条件,而单一Jobs则由Steps组成,Steps的先后次序则是固定的。
第一步,uses: actions/checkout@v2,则是执行了git checkout的自动化脚本,将我们博客的代码拉到工作区,这些代码后面都要发送到VPS。
第二步,模拟本地免密登录VPS。回想一下我们本地免密登录Github都是将公钥分发给VPS,实际上每次登录都是本地使用私钥和VPS持有的公钥配对,在这个过程中只有公钥需要分发,私钥一般都是不会暴露在网络上,只能够用私钥打开公钥而不能反着来,因此这种加密被称作非对称加密。设想如果其它主机通过某种途径获取到这个私钥,它同样也可以通过私钥进行登录。第二步实际上也是在做这样的事,由于Runner是Github自动分配的,所以我们每次登录的IP地址都是不同的,所以我们通过Github的Secrets分发私钥。
如果我们是在本机第一次ssh免密登录,则会出现 The authenticity of host 'xxx' can't be established. 这样的警告,然后抛出 Are you sure you want to continue connecting (yes/no/[fingerprint])? Warning: Permanently added 'xxx' to the list of known hosts,脚本一般不知道怎么处理这件事,所以最后的任务就是将xxx添加到list of known hosts中。查看本机(MBP16)的 ~/.ssh/known_hosts 我们可以发现这些该文件格式为 [IP地址:端口号 SSH-2.0-OpenSSH_8.0 一大坨hash值],利用 ssh-keyscan 生成相应格式代码并添加,即可完成该任务。
第三步,则是通过rsync同步文件。rsync 是Linux内置的文件同步工具,主要用法为 rsync local-file user@remote-host:remote-file,作用是同步两端相似的目录结构和内容,相比起其他复制方法,rsync不会复制已有的内容,通过差分对比发现远端缺失的内容,将源服务器内容复制到目标主机中。rsync可以实现代码的增量备份,将github中增加的代码拷贝到VPS上,而诸如评论等VPS上自己生成的内容不受影响。
Zellwk的代码为 rsync -avz -e "ssh -p ${{ secrets.SSH_PORT }}" ./dist/ ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }}:/path/to/your/directory/,在这里主要遇到了两个错误,一个是 failed to set permissions,另外一个是 failed to set times on "/foo/bar": 前者是由于我禁止了root用户登录,所以登录的用户没有权限操作,所以登录用户没有执行足够权限执行 -avz,所以需要改为 -rltvz;后者可能同样也是由于账号引起的问题,我的文件挂载在Linux的User Na mespace中,没有内核级别的时间设定权限,因此需要添加 --omit-dir-times 规避这一问题。