先说结论:一个内容量不大的静态知识站,可以在不增加 VPS、数据库和自建 CMS 的情况下获得可视化编辑、草稿预览、自动验证和可回滚发布。
这套链路由站主与 Codex 协作完成:站主定义网站目标、成本边界和发布规则,Codex 协助配置、测试和证据整理。本文只把能被仓库和构建结果支持的行为写成事实。
本站的日常编辑约定是:Pages CMS 选择 GitHub 的 main 保存,隔离测试也可以在非生产分支保存;Cloudflare Pages 为非生产分支生成 Preview,GitHub Actions 在发布前重新验证。当前受控发布工作流只在验证通过后,通过普通 push 将 production fast-forward 到指定的 main 提交,最后由 Cloudflare Pages 更新正式站。
这套组合不要求为 CMS 另建 VPS 或内容数据库,但不等于“零成本”。GitHub Actions 受账户套餐额度约束,Cloudflare Pages 也有计划与用量限制,域名还有原有续费。本文讨论的是减少一套自建服务,不是宣称已核对账单或永久免费。
需要的是编辑入口,不是第二套内容系统
这个网站最初已经有一条可靠的最短路径:Astro 读取仓库里的 Markdown,构建静态页面,再由 Cloudflare Pages 托管。真正的摩擦不是缺少数据库,而是每次编辑都要直接处理 frontmatter、文件名和 Git。
WordPress、自托管 CMS 或数据库型 CMS 都能解决更多问题,但也会增加另一组长期责任:运行时升级、备份、权限、数据库迁移、服务监控,以及“仓库和 CMS 到底谁是真实来源”的同步问题。对当前规模来说,这些能力还没有对应的真实需求。
Pages CMS 的定位刚好更窄。它在 GitHub 仓库之上提供编辑界面,保存结果仍是 Markdown 和媒体文件,没有再造一份内容数据库。这延续了本站在先做出知识网站,再设计完整知识系统中的原则:先让真实使用暴露下一层摩擦,再决定要不要扩展架构。
五个环节各自只做一件事
这条链路可以压缩成一行:

