把博客发布交给 GitHub Actions
站内文章3年前,我将博客框架从 WordPress 切换到了 Hexo,并通过服务器进行托管,机制如下图:

期间,我有尝试过将目前的发布方案交给 GitHub Action 去处理。但是当时我的博客发布的工作流特别复杂,这检查那检查,这生成那生成…通过对博客 站内文章关系图谱 方案改造后,我的博客工作流就变得简洁了很多。所以今天我想尝试能不能抽出一点时间一劳永逸解决这个问题。(结果今天的单休就泡汤了)
本文是对服务器托管静态文件的情况下,对本地发布步骤的改造。当然,如果你的公共目录就放在 GitHub,也许会简单多。不过那是另外一套架构的做法,估计也大差不差,这里就不再讨论。
GitHub 上有博客项目和主题的私有仓库
在开始之前,我们确保我们的博客项目的源代码已交给 GitHub 托管,后续操作将基于这个仓库进行 GitHub Actions 的构建:当仓库 main 分支变动时,执行 GitHub Action,将 public 目录发送到服务器。如果我们的博客配置中包含有敏感密钥,或者我们不想将文章 Markdown 内容直接公开,那么建议仓库保持私有仓库,除非我们已经处理好这些问题。
我们的主题也应该是交给 GitHub 管理的(主题开发者的仓库,或自己的私仓)。主题和博客项目的关系应当形成一种 Git Submodule 的关系。在博客项目中,首先删掉 theme 文件夹下我们使用的主题(注意备份,确保已经保存到远仓)。然后通过以下命令重新导入到博客项目中:
1 | 把主题仓库作为子模块添加 |
根目录生成的 .gitmodules 记得妥善管理。子模块文件夹本身就是独立仓库,Git 会自动忽略。
SSH 的公钥和私钥
注意到,我们之前部署的主要流程是将本地电脑生成好的 public 文件夹发送的服务器中的仓库,中间是通过 SSH 链接的。现在我们想要 GitHub Actions 做这件事,那么我们必须生成另一对 SSH 公钥和私钥:
- 公钥放在服务器的
/home/git/.ssh/authorized_keys中; - 私钥放在 Actions secrets and variables 中。
1 | # 生成专用密钥(4096位高强度加密,-f指定固定文件名deploy_key) |

其他环境变量的配置
如果你在本地发布工作流中使用到了一些脚本,这些脚本依赖了一些本地计算机的环境变量,如 JavaScript 的类似这样的代码 process.env.TENCENT_API_SECRETID,那么也放进 Secrets 中。
GitHub 的 PAT 我也放进了 Secrets,这是为了保证子模块拉取的顺利进行。
另外,我们还需要补充 Git 的一些基础参数,这部分可以放在 Variables。Variables 和 Secrets 区别在于变量的值是否明文。

新增 Workflows
在博客根目录下新增相应文件夹和工作流文件:

内容参考如下:
1 | name: Hexo Deploy |
重新审视之前的工作流
基本步骤已经弄得差不多了,现在开始检查。检查 package.json 中,npm run publish、postpublish、prepublish 工作流是否都正确。检查无误后,我们就可以进行 push 测试了。
在 push 的过程中,观察 Actions 中的日志,逐步排查问题。不断试错,不断解决即可。
问题解决
采用 GitHub Actions 方法发布时文章的更新时间错误
如果使用 GitHub Actions 进行发布,所有文章的更新时间可能会全部设定为当前时间。为了解决这个问题,我们需要为我们的文章提供正确的更新时间。
直接想到的办法是为所有文章的 Front-Matter 增加 updated 字段,但是这么做未免太麻烦。我们可以使用插件/脚本解决。
首先,关掉 Hexo 默认的「更新时间」的逻辑。在 _config.yaml 中,修改以下设置:
1 | updated_option: "empty" |
然后,在项目根目录的 scripts 文件夹下新增以下脚本:
1 | const { exec } = require('child_process'); |
这个脚本借助 Hexo 过滤器,在文章渲染前自动处理时间:
- 如果文章已手动设置
updated时间,则直接跳过处理; - 通过 Git 命令获取文件最后一次提交的时间,自动赋值给文章
updated字段。
使用 Obsidian 管理的同学还可以使用 Linter,顺手管理 YAML 字段。





