其他语言: English, Español, Português, 日本語, Deutsch, Français, 한국어, Italiano, हिन्दी · 全部中文指南
声明:Carbon Forge 由 100monkeys.ai 开发,因此请把这篇指南当作开发方的说明来读。它由我们的一个 AI 智能体根据公开文档(英文)起草,主要是 Code and files on the substrate 中的 "Cut a release" 和 "Publish to git" 两节、Archives 目录页和 Source control settings,并经公司审阅;这个简体中文版是英文原版指南的译文。每个调用都是文档自己的示例,引文保留英文原文,译文附在旁边。文档没有回答的问题,最后一节会说明。
两句话说清模型
"A cut names the whole workspace's state at one moment and never moves."(cut 为某一时刻整个工作区的状态命名,而且永不移动。)"The git archive renders a code tree as git: one commit per tree revision, whose message is the landing's intent, and an annotated tag refs/tags/nn/<cut> per cut, whose message carries the cut's notes."(git 归档把代码树渲染为 git:每个树修订一个提交,其消息是那次落地的 intent;每个 cut 一个附注标签 refs/tags/nn/<cut>,其消息带有 cut 的说明。)因为它是 "from the record byte for byte, so the same revision always renders to the same commit"(从记录逐字节渲染的,所以同一个修订总是渲染出同一个提交),GitHub 上的副本随时都可以和记录核对。
第 1 步:先落地这个版本要包含的工作
cut 命名的是 trunk,所以仍在 lane 上的东西不在其中。先把属于这个版本的内容落地,intent 以 feat、fix、docs、chore 或 revert 开头:发布说明就按这第一个词给每次落地归类(把一次代码修改和它的文档放进同一个 changeset 落地完整走了一次落地)。
第 2 步:切出版本
snapshots.cut { "name": "v1.4.0" } cut 是永久的。范式页面说 "the database refuses to change or remove a snapshot or a cut while the workspace exists."(只要工作区存在,数据库就拒绝修改或删除快照或 cut。)snapshots.materialize 按 cut 保存的样子读取任何实体,trees.diff 和 trees.grep 也接受快照。
第 3 步:阅读发布说明
cuts.notes { "from": "v1.3.0", "to": "v1.4.0" } cuts.notes "lists every changeset landed between the two cuts, grouped by the first word of its intent"(列出两个 cut 之间落地的每个 changeset,按其 intent 的第一个词分组),并附上每个 changeset 的来源 lane 和它涉及的路径;path_glob 可以把范围缩小到工作区的一部分。在应用中,/<workspace>/cuts/<name> 打开同样的说明,标题为 Notes from <previous cut>,Compare with another cut 可以任选两个 cut 比较。
第 4 步:把 GitHub 账户连接到实例
推送 "as the instance's GitHub App"(以实例的 GitHub App 身份)进行,所以这个 App 必须安装在拥有该仓库的账户上。实例管理员打开 Settings → Instance → Source control,点击 Connect a GitHub account,浏览器会跳转到 "GitHub's install page for the CarbonForge.dev GitHub App"(CarbonForge.dev GitHub App 在 GitHub 上的安装页面)。在那里选择账户或组织以及仓库。文档列出的权限:
- Contents: Read and write,用于推送提交、
refs/heads/main和 cut 的标签。 - Workflows: Read and write,只有当推送新增或修改
.github/workflows/下的文件时才需要;没有它,"GitHub refuses"(GitHub 会拒绝)。 - Actions: Read,用于第 8 步读取远程仓库的运行记录。
回到 Carbon Forge,该标签页会显示 Connected to @<account>。
第 5 步:渲染并校验 git 归档
archives.render { "tree": "app", "cut": "v1.4.0" }
archives.verify { "tree": "app", "cut": "v1.4.0" } render 把代码树的提交和 cut 的标签写入归档;verify 报告归档的状态,如果有差异,报告第一处差异。在 cut 的页面上,工作区管理员有同样的 Render 和 Verify 按钮,每个回答都原样显示:Rendered: tag <id>、Verify: <status>。
第 6 步:设置远程仓库
archives.set_remote { "tree": "app", "remote": "https://github.com/acme/app.git" } 远程仓库写作 https://github.com/<owner>/<repo>.git。它也是 Settings → Workspace → Source control 中每个仓库的 Remote 字段。设置它需要工作区管理员权限,"because the remote decides where the archive is published"(因为远程仓库决定归档发布到哪里)。
第 7 步:推送
archives.push { "tree": "app" } "The push sends the remote only what it lacks, fast-forward only, as the instance's GitHub App, and refuses rather than move main backwards or move a tag."(推送只发送远程仓库缺少的内容,只做快进,以实例的 GitHub App 身份进行;它宁可拒绝,也不会让 main 后退或移动标签。)它作为任务排队(Push queued as job <id>.)。拒绝会说明原因,例如 The tree <path> has no archive remote; set one with archives.set_remote, and nothing was pushed.,或者说明归档 "has no rendered cut"(没有已渲染的 cut)。来自 GitHub 的拒绝会以 The remote refused <ref>: <reason>. 返回,附带 GitHub 自己的理由。
"The remote is a copy for git tooling and readers; nothing is read back from it."(远程仓库是给 git 工具和读者的副本;不会从它读回任何东西。)工作继续在 Carbon Forge 中进行,下一个 cut 再推送一次。
第 8 步:回读部署结果
"Once a cut is pushed, its deploy can be read back"(cut 推送之后,可以回读它的部署):archives.deploy_status 返回远程仓库针对该 cut 提交的 GitHub Actions 运行,最多 100 个,并附摘要,"and the record still takes nothing from the remote."(而记录仍然不从远程仓库获取任何东西。)cuts 索引把每个代码树标记为 pushed、tagged 或 not rendered。在 cut.taken 或 archive.pushed 上的 webhook 可以不经轮询就通知部署脚本。
文档没有回答的问题
- 首次推送到已有历史的仓库。推送只做快进,拒绝让
main后退;文档描述了一种需主动开启的历史改写,但没有讲已存在一个无关的main时该怎么办。 - GitHub 以外的托管平台。文档中的每个远程仓库都是 GitHub URL。
- 自托管部署能否使用 CarbonForge.dev App,还是必须注册自己的 App;设置页面列出了运维设置,但没有说明这一选择。