Hexo 的多主题自动化部署

“对项目进行配置化改造可以提高开发效率,让开发者将精力集中在业务开发中,减少琐碎重复工作对心情的影响。更重要的是,配置化可以减少因人工操作带来的失误,并且利于排查问题。”

上面正是我在对自己的项目进行改造后的感受。我维护了两个 Hexo 主题项目, hexo-theme-tranquilityhexo-theme-academic(未正式发布)。由于两个主题的测试用例重合度非常高,每次新增测试用例都得手动在两个主题上测试,很不方便。并且为了方便用户使用和查看示例,我想构建一个专门的演示网站对两个主题进行展示,并且希望每当我更新测试用的项目时,演示网站的两套主题都会自动更新部署。

于是考虑通过配置化利用一个博客资源同时部署若干主题,并通过 Github Actions 让两个主题更新后都自动部署,方便用户查看更新内容和测试示例。

最终实现的效果是这样的,建立测试项目 hexo-theme-test。每当该项目进行推送时,自动在网址 theme.hozen.site/tranquilitytheme.hozen.site/academic 部署使用了主题 hexo-theme-tranquilityhexo-theme-academic 的网站。

简言之,需要解决两个问题,一个就是同一个博客资源部署多个主题,另一个是 Github Actions 的自动部署。下面分别介绍。

Hexo 多主题部署

Hexo 在 themes/ 目录下可以存放多个主题文件,但只能通过 _config.yml 指定一个主题进行部署,并且部署到网站根目录。当修改主题后,之前生成的内容会被覆盖。我希望 themes/ 目录下的主题都能部署,并且互不影响。

部署多个主题的解决方法很简单,只需要通过更改 _config.yml 的配置,修改主题的输出目录,使不同的主题使用不同的子目录即可。例如我要部署【致远】和【academic】两个主题时分别修改 _config.yml 的以下部分:

致远主题
1
2
3
url: http://theme.hozen.site/tranquility
public_dir: public/tranquility
theme: tranquility
academic 主题
1
2
3
url: http://theme.hozen.site/academic
public_dir: public/academic
theme: academic

这样在两个 URL 下就输出了使用不同主题渲染的网站了。

配置分离

通过以上方法虽然解决了业务需求,但是由于每次部署主题都得去修改 _config.yml 文件。并且万一忘记修改,那么另一个主题网站就会被当前的主题所覆盖,很不方便。因此这里利用 Hexo 的配置融合机制。将两个主题不同的配置项分别放入配置文件: _build.tranquility.yml_build.academic.yml 中。通用的配置仍然放在 _config.yml

最后编写两个 scripts 命令来加载不同的配置文件:

package.json
1
2
3
4
5
6
7
8
9
10
{
"scritps": {
...
"build-tranquility": "hexo generate --config _config.yml,_build.tranquility.yml",
"build-academic": "hexo generate --config _config.yml,_build.academic.yml",
"trqlt": "hexo server --config _config.yml,_build.tranquility.yml",
"acdmc": "hexo server --config _config.yml,_build.academic.yml"
}
...
}

这样我们就可以通过执行 npm run trqltnpm run acdmc 来在本地运行不同的主题,并通过执行 build-tranquilitybuild-academic 来构建不同的主题了。并且两个主题互不影响,构建后的文件分别放置在 public/tranquilitypublic/academic 下。

使用子模块管理两个主题

需要注意,由于这个博客网站只是为了测试和演示,其使用到的主题都是单独的项目,并且以后可能加入其他主题。所以需要使用 Git 的子模块 submodule 功能来管理模块。具体来说就是在该仓库下将 hooozen/hexo-theme-tranquilityhooozen/hexo-theme-academic 两个项目作为子模块分别导入到 themes/tranquilitythemes/academic 目录下。这样做的好处是,可以在该仓库下直接对两个项目进行更新和维护,同时又保证了这两个项目的独立性。

有关 Git 子模块的使用方法,可以参考 Git 子模块 submodule 的使用

