在《AI 杂记》里,笔者说过,博客的构建和部署早就自动化了,最麻烦的事情却还在原地:文章没写。后来和 AI 一起整理想法,又给博客加了格式检查与校对,写字这件事总算顺了一些。
接下来想省掉的是另一种折腾:换一台电脑,先找仓库,再装工具,还得向 AI 重新介绍自己。
Windows 上聊过的写作习惯,Mac 未必知道;本机配置好的 Hugo,云端也不会凭空长出来。电脑一关,原先依赖它运行的事情便跟着休息。灵光一现的时候,还得先问问环境有没有准备好,灵光难免觉得自己来得不是时候。
于是今天,把这个博客的协作环境搬到了 Codex 云端。目标很具体:以后继续说想法、改文章,检查通过后接上原有的 GitHub Actions,最后在网站上读到结果。
这里记录这次实际采用的办法,以及哪些地方必须亲自验收。
先把交接写进仓库
最先准备的东西,是一份交接说明。
聊天适合讨论,仓库适合保存需要长期遵守的规则。新任务不能靠一句“按我们以前说的来”,就自动知道以前究竟说过什么。把重要约定整理成文件,换设备、换聊天时都有明确的入口,也能通过 Git 看出规则什么时候改过。
目前博客里的分工如下:
| 文件 | 保存什么 |
|---|---|
AGENTS.md | 项目入口、工作范围,以及应当继续阅读的规则 |
.codex/blog-profile.md | 博客用途、读者和协作偏好 |
.codex/writing-style.md | 中文表达、文章结构与 Markdown 习惯 |
.codex/post-frontmatter.md | TOML 元数据、草稿和发布日期规则 |
.codex/taxonomy.md | 分类与标签约定 |
.codex/workflows.md | 写作、修改、检查与发布流程 |
.codex/cloud-handoff.md | 云端工具要求、首次验收和接手提示词 |
这些文件只放与博客有关、可以公开的协作知识。没必要为了让 AI “更懂我”,把整段私人聊天搬进公开仓库。
其中有几条特别实用:日常文章主要改 content/posts/;没有明确要求,不动主题、布局和部署;新文章使用 +++ 包围的 TOML front matter;发布日期默认使用日本时区,已经发表的文章保留首次发布日期。
还有一条是权限边界:同意一句措辞,并不等于同意发布。准备环境时,只准备环境;这次写文章,则另行明确要求检查、提交并推送。
把这些说清楚,能少掉不少“我以为你希望顺便改一下”。顺便这两个字,有时比正式需求还费维护。
把工具和启动办法一起留下
这次博客使用 Hugo 和 PaperMod,文章质量工具使用 Node.js。云端配置先对照仓库里的部署 workflow,再安装对应工具:Node.js 24、Hugo Extended 0.133.0,以及 package-lock.json 固定的依赖。
主题通过 Git 子模块保存,需要初始化到仓库记录的提交,不能顺手更新到上游最新版本。否则一次环境迁移,就可能夹带一次主题升级,出了问题也难分清是谁带来的。
核心安装步骤是:
git submodule update --init --recursive
npm ci
git config --get core.hooksPath
hugo version
实际安装脚本还负责下载固定版本的 Hugo,并用官方发布的 SHA-256 校验清单核对压缩包。工具放在工作区内的独立目录,后续 shell 显式把该目录加到 PATH。
仓库旁边还选了一个 gallery。它是已经解耦出去的图片、音乐等静态资源仓库,没有 Node.js 项目,也没有需要额外启动的应用。保留这个仓库,方便维护素材即可,不必为了显得配置完整,再替它发明一套构建流程。
环境里保存了两类可复用内容:install_script 记录如何安装或刷新依赖,start_skill 记录后续任务如何读取规则、启动服务和检查就绪状态。
准备好的文件和依赖可以随环境快照保留;正在运行的开发服务器,则不能假设换一个实例以后还活着。因此,启动命令、工作目录和验收方法也要保存。只说“已经启动过”,对下一次任务帮助不大。
这次开发服务使用:
hugo server --bind 127.0.0.1 --port 1313 \
--baseURL http://127.0.0.1:1313/ --disableFastRender
这个地址用于环境内部检查。读者访问的仍然是正式博客域名,开发服务不承担公开发布。
安装完成以后,要试着干活
依赖装完,只能说明安装命令走到了结尾。要确认这个环境能写博客,还得让它执行博客平时要做的事情。
首次验收用了下面几项检查:
npm test
npm run articles:check -- content/posts/others/blog-quality-hooks-and-actions.md
HUGO_ENVIRONMENT=production hugo --gc --minify
这里特意指定了一篇现有文章。省略路径时,脚本可能因为没有选到变动文章而跳过;一个成功退出、却什么也没检查的命令,不能拿来证明文章检查已经能用。
本次准备环境时,9 项工具测试全部通过,指定文章无需格式修复;生产构建生成了 182 个页面。启动开发服务以后,又实际请求首页、文章页和搜索索引:页面返回 HTTP 200,正文包含预期内容,搜索索引包含 88 条记录。这些数字对应迁移时的仓库状态,新增文章以后自然会变化。
Hugo 构建出现了一条现有主题使用 .Site.Social 废弃 API 的警告,但没有导致失败。当前构建也没有要求额外安装 Sass,没有为了照抄部署配置而补装暂时用不到的工具。
Git hook 则单独检查 core.hooksPath,确认它指向 .githooks。不能因为 npm ci 成功,就直接宣布钩子已经启用;安装脚本可能在 CI 中跳过,也可能为保留用户原有配置而没有覆盖它。
开始和结束时,都核对了两个仓库的工作区。环境准备没有修改文章、依赖声明、锁文件或部署配置,也没有提交和推送。
连得上 GitHub,也得分清连到了哪里
这次遇到的访问问题分成两类,处理方法也不同。
第一类来自命令执行限制。最初执行 Git 命令时,提示无法连接平台代理;启动 Hugo 开发服务时,则提示不允许监听本地端口。通过平台支持的执行权限流程重跑后,仓库读取、测试和开发服务都成功了。这部分已经解决,无需另外配置 GitHub token。
第二类来自云端环境的域名白名单。当时允许访问 github.com,所以可以拉取仓库、推送文章,也能从 GitHub 网页查看 Actions 运行结果。但下面两个地址仍被环境的出站访问策略拦截,请求在代理建立连接时就收到 403,尚未取得目标网站的响应:
| 域名 | 用来做什么 | 受限时的影响 |
|---|---|---|
api.github.com | 通过 GitHub API 查询 Actions 等状态 | 无法使用 API 查询;本次改从允许访问的 GitHub 网页核对运行结果 |
guyuechen.icu | 请求正式博客文章,核对状态码与正文 | Codex 无法在云端完成公开页面的最后验收 |
这份白名单限制的是 Codex 云端发出的请求。读者使用自己的浏览器访问博客,不受它影响;GitHub Actions 也在 GitHub 的运行环境里执行。因此,Codex 无法请求文章页面,与文章是否已经部署,是需要分别核对的两件事。
本文首次发布时,文章已经成功推送到 main,格式检查、文章校对和 Hugo 发布三份 workflow 均成功。作者随后也从浏览器读到了文章。剩下的缺口,是让 Codex 自己能请求正式页面,完成自动验收。
处理办法是在 Codex 的 MyNote 环境设置中,保留已有网络配置,并把 api.github.com 和 guyuechen.icu 加入允许访问的域名。两项补充已保存到环境配置草稿,需要在设置中审阅保存,并按界面提示发布环境;保存草稿本身不会立即放开当前实例的访问。设置生效后,还要重新请求这两个地址,确认实际可用。无需为此修改博客 DNS、GitHub Actions 权限或提供新的密钥。
权限检查也应分阶段记录:准备环境时只验证仓库读取,没有做写入测试;发布本文时才通过真实推送验证写入。创建 PR 则是另一个操作,本次没有为了测试而创建 PR。
现有文章校对也不需要另配 OpenAI API key。仓库里的工具使用规则、中文词典和英文拼写检查;与它讨论文章的 Codex,以及推送后运行的校对脚本,各有自己的职责。
环境发布与文章发布,各走各的流程
准备工作完成以后,把安装脚本和启动说明保存到环境配置草稿,再由我在产品里审阅、保存并发布环境。这一步形成以后任务可用的起点。
保存草稿不会替人执行脚本,也不会自动完成环境发布。环境发布完成,更不会把博客里的文章一起推上 GitHub。
本文写作时,聊天已连接到发布后的新实例。先复查仓库和工具,再开始修改,能确认接手的是实际环境,而非只记得上一次“已经准备好了”的口头结论。
同样,新聊天有独立工作目录,不能假设另一个聊天里未提交的半篇稿子会自动出现。需要长期保留的稿件,应及时提交到合适的分支;仓库、环境和聊天,各自承担一部分记忆。
让这篇文章走一遍完整流程
这次给 Codex 的要求很直接:把上云过程整理成新文章,检查之后直接发布,再核对从指令到公开网页的整条路径。
按当前仓库配置,过程如下:
说出想法并明确发布要求
→ Codex 读取仓库规则,整理 Markdown
→ 格式检查、工具测试、Hugo 构建
→ git commit,执行文章 pre-commit hook
→ 推送 main
→ GitHub Actions 响应 push
→ Hugo 构建并部署到 GitHub Pages
→ 请求正式文章地址,核对页面内容
其中“Actions 响应 push”还要展开看。改动文章并推到 main 后,三份 workflow 分别匹配事件:
| 工作流 | 本次应当验证什么 |
|---|---|
Article checks | 工具测试和变动文章格式检查完成 |
Article proofreading | 选中本文并生成校对报告;有中文候选修正才提出 PR |
Deploy Hugo site to Pages | 自带检查通过,Hugo 构建完成,再成功部署 |
它们可以并行开始,并非前一条绿了以后才启动下一条。发布工作流内部的 deploy 等待 build,但不会靠等待另一份格式检查 workflow 来保证文章合格;它自己也会运行测试与格式检查。具体机制已在《给博客加两道检查》里展开。
校对没有候选修改时,不创建 PR 是正常结果。若真的出现校对 PR,仍然需要人工看差异,不能因为这次想测试自动化,就顺手把自动合入也算进去。
验收时,则要把几个结论拆开:本地检查通过,说明稿件能在当前环境里构建;推送成功,说明提交已到远端;Actions 运行记录,说明 GitHub 实际执行了哪些步骤;正式页面返回正确内容,才说明读者能从网站读到这篇文章。
不能用一句“已推送”概括后面所有结果。本文首次发布的提交为 e9d98e4,对应的格式检查、文章校对和构建部署均成功。作者也已通过浏览器确认文章可读;Codex 发起的正式页面请求仍等待前面提到的网络配置生效。
以后怎么继续
以后继续协作,先让任务读取 AGENTS.md 和交接说明,检查工作区、工具与 Git 连接,再提出这次具体要做什么。修改文章、审阅措辞、提交和发布,可以按照任务需要组合,但要说清楚这次授权走到哪一步。
跨设备回来继续写,先确认远端是否更新。有未提交内容就先保护好;干净且没有分叉时,再用 git pull --ff-only 同步,不让一次换设备悄悄变成一次冲突处理练习。
所谓优雅上云,对这个小博客而言,就是把以前靠记忆维持的东西逐件安顿好:规则有文件,工具有版本,启动有步骤,发布有记录,结果有页面可以核对。
以后再有一点想法,终于可以先把话说出来。至于这套办法究竟省不省事,就让眼前这篇文章先走一遍。