GitHub Actions 自动部署 Hexo 博客到 cPanel 虚拟主机

本文最后更新于:2022年4月8日 凌晨

1 引言

我在网上看到很多用 GitHub Actions 自动部署 Hexo 博客的文章。

因为我有多台设备,不想重复在每一台设备上配置 Hexo+Git 的环境,而是想直接把 MarkDown 文件推送到 GitHub,所以存在自动部署的需求,于是尝试着自动部署自己的博客。

但是因为我是新手上路,而且我的博客主力镜像是在虚拟主机上,所以掉了不少坑。

下文将过程和坑简单叙述一下。

2 操作步骤

操作步骤与这两篇文章[1][2]一致。

2.1 博客结构与虚拟主机

我有两个 GitHub 仓库:

  • kukmoon_blog :私密仓库,存放博客源码;
  • kukmoon.github.io : 公开仓库,存放静态博客页面。

此外,我把博客主力镜像放在虚拟主机上,它使用 cPanel 管理面板,支持 Git。

也就是说,我要实现的自动部署是,一次推送,同时部署到 kukmoon.github.io 仓库和我的虚拟主机。

kukmoon_blog 仓库已经与本地的 C:\Users\Kukmoon\kukmoon_blog 仓库建立了关联。

注意:单仓库双分支的情况,不适用于本文,可参考这篇文章[3]

2.2 密钥配置

2.2.1 生成部署密钥

执行下列命令,在本地生成一对新的公钥和私钥,专门用于 GitHub Actions 自动部署。不使用本机已有的公钥和私钥,是为了避免本机私钥泄漏。

ssh-keygen -f github-deploy-key -C "kukmoon.github.io"

2.2.2 为 GitHub 配置部署密钥

按照参考文献[1]的介绍,将私钥 github-deploy-key 的文件内容添加到 kukmoon_blog 仓库的 Settings → Secrets → Add a new secret 页面上,NameHEXO_DEPLOY_PRI后面的代码要用到这个名称);

再将公钥 github-deploy-key.pub 的文件内容添加到 kukmoon.github.io 仓库的 Settings → Deploy keys → Add deploy key 页面上,TitleHEXO_DEPLOY_PUB,勾选 Allow write access 选项。

2.2.3 为虚拟主机配置部署密钥

按照参考文献[4]的介绍,通过 cPanel 面板,将公钥 github-deploy-key.pub 的文件内容导入,并且授权(Authorize)即可。注意:只导入公钥,千万不要导入私钥!

类似地,如果要通过 GitHub Actions 把 Hexo 博客自动部署到 VPS,也要将公钥 github-deploy-key.pub 的文件内容导入到 VPS 中。

2.3 编写 Workflow

最好是在本地编写 Workflow,然后推送到 GitHub 的 kukmoon_blog 仓库。在本地编写比较方便,而且首次推送时就会执行这个 Workflow。

按照参考文献[1]的介绍,在 C:\Users\Kukmoon\kukmoon_blog 下创建 .github/workflows/deploy.yml 文件。

在这个文件中粘贴以下内容,并保存。

name: CI

# 只监听 master 分支的改动
on:
  push:
    branches:
      - master

# 自定义环境变量
env:
  GIT_USER: Kukmoon  # 改成你自己的 GitHub 用户名
  GIT_EMAIL: kukmoon97@gmail.com  # 改成你自己的 GitHub 注册邮箱

jobs:
  build:
    name: Build on node ${{ matrix.node_version }} and ${{ matrix.os }}
    runs-on: ubuntu-latest
    strategy:
      matrix:
        os: [ubuntu-latest]
        node_version: [12.x]  # 改成你本地的 Node.js 版本,可以用 `node --version` 命令查询

    steps:
      # 获取博客源码和主题,我的主题放在 `theme` 文件夹中,可随博客源码一起获取
      - name: Checkout
        uses: actions/checkout@v2
      
      # 用 Node.js 渲染
      - name: Use Node.js ${{ matrix.node_version }}
        uses: actions/setup-node@v1
        with:
          node-version: ${{ matrix.node_version }}

      # 安装 Hexo-cli    
      - name: Install hexo with dependencies      
        run: |
          npm install -g hexo-cli

      # 安装依赖
      - name: Install hexo with dependencies      
        run: |
          npm install

      # 配置环境
      # ssh-kenscan github.com >> ~/.ssh/known_hosts   # 从 GitHub 获取公钥并保存到 known_hosts 文件
      # ssh-keyscan 999.999.999.999 >> ~/.ssh/known_hosts   # 从我的虚拟主机(假设IP为999.999.999.999)获取公钥保存到 known_hosts 文件
      - name: Configuration environment
        env:
          HEXO_DEPLOY_PRI: ${{secrets.HEXO_DEPLOY_PRI}}
        run: |
          sudo timedatectl set-timezone "Asia/Shanghai"
          mkdir -p ~/.ssh/
          echo "$HEXO_DEPLOY_PRI" > ~/.ssh/id_rsa
          chmod 600 ~/.ssh/id_rsa
          ssh-keyscan github.com >> ~/.ssh/known_hosts          
          ssh-keyscan 999.999.999.999 >> ~/.ssh/known_hosts
          git config --global user.name $GIT_USER
          git config --global user.email $GIT_EMAIL

      # 生成并部署
      - name: Deploy hexo
        run: |
          hexo clean
          hexo g -d

      # 部署后更新博客源码,用于添加 abbrlink,如果不用 abbrlink,需要删除
      - name: Update Blog
        run: |
          sh "${GITHUB_WORKSPACE}/.github/script/blog-update.sh"

