GitHub+Hexo(8)-AI时代的Hexo博客新路径

以前我经常想一个问题,能不能直接在手机上写博客,然后发布到自己的 github.io。这样的功能在一些成熟的博客平台上并不稀奇,微信公众号、知乎之类的平台,本身就把编辑、存储和发布全部做好了,用户写完以后点一下发布,剩下的事情不用管。

但是 GitHub 不一样。GitHub 首先是一个代码托管平台,它可以保存 Markdown,也可以通过 GitHub Pages 展示静态网页,但是中间还有一步它是不管的——怎么把 Markdown 变成网页。我这个博客一直用 Hexo,所以每次发博客,都得先回到自己的电脑上执行 hexo g、hexo d,生成网页以后再上传到 GitHub。

手机当然也能写 Markdown,但是写完以后怎么办?手机上没有一个很方便的环境让我去安装 Node.js、Hexo、主题和各种插件,更不用说再执行这些构建命令。因此这个想法以前虽然有过很多次,但是一直没有认真折腾。

这两年情况有了一些变化。GitHub Actions 已经可以在云端自动运行程序,ChatGPT 又开始可以连接 GitHub 等各种云服务。这样一来,原来必须放在自己电脑上完成的 Hexo 构建,好像也没有必要非得留在电脑上了。于是我又重新研究了一遍这个问题。

两个GitHub仓库

原来的博客里,最重要的是 guohuahu.github.io 这个仓库,里面放的是 Hexo 生成以后的网站文件,比如 index.html、CSS、JS、图片以及按年份生成的文章页面。GitHub Pages 读取这些文件,然后把它们作为网站展示出来。需要注意的是,这个仓库里并没有真正的博客源码,Markdown、Hexo 配置和主题以前都在我的电脑上。

因此这次我又建了一个私有仓库 blog-source,把完整的 Hexo 项目放进去,包括:

1
2
3
4
5
6
source/
themes/
scaffolds/
_config.yml
package.json
...

这样两个仓库的作用就分开了。blog-source 保存源码,guohuahu.github.io 保存生成结果。整个过程可以简化成:

1
2
3
4
5
6
7
8
9
10
11
blog-source
↓
GitHub Actions
↓
Hexo
↓
生成 public
↓
guohuahu.github.io
↓
GitHub Pages

为什么这样可以?因为 Hexo 和 GitHub Pages 本来就是两个独立的环节。Hexo 负责把 Markdown、主题和配置生成 HTML,而 GitHub Pages 只负责把这些 HTML 作为网页展示出来。以前中间执行 Hexo 的地方是自己的电脑,现在只是换成了 GitHub Actions。

这样一来,guohuahu.github.io 这个仓库平时就基本不用手工去改。真正需要维护的是 blog-source。文章、主题、配置和依赖都在这里,网站仓库则更像是生成后的结果。

GitHub Actions做了什么

每当 blog-source 有新的提交,GitHub Actions 就会启动一个临时的运行环境,然后按照预先写好的流程完成构建。大概就是:

1
2
3
4
5
6
7
8
9
10
11
下载 blog-source
↓
安装 Node.js
↓
npm install
↓
hexo generate
↓
检查 public
↓
更新 guohuahu.github.io

做完以后,这个临时环境就可以结束,下次有新的提交再重新运行。因此本地电脑不需要一直开着,也不需要每次都手工去执行 Hexo 命令。

如果在电脑上写博客,就正常修改 source/_posts/ 下面的 Markdown,然后提交到 blog-source。如果以后在手机上有合适的 GitHub 编辑工具,同样可以直接改这个仓库。如果通过 ChatGPT 修改,原理也没有区别,最后还是修改同一个源码仓库。

整个流程也就变成:

1
2
3
4
5
6
7
8
9
10
电脑 ─────┐
手机 ─────┼──> blog-source
ChatGPT ──┘ ↓
GitHub Actions
↓
Hexo
↓
guohuahu.github.io
↓
网站

对用户来说,需要操心的仍然是文章内容、主题、配置和依赖版本。至于什么时候执行 hexo g、生成了多少个 HTML、这些文件怎么提交到网站仓库,都可以交给 Actions。

为什么源码仓库最好是私有的

这里还有一个问题,为什么不直接把所有东西都放到 guohuahu.github.io 里面?当然也可以,但 Hexo 的源码和最终的网站文件用途并不一样。

网站仓库本来就是公开的,因为 HTML、CSS、JS 和图片本身就是给别人访问的。源码仓库除了 Markdown,还包括主题配置、部署配置、插件版本以及一些历史文件。这些内容不一定需要公开,尤其是一个用了很多年的老项目,里面很容易留下以前的脚本、备份甚至密钥。

我这个博客在迁移的时候就发现过一些历史遗留文件,所以顺便做了一次整理。最后保留:

1
2
blog-source            私有
guohuahu.github.io 公开

公开仓库只负责展示,私有仓库负责维护,这样也比较清楚。

为什么Actions显示成功,网站却是空的?

这次迁移过程中最麻烦的问题倒不是 GitHub,而是 Node.js。第一次在 GitHub Actions 上运行 Hexo 的时候,从日志上看完全正常,index.html、about/index.html 等文件都显示已经生成,最后 Actions 也是绿色的 success。按道理讲,到这里网站应该已经生成成功了。

但是文件推送到 guohuahu.github.io 以后,网站反而打不开。后来检查才发现,index.html、CNAME、search.xml 等文件虽然都存在,但是大小全部是 0 字节。也就是说,Hexo 告诉你“生成了”,但实际上只创建了文件名,内容并没有写进去。