自动部署网站

Hexo 官网给出了利用 Github Actions 自动部署网站的示例,但并不能完全满足我的需求,主要有以下几点需要改进:

  1. 网页的生成依赖到了第三方软件 pandoc,Github Actions 的默认环境中没有该软件

  2. 需要部署两个主题网站到子目录下

  3. 需要移动 CNAME 文件到根目录

对于第 1 个问题,需要利用 Docker 来解决。第 2 和第 3 个问题增加相应的指令即可。

创建 Docker 镜像

创建具备 Node 和 Pandoc 环境的镜像,可以 Node 镜像为基础在其中安装 Pandoc 软件包。由于把项目生成和部署的操作在 Github Actions 中执行,因此 Docker 镜像中并不放入项目代码也不执行任何项目命令,只是创建运行环境。所以 Dockerfile 非常简单,如下:

1
2
3
4
5
6
7
8
# 使用一个包含 Node.js 的基础镜像
FROM node:18

# 设置工作目录
WORKDIR /app

# 安装 Pandoc
RUN apt-get update && apt-get install -y pandoc

使用以上 Dockerfile 创建镜像后,将该镜像上传到 Dockerhub 就能在 Github Actions 中拉取镜像并创建容器了。

编写 workflows 配置

准备好镜像后,只需要子啊 Github Actions 中使用该镜像创建容器,并执行相应的项目命令就能构建项目代码了,并使用相关的插件实现网站的自动部署。

首先在项目根目录下创建 .github/workflows/pages.yml 文件,然后编写以下内容:

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
30
31
32
33
34
35
36
37
38
39
40
41
name: Pages

on:
push:
branches:
- main # default branch

jobs:
pages:
runs-on: ubuntu-22.04
permissions:
contents: write
container:
image: docker://hooozen/node-pandoc:latest
steps:
- name: Checkout
uses: actions/checkout@v3
with:
submodules: recursive
- name: Cache NPM dependencies
uses: actions/cache@v3
with:
path: node_modules
key: ${{ runner.OS }}-npm-cache
restore-keys: |
${{ runner.OS }}-npm-cache
- name: Install Dependencies
run: npm install
- name: Build tranquility
run: npm run build-tranquility
- name: Clean config
run: rm -f db.json
- name: Build academic
run: npm run build-academic
- name: Set CNAME
run: cp source/CNAME public/CNAME
- name: Deploy
uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./public

在该配置文件中:

  • 使用 container 项指定了上文中创建并上传的具备 Node 和 Pandoc 的 Docker 镜像。Github actions 在执行时会使用该镜像创建 Docker 容器,接下的操作都会在该容器中进行。

  • 使用 actions/cache@v3 检出项目主分支,然后使用 actions/cache@v3 缓存 node_modules 目录以提高构建速度。

  • 接下来执行 npm install 安装依赖,然后依次运行 npm run build-tranduilitynpm run build-academic 来使用两个不同的主题构建页面。注意两个构建命令之间执行 rm -f db.json 来清除 Hexo 的缓存文件。

  • 构建完成后,在 public 文件夹下会生成两个子目录 tranquilityacademic 分别存放了两个主题的页面。

  • 由于我使用了自定义域名,因此使用 mv 命令将 CNAME 文件移动到 public 目录下。

  • 最后使用 peaceiris/actions-gh-pages@v3 来将 public 下所有的文件部署到 gh-pages 分支。

经过以上操作,每当我在 hexo-theme-test 项目中推送代码时,Github Actions 都会自动将更新内容使用两个主题分别部署在 theme.hozen.site/tranquilitytheme.hozen.site/academic 上。

总结

  • 对于 Node 项目不同环境和子项目的配置化,可以通过编写 scripts 命令和编写 Node 或者 Bash 脚本来读取配置文件实现。
  • Github Actions 是对 Github 仓库进行自动化配置的好工具,有非常强大的功能。
  • Docker 的应用对于跨平台的项目运行和部署非常方便。