写博客最容易被忽略的部分,往往不是写作,而是“写完以后怎么稳定地发布”。文章内容可以在十几分钟内完成,真正让人犹豫的却是:配置有没有被顺手改坏,生成目录是不是最新,远端同步会不会把不该删的文件一起删掉,以及下次换一台电脑还能不能把流程重新跑起来。

我现在把 Hexo 博客当成一个很小但完整的软件项目来维护。它不需要复杂的流水线,却需要几个清晰的护栏:内容有规范,构建有固定入口,发布前先演练,失败时能停在原地查原因。

先把仓库当作产品

这个仓库的职责可以按四层来理解。source/_posts 放文章,source/images 放可复用的小图片;_config.yml 管 Hexo 基础行为,_config.butterfly.yml 管主题外观;ops 里的脚本负责检查、构建和部署;docs 则保存那些“为什么这样做”的说明。

这样的分层很朴素,却能减少误操作。日常写文章只需要新增一个 Markdown 文件,不必打开主题子模块。想调整页脚或导航时,优先查看根目录配置。需要发布时,先找现成脚本,不临时拼一串难以复述的命令。

我还会在每次开始前先看一眼工作树:

git status --short --branch

如果已经有未提交修改,就先记住它们属于哪一次工作。发布新文章时只暂存本次新增文件,不把别人的配置改动混进来,这个习惯比“提交前再仔细看一遍”更可靠。

写作阶段先做小检查

每篇文章至少要有标题、日期、标签、分类和描述。文件名使用小写连字符,例如 hexo-butterfly-publishing-workflow.md,这样生成出来的 URL 稳定,也不依赖中文文件名的编码细节。

我通常先把 front matter 写完整,再开始正文。这样做的好处是,文章从第一分钟起就已经是一个可发布对象。分类用于栏目归档,标签用于更细的检索;分类不随文章临时发明,标签也尽量复用已有词表。

内容写完后,先运行仓库检查脚本:

pwsh ./ops/check.ps1

它会确认关键目录、配置入口和文章的必填字段。检查通过不代表文章已经“写得好”,但至少能排除一批低级错误。

构建要有固定入口

本地直接运行一条 Hexo 命令当然很快,但不同电脑上的 Node 版本、依赖版本和系统路径可能不同。仓库把 Docker 作为默认构建方式,把环境差异收进脚本里:

pwsh ./ops/build.ps1

如果当前机器没有可用的 Docker,再使用 npm 兜底:

pwsh ./ops/build.ps1 -Mode npm

无论选择哪种模式,目标都是得到一个完整的 public/。我会在构建后检查新文章对应的目录是否生成,而不是只看命令有没有返回成功。对静态站点来说,“命令成功但页面没生成”仍然是发布失败。

dry-run 是发布清单

部署脚本会把 public/ 同步到站点目录。同步动作通常带有镜像语义,所以我把 dry-run 当成必经步骤:

pwsh ./ops/deploy.ps1 -DryRun

这一步要确认三件事:目标主机是不是预期的主机,目标目录是不是站点目录,待同步的文件是不是本次构建产物。dry-run 通过后,才执行正式部署:

pwsh ./ops/deploy.ps1

如果工具链切换到了 scp/ssh 兜底路径,也不要跳过演练。远端同步是有外部影响的动作,先确认范围,再让它发生,成本最低。

出错时保持最小动作

构建失败时,先看 Markdown 语法、front matter 和依赖输出;部署失败时,保留终端日志,确认网络、密钥路径和远端目录,再决定下一步。不要为了“让工作树变干净”直接重置未知修改,也不要在错误原因不明时反复执行覆盖式部署。

真正可依赖的发布流程,不是永远成功,而是失败时能停在一个可解释、可恢复的位置。只要文章、构建产物和命令记录都在,下一次修复就不会从猜测开始。

小结

我喜欢这种低风险流程的原因很简单:它把发布从一次紧张的操作,变成几步可重复的检查。写作负责表达,脚本负责守边界,文档负责让别人接得住。

如果你也在维护一个个人博客,可以继续看如何把 AI 工具变成稳定工作流;发布流程之外,个人项目的长期维护习惯同样值得提前设计。