2.4 abbrlink(短链接)

我用了 Hexo 插件 hexo-abbrlink,来为博文生成短链接,为了将短链接写入 MarkDown 文章的 Front-matter 区域的 abbrlink 字段,需要在 hexo g 之后运行一个脚本 blog-update.sh,将 Hexo 对 MarkDown 文章的修改推送回 kukmoon_blog 仓库[2][5]

按照参考文献[2]的介绍,在 C:\Users\Kukmoon\kukmoon_blog 下创建 .github/script/blog-update.sh 文件,复制粘贴以下内容,保存。

#!/bin/sh

if [ -z "$(git status --porcelain)" ]; then
    echo "nothing to update."
else
    git add .
    git commit -m "triggle by commit ${GITHUB_SHA}" -a
    git push origin master
fi

2.5 推送

hexo new 新建一篇博客文章,保存。

接下来,添加、提交、推送。

git add .
git commit -m "测试自动部署-1"
git push

GitHub 的 kukmoon_blog 仓库就开始执行 Workflow 文件中的指令,进行自动部署。如果成功,就会出现绿色对勾。
GitHub Actions 执行结果页面

2.6 拉取

等博客的静态页面更新后,最好在博客源码所在的本地仓库( C:\Users\Kukmoon\kukmoon_blog 文件夹) 执行一次 git pull 命令,把 Hexo 的 abbrlink 插件对最新的 MarkDown 文章的修改拉取到本地。

当然,等下次推送时再拉取也可以,但是,如果在拉取之前对最新的 MarkDown 文章进行了修改,就会与远程的同名文件产生冲突,导致拉取失败。所以,还是及时拉取比较好。

3 错误分析(我掉过的坑)

参考文献[2]列出了作者遇到的四个错误。下文列出我遇到的错误(我掉过的坑)。

3.1 提示缺少 db.json 文件

远程渲染时,提示缺少 node_modules/mine_db/db.json 文件。

打开 C:\Users\Kukmoon\kukmoon_blog\.gitignore 文件,看看 db.json 是不是在里面,把它改成 /db.json

文件 .gitignore 是排除列表,不会被 Git 提交到分支,也不会上传到 GitHub 的远程仓库。/db.json 表示只排除本地仓库根目录下的 db.json 文件,不会排除子目录中的同名文件[8]

3.2 Host key verification failed

把静态页面部署到虚拟主机时,提示 Host key verification failed

这个坑让我花了很多时间,谁让我是前端小白呢?

SSH 远程登录需要验证密钥。如果让 A 机通过 SSH 连接 B 机,A 机事先没有在 ~/.ssh/known_hosts 文件中保存 B 机发来的公钥(或者公钥不匹配),就会出现此错误[6]。我只需要把虚拟主机的 IP 地址和公钥添加到这个文件中就可以了。上述 Workflow 中的 ssh-keyscan 999.999.999.999 >> ~/.ssh/known_hosts 一行就是起到这个作用(请把此处的 999.999.999.999 改成你的虚拟主机或 VPS 的 IP 地址)。

4 讨论

4.1 GitHub Actions 的运行原理

GitHub Actions 是在虚拟主机中运行的。GitHub 按照 on 节中的要求监听事件的发生,一旦事件发生,就按照 job 中的要求指定虚拟主机的操作系统,在虚拟主机中设定 env 节中的环境变量,之后执行 steps 节中的命令。它可以在虚拟主机中运行 Linux 命令、安装应用程序、运行应用程序、运行 GitHub Actions 脚本、运行某个仓库中的代码,等等。例如,checkout 表示将某个仓库中的内容读取到虚拟主机。全部命令运行结束后,清空虚拟主机,退出。

这就是需要选择运行环境为 Ubuntu、安装 Node.js 与 Hexo、设置环境变量、运行 name: Configuration environment 节中的一连串指令的原因。

这也是需要把 Hexo 对博客源码的修改用 git push 推送回去的原因。

4.2 检测依赖是否完整

Hexo 博客所需要的绝大多数依赖都放在 C:\Users\Kukmoon\kukmoon_blog\node_modules 中。如果有些依赖没有安装在这里,但是又是静态 HTML 文件渲染所必需的,就需要修改 Workflows 文件,在 name: Install hexo with dependencies 一节中单独安装它。

参考文献[2]提到使用 MathJax 时需要单独安装 pandoc,还好我用 KaTeX 渲染数学公式,远离 MathJax 保平安!

4.3 用 git push 代替 Hexo 部署

Hexo 把静态页面部署到 GitHub Pages,本质上是调用 git push 命令,因此,我们可以在 Workflow 中,只执行 hexo g 命令,生成静态页面,然后用 git push 命令推送到 GitHub、Coding、Gitee,或者支持 Git 的虚拟主机,详见这篇文章[7]

5 结论

用 GitHub Actions 完全可以实现自动部署 Hexo 博客到 GitHub 和 虚拟主机。在 GitHub 上建立两个仓库,一个存放博客源码,一个存放生成的静态页面(GitHub Pages)。为博客源码仓库编写 Workflow,可以实现每当把修改推送到博客源码仓库时,就可以自动渲染生成静态页面,并部署到 GitHub Pages 仓库,也可以部署到虚拟主机,不需要本地具备 Node.js+Hexo 的环境,只需要有 Git 即可。

6 参考文献