Pages CMS 编辑
→ GitHub main 保存
→ Cloudflare Preview
→ GitHub Actions 验证
→ production fast-forward 并由 Cloudflare 正式部署
它看起来只是几个现成工具的连接,关键却在职责边界。
1. Pages CMS:编辑文件
仓库根目录的 .pages.yml 描述了中文和英文内容字段、允许的图片格式、双语配对键和草稿默认值。编辑器可以创建内容,但不能重命名或删除现有条目。settings.content.merge: true 用来保留 Schema 没有暴露的 frontmatter 字段。
Pages CMS 只负责把一次编辑变成 Git 提交。它不决定这次保存能否进入正式站。
2. main:保存日常内容
本站的日常内容约定保存到 main;这不是 Pages CMS 对所有保存的技术强制。这个约定让内容拥有 Git diff、提交历史和 Cloudflare Preview,但不会直接更新正式域名。
把保存和发布拆开很重要。编辑者可以先检查标题、双语配对、移动端换行和链接,不必在按下“保存”时同时承担生产发布的后果。
3. Preview:展示候选,并设置反索引指令
非生产分支允许渲染 draft: true 的内容,并在页面上显示草稿标识。所有 Preview 页面带有 noindex, nofollow,Preview 的 robots.txt 使用 Disallow: /,canonical 仍指向正式域名。这些配置的目标是减少草稿的搜索暴露,不是提供访问控制。
Preview 是否公开取决于 Cloudflare Access 配置;未启用 Access 时,知道 URL 的人可能直接访问。无论是否启用 Access,noindex 都只是索引指令,不是访问控制。Google 只有在抓取页面后才能识别页面内的 noindex;如果 robots.txt 阻止抓取,爬虫反而可能看不到它。
4. GitHub Actions:先验证,再移动生产分支
Pages CMS 里的“验证并发布”按钮触发 workflow_dispatch。工作流检查 payload 中声明的 source、action、repository 和 ref 字段,再确认请求的 SHA 是否等于当前 main,并检查 production 是否仍是 main 的祖先。这是输入合同校验,不是对触发者身份的独立密码学认证。
条件成立后,它才运行:
npm ci
npm audit --audit-level=moderate
npm run verify
npm run verify 又会检查内容 Schema、双语配对、Astro 类型、单元测试、生产构建、草稿排除、canonical、hreflang、sitemap、RSS、内部链接和许可证。任何一步失败,production 都不会移动。
5. production:由当前工作流执行普通 fast-forward
验证通过后,当前受控发布工作流执行普通 push,把 production fast-forward 到精确的 main 提交。它不使用 force push;如果两个分支已经分叉,这条工作流会停止,维护者必须先理解分叉原因。仓库目前没有经过本轮验证的 ruleset 或 branch protection 证据,因此这里不把工作流约束描述成分支本身能阻止所有其他写入。
Cloudflare Pages 只把 production 当作正式发布分支。这样,一次 Pages CMS 保存最多产生 Preview,不会越过验证直接改动 lab.omnihex.xyz。
草稿隔离不是一个开关
这套系统没有把安全寄托在单个 draft 标签上。
生产数据查询只接受 draft === false;静态路由生成会排除草稿;首页、知识列表和 RSS 使用同一组已发布内容;sitemap 过滤草稿 URL;详情页只为非草稿输出 BlogPosting JSON-LD。构建后,scripts/verify-dist.mjs 还会扫描所有 HTML、XML 和文本输出,确认没有草稿 slug。
换句话说,draft: true 同时受到“生成路径”和“构建产物”两层检查。它可以在开发和 Preview 中被人工查看,但不能只靠编辑器状态决定是否公开。
少一套自建服务,不等于零成本
这里只比较这套实现会不会再引入一套必须自己运行的服务,不核算账户的实际账单。
| 环节 | 当前项目的固定增量 | 仍需关注的成本 |
|---|---|---|
| Pages CMS 托管编辑器 | 不需自建 CMS 运行时或内容数据库 | 第三方托管可用性、GitHub App 权限和产品变化 |
| GitHub 私有仓库与 Actions | 复用已有 Git 内容源 | 私有仓库 Actions 超过包含额度可能计费或停止 |
| Cloudflare Pages 静态托管 | 不需另外运行内容服务器 | 计划与用量仍有构建、项目、文件数量等限制 |
| 域名 | 使用已经持有的 omnihex.xyz 子域 |
域名本身仍有原有续费成本 |
人的维护时间也不是零。有人仍要审阅事实、检查 diff、处理失败构建和决定何时发布。这个系统减少的是额外服务器与内容同步,不是取消编辑责任。
这条链路怎样被验证
验证不是一张架构图,而是几个可以复查的检查点:
- 接入当时的四篇旧内容显式使用 Boolean
draft: false,新内容默认draft: true; - 中英文临时草稿曾在隔离分支完成真实创建、配对、图片上传和清理;
- 候选分支的 GitHub Actions 同时运行生产构建与 Preview 隔离检查;
- 当前受控发布工作流只在门禁通过后,把指定的
main提交 fast-forward 到production; - 后续候选又补上 SEO gate 和正式域名验收;
- 正式测试检查 sitemap 中的页面、404、验证标记和
pages.dev重定向。
这些检查点共同证明的是“链路按当前规则运行”,不是所有未来编辑都不会出错。Cloudflare rollback 可以先把当前生产流量切回上一份成功部署,但不会自动修改 Git 分支;紧急回滚后,仍要通过 git revert 或修复提交恢复 Git 历史与实际部署的一致性。
适合谁,不适合谁
这套组合适合内容以文件为主、发布频率有限、希望保留 Git 审计和静态部署优势的小团队或个人站点。它尤其适合已经有 Astro、Hugo 或 Jekyll 项目,只缺一个编辑界面的情况。
如果需要复杂角色权限、多人实时协作、站内表单、评论、支付、个性化查询或大量结构化关系,文件型 CMS 可能不够。那时数据库和应用后端不是“架构过度”,而是业务需求的一部分。
本文的判断不是“所有网站都应该这样搭”,而是:在需求仍然简单时,不要为了获得编辑界面就提前接管一整套服务器。先保留一条能看见 diff、能阻断失败、也能回滚的最短路径,再根据真实摩擦升级。
这也符合公开实验室的内容原则:从真实项目材料整理成可复用知识,同时把失败、边界和验证方法一起留下。