最后发现问题出在版本兼容。我的博客还在使用 Hexo 3.9.0,而一开始 GitHub Actions 配的是 Node.js 14。Hexo 社区以前就有人报告过 Node.js 14 下生成空 HTML 的问题(hexojs/hexo#4257)。这次本地进一步检查以后,问题也落在旧版 Hexo 的生成缓存逻辑上,所以会出现日志正常、文件也有,但是内容为空的情况。

把环境换回 Node.js 12 以后,生成恢复正常:

1
2
3
158 files generated
69 个非空 HTML
public 大约 9.6 MB

这也说明一个比较容易犯的错误:Actions 运行成功,不代表网站一定生成正确。自动部署最好不要只看命令有没有报错,还要检查结果本身。

因此现在 Actions 里面又增加了几项检查,比如:

1
2
3
4
test -s public/index.html
test -s public/about/index.html
test -s public/css/main.css
test -s public/CNAME

同时还会检查生成的 HTML 数量。只有这些都正常,才允许继续发布。

老博客要不要顺便升级?

发现 Node.js 版本有问题以后,很容易想到,既然 Hexo 3.9.0 已经这么老了,是不是干脆把 Hexo、Node.js、主题和插件一起升级算了。

后来没有这样做。原因也很简单,这个博客已经运行很多年,Hexo、NexT 主题、renderer 和各种插件之间已经形成了一套可以工作的组合。如果在迁移发布方式的同时,又把这些东西一起升级,一旦网站出现问题,很难判断到底是哪一层导致的。

是 Actions 配错了?还是 Node.js 不兼容?还是 Hexo 本身改了?还是主题或者插件出了问题?这些问题混在一起以后,排查会很麻烦。

所以对于这种已经稳定运行很多年的老项目,我觉得比较稳妥的办法是,先把原来的兼容环境搬过去。比如我这个项目,先固定在 Node.js 12,让 Hexo 3.9.0 正常运行。等新的发布流程稳定以后,如果还想升级,再单独在本地尝试。

例如可以先在本地升级 Node.js、Hexo 和插件,然后执行:

1
2
3
hexo clean
hexo generate
hexo server

把文章、图片、分类、搜索和主题都检查一遍。确认没有问题,再把 GitHub Actions 里的版本一起改掉。这样迁移和升级就是两件事,出了问题也容易判断原因。

自动发布不要每次重新建仓库

这次还犯过另外一个错误。最早的自动发布方式比较直接,大概是:

1
2
3
4
5
cd public
git init
git add .
git commit
git push --force

看起来好像没问题,反正 public 就是要发布的网站,重新建一个 Git 仓库然后覆盖远程就行了。

但是这样做会把 guohuahu.github.io 原来的 Git 历史直接切断。每次发布网站,不应该相当于重新创建一个仓库,否则以前的提交关系都没有了,以后想回退也比较麻烦。

后来改成先 clone 现有的网站仓库,再把新的 public 同步进去,然后正常 commit 和 push:

1
2
3
4
5
6
7
clone guohuahu.github.io
↓
用新的 public 更新里面的文件
↓
git commit
↓
git push

这样每次网站更新都有连续的提交历史,万一哪一天新的版本有问题,也能知道上一个正常版本是什么。

CNAME也有一个小问题

这个博客以前除了 huguohua.cn,还用过 Coding 的 Pages,所以历史的 CNAME 文件里面留下了两个地址:

1
2
huguohua.cn
huguohua.coding.me

平时没有动它的时候也没觉得有什么问题,重新部署以后 GitHub Pages 的自定义域名却出现了异常。最后把已经不用的旧地址删掉,只留下:

1
huguohua.cn

网站才重新恢复。

这种问题单独拿出来看都很小,但是老项目就是这样。用了很多年以后,会留下各种当时有用、后来已经不用的配置。平时不动它的时候没有感觉,一旦重新部署,就很容易一起冒出来。

现在怎么发博客

完成这些以后,再发一篇博客已经没有多少事情要做。如果在电脑上写,就直接修改 Markdown,然后提交到 blog-source;如果手机上修改,同样提交到这里;如果是和 ChatGPT 聊天的时候形成了一篇文章,也可以让 ChatGPT 去修改 GitHub 里的 Markdown。

后面的:

1
2
3
4
5
安装环境
生成网站
检查文件
提交网页
更新 GitHub Pages

就不需要再手工执行了。

这正是我以前想在手机上发 Hexo 博客时缺少的那一段。手机本身并没有突然变成一台可以方便运行 Hexo 的电脑,只是 Hexo 已经没有必要运行在手机或者自己的电脑上了。

最后

这次折腾博客,开始只是想解决一个很小的问题:能不能不打开电脑,也可以发一篇 Hexo 博客。最后发现,有意思的地方已经不只是 Hexo 自动部署。

OpenAI 的 ChatGPT 现在可以连接 GitHub、云盘、邮箱以及越来越多的云服务。以前操作电脑的时候,很多事情要求人准确地完成某一个步骤,然后才能进入下一步。路径可能并不复杂,但是少一个命令、改错一个文件、找错一个仓库,后面就进行不下去了。对于熟悉的人来说,这些都不是大问题,但是时间长了不用,同样还是要重新查一遍。

现在 AI 开始参与这些过程以后,中间很多障碍可以被抹平。人仍然需要知道自己想做什么,也需要判断最后的结果是不是正确,但是很多具体的操作已经不一定需要自己记得那么清楚。以前必须非常精确地做到某一步,下一步才能继续,现在很多地方可以直接说明想做什么,再由 AI 帮忙去不同的服务里查文件、改内容、看运行结果、找错误。

这次博客就是一个例子。Hexo 还是以前的 Hexo,GitHub 也还是以前的 GitHub,GitHub Pages 也没有什么变化,只是把这些服务连接起来以后,以前一直觉得比较麻烦的事情,现在变得顺了一些。

也算是科技改变生活吧。