Knighthana
文章112
标签152
分类7

文章归档

博客构建链路改造记录

博客构建链路改造记录

博客构建链路改造记录

2026年8月,我把这个博客的构建与部署链路做了一次大改造:从”本地构建 + 提交产物”变成”仓库只存原料 + GitHub Actions 自动构建发布”。这篇文章记录改造动机、关键变更、以及过程中踩到的坑,方便日后回溯。

背景:改造前的链路

改造前的流程是:

  1. blog_builder/(一个独立于仓库的 Hexo 工程目录)里跑 hexo generate
  2. 用脚本把产物 rsync 到仓库的 docs/
  3. 手动提交 docs/ 并 push

这套流程的问题:

  • docs/ 里全是构建产物(HTML/CSS/JS),每次重建都产生大量无意义的 diff 和历史
  • 图片(约 17MB)作为普通文件躺在 git 里,仓库体积被撑大
  • Hexo 工程本身没有进仓库,换机器需要重新手工搭建环境

目标架构

1
2
3
4
5
6
7
8
9
10
11
12
仓库(只存原料)
├── raw/ 文章 markdown 原料
├── builder/ Hexo 工程(config、scaffolds、source/img 图片)
├── external/ 外部 HTML 原料
└── .github/ CI workflow + dependabot
↓ git push
GitHub Actions
├── checkout(lfs: true) ← 拉取 LFS 图片
├── hexo generate
└── deploy-pages 发布为 Pages artifact

GitHub Pages

关键点:Pages 直接以 LFS 指针文件形式暴露会挂(Pages 官方不支持 Git LFS,分支里的 LFS 文件只会以指针文本形式服务出去),所以图片必须经 CI 的 checkout(lfs: true) 拉成真实文件、构建进产物、再以 artifact 形式发布。

主要变更(对应 git 记录)

1. 重构:builder 工程入仓,docs 退役(commit 73e5752

  • blog_builder/ 的完整 Hexo 工程(_config.yml_config.nexmoe.ymlpackage.jsonscaffolds/bin/managercheck_tags.js)合入仓库 builder/
  • 图片从 docs/img/ 迁移到 builder/source/img/(真实文件),favicon 同理
  • docs/ 整体删除并加入 .gitignore——从此它是纯构建产物,不再进版本库
  • 新增 .gitattributes:栅格图片(png/jpg/jpeg/gif/webp/bmp/tiff/ico)走 Git LFS

2. 历史重写(filter-repo + lfs migrate)

  • git filter-repo --path docs --invert-paths:把全部历史中的 docs/ 产物抹掉(md 文章历史原样保留)
  • git lfs migrate import:把图片 blob 转成 LFS 对象
  • 效果:git pack 从 ~25MB 缩到 ~7MB,图片作为 59 个 LFS 对象单独存储

3. 修复:CI 里的 mtime 脚本(commit b9dae31

见下方”踩坑记录”第 2 条。

4. 安全:依赖升级消除全部漏洞(commit 5e53e3b

  • hexo-filter-mathjax 0.9.1 → 0.11.1
  • hexo-generator-feed 3.0.0 → 4.0.0
  • overrides 消除嵌套的 hexo 6.3.0(<7.2.0 有高危漏洞)
  • npm audit 归零

5. 修复:mathjax ams 扩展缺失(commit 9d5a956

见下方”踩坑记录”第 4 条。

踩坑记录

1. 仓库里现成的软链是坏的

改造前仓库的 builder/source/ 里就有 _posts -> ../raw/ 软链,但解析结果是 builder/raw/(不存在)——正确写法是 ../../raw/builder/source/imgfavicon.png 同理。教训:相对软链的层级要在迁移时实测 realpath 验证。

2. CI 里 git ls-files 的引号陷阱

为了在 CI 里恢复文件的 mtime(_config.ymlupdated_option: mtime 依赖它),我写了:

1
git ls-files 'raw/*.md' | while read -r f; do touch -d "$(git log -1 --format=%cI -- "$f")" "$f"; done

结果第一个 CI 构建直接失败。原因:git ls-files含空格/中文的文件名会输出带引号 + 八进制转义的形式(如 "raw/Ubuntu\347\232\204advantage tools\345\244\261\350\264\245.md"),read -r 拿到的是字面量引号串,touch 找不到文件,循环退出码非零 → 整个 job 失败。

修复:改用 NUL 分隔:

1
git ls-files -z 'raw/*.md' | while IFS= read -r -d '' f; do touch -d "$(git log -1 --format=%cI -- "$f")" "$f"; done

教训:git 输出文件列表给 shell 处理时,一律用 -z + read -d ''

3. 主题的”过时依赖检查”会硬性 kill 构建

升级依赖时,npm audit 报告嵌套的 hexo@6.3.0<7.2.0 有高危漏洞)——它来自 hexo-theme-nexmoe@4.2.2 声明 hexo: ^6.0.0。我用 npm overrides 把嵌套 hexo 强制统一成 8.1.2,结果构建立刻失败:

1
Package hexo's version (8.1.2) does not satisfy the required version (^6.0.0).

原因是主题自带 include/dependency.js,会对 hexo 版本做 semver.satisfies 校验,不满足就 process.exit(-1)。这不是真正的不兼容——主题代码本身在 hexo 8 下运行完全正常,只是声明过时 + 检查粗暴

解决方案:用 patch-package 固化一个补丁,把主题 package.json 里的 hexo: ^6.0.0 改成 ^8.1.1,配合 postinstall: patch-packagenpm ci 后自动应用。这样既消除了漏洞,又绕过了过时检查。

教训:npm overrides 能改依赖树,但改不掉包自身的运行时断言;npm 生态遇到这种可以 patch 源码文件。

4. mathjax 0.11 的 ams 扩展缺失

升级 hexo-filter-mathjax 到 0.11.1 后,含 align/bmatrix/pmatrix 的页面出现大量 Unknown environment 'align'

原因:0.11 版的默认包加载行为变了——defaultPackages() 只加载 ['require', 'autoload'],而 ams 扩展(align/aligned/bmatrix/pmatrix 等环境全在 @mathjax/src/mjs/input/tex/ams/AmsMappings.js)只在 tags: 'ams' 时加载。当前配置是 tags: none,所以 ams 没被加载;autoload 的动态加载在此环境下又因 Package.resolvePath 解析失败而不起作用。

修复:在 _config.yml 的 mathjax 段显式加载 ams:

1
2
3
4
mathjax:
tags: none
packages:
- ams

验证:三个受影响页面的 Unknown environment 从 86+ 处归零,tags: none 保持则没有公式编号副作用。

5. filter-repo 会移除 origin remote

git filter-repo 出于安全考虑会自动删除 origin remote,重写历史后需要重新 git remote add origin ...教训:filter-repo 后别急着 push,先确认 remote 还在。

新工作流

现在的日常操作极简:

1
2
3
4
5
6
7
8
# 写文章
$EDITOR raw/新文章.md

# 本地预览(可选)
cd builder && npx hexo server

# 发布
git add . && git commit && git push

push 后 GitHub Actions 自动完成:拉 LFS 图片 → npm ci(含 patch 应用)→ hexo generate → 发布到 Pages。docs/ 不再需要人管。

数学公式与 mathjax 的现状

关于公式渲染,早前在2023年4月折腾Hexo和Node里记录过 mathjax + pandoc 的组合(hexo-renderer-pandoc + hexo-filter-mathjax,pandoc 传 --mathjax 参数),这套组合目前仍然离不开:

  • pandoc 负责 markdown → HTML 的转换(含 LaTeX 语法透传)
  • hexo-filter-mathjax 在生成期用 MathJax 做服务端 SVG 渲染(全后端,无需前端 JS)

本次把 hexo-filter-mathjax 升到 0.11.1 后,公式渲染更完整(比如一步状态转移概率矩阵里 bmatrix 环境恢复渲染),但副作用是 @mathjax/src 及其字体包让本地 node_modules 膨胀了约 99MB。目前选择接受(本地磁盘不敏感),后续若在意体积再研究字体裁剪。

结语

这次改造把”构建”从本地手工劳动变成了 CI 的自动化环节,仓库历史从此只记录有意义的内容变更。过程中踩的坑(软链、引号、主题断言、ams 扩展)都写在了上面,希望下次折腾时能少走弯路。

Knighthana

2026/08/08