【免费下载链接】opencodex
Universal provider proxy for OpenAI Codex & Claude Code — use any LLM (Claude, Gemini, Grok, DeepSeek, Ollama…) with Codex CLI, App, SDK, and Claude Code
本文围绕 opencodex 仓库在 2026-07-22 执行的一次真实发布列车记录展开,完整讲解其
dev → preview → main → npm四层分支发布模型、版本号决策规则(为什么 2.7.32 被跳过而直接发布 2.7.33)、由 scripts/release.ts 驱动的五步执行协议,以及ci.yml+service-lifecycle.yml双门禁、GitHub Release 与 npm dist-tag 的最终收敛验证。读完你可以直接理解并复用这套"预览线 + 稳定线"的发布流程。
一、发布列车背后的分支模型
opencodex 的发布体系由四个状态层构成,每次发版都要让它们严格收敛:
| 层 | 职责 | 本次训练前状态 |
|---|---|---|
dev | 集成开发线,承载社区 PR 与修复提交 | 29763560(CI success、sol 敌对评审 PASS、部署 readiness READY) |
preview | 预览发布线,仅包含 dev 之上的"发布 bump 提交" | 26fd5ea9,即release: v2.7.32-preview.20260722(未实际部署的 bump) |
main | 稳定发布线,是 preview 的祖先 | 304a1eab(对应 npmlatestv2.7.31) |
| npm dist-tags | 对外发布通道 | latest=2.7.31、preview=2.7.29-preview.20260721 |
值得注意的是,本次训练开始时preview分支上已经存在一个名为v2.7.32-preview.20260722的 bump 提交:release.yml的 run 虽然是 success,但那次只是dry-run——npm 上根本没有2.7.32-preview版本,npmpreview通道仍停留在2.7.29-preview.20260721。这正是发布列车要解决的典型状态:"本地/分支上版本号已前移,但对外通道没有实际发布"。
关于 readiness 如何判定,可参见同一工作流的前置记录 030_deploy_readiness.md:它通过本地门禁(3431 个隔离测试通过、tsc零错误、lint/privacy/locale 全绿)、远程门禁(Cross-platform CI success)以及 sol 敌对评审三轮(最终 PASS、无残留部署阻塞项)三层证据,判定 dev 达到READY,才允许进入本次发布列车。
二、版本决策:为什么跳过 2.7.32 直接走 2.7.33
发布前首先要确定本次要发布的版本号,而这里存在一个真实的分叉点:
preview分支的package.json已经是2.7.32-preview.20260722;- 若再次执行
npm version 2.7.32-preview.20260722,npm 会报no-change 错误(版本未变化,无法生成新的 bump 提交); - 同时
2.7.32-preview.20260722属于"未部署版本"——npm、Git 标签、GitHub Release 三处都不存在该版本的任何发布产物。
因此决策是放弃 2.7.32 线,沿2.7.33 线推进:
preview发布2.7.33-preview.20260722(带日期戳的预览版);main发布2.7.33(稳定版,2.7.32 stable 被跳过——因为它从未在任何通道部署过)。
这一决策背后其实是 scripts/version-line.ts 中一整套版本解析与推演逻辑:
- 稳定版走
nextStableRelease:基于稳定通道 tip 做 patch/minor/major 递增,并检查"是否存在更高 core 的 preview 阻塞当前 patch 线"; - 预览版走
nextPreviewRelease:稳定线确定 core 后,追加-preview.YYYYMMDD日期戳;若同 core 已有预览,则用.序号递增(如-preview.20260722.2),并强制拒绝"stamp 时钟回退"; - 所有候选版本最终都要通过
assertAboveGlobalFloor——即必须严格高于已发布的所有版本(稳定 + 预览),防止把已发布的旧版本重新推到通道顶端。
从源码结构看,这套"阻塞检查 + 全局地板"机制,正是为了避免 dev 分支版本线落后于 main、导致发布回归通道 tip 的隐患。
三、五步执行协议(scripts/release.ts 规则)
原文档给出了本次发布列车的执行顺序,共五步:
- preview 检出 + 合并 dev:在 preview 上合并 dev(本次 base 之后 dev 未改版本号,因此预期
package.json无冲突)。 - preview 发版:在 preview 分支执行
bun scripts/release.ts 2.7.33-preview.20260722 --publish完整链路为:preflight → bump commit → push → 等待ci.yml+service-lifecycle.yml绿 → dispatchrelease.yml→ watch 运行结果。 - main 发版:将 main 快进到 preview tip,然后在 main 上执行
bun scripts/release.ts 2.7.33 --publish。 - dev 收敛:将 dev 快进到 main tip 并 push,本地 HEAD 回到 dev。
- 验证:
npm view @bitkyc08/opencodex dist-tags --json核对 dist-tags,并确认三个分支 tip 收敛。
release.ts的完整用法(见 scripts/release.ts 头部注释)为:
bun scripts/release.ts <version> [--tag latest|preview] [--publish] bun scripts/release.ts --bump patch|minor|major [--tag latest|preview] [--publish] bun scripts/release.ts watch其中--bump模式会自动从远端标签 + npm 通道解析下一个版本号;watch模式只观察最近一次 Release run。release.ts还有几个硬性约束值得注意:
- 分支与 dist-tag 绑定:
preview分支只能发布preview通道,main只能发布latest,--tag传错会直接报错退出; - 版本形态绑定:preview 分支必须使用带
-preview.的预发布版本,main 必须使用无预发布后缀的稳定 SemVer; - 工作区必须干净:
git status --porcelain有任何输出都会中止。
3.1 preflight:发布前的五道本地关卡
每次release.ts运行(无论 dry-run 还是--publish)都会先执行完整的 preflight,按序为:
- release metadata preflight:
assertUnusedReleaseVersion并行检查 npm 版本、远端 Git 标签(git ls-remote带 peeled 查询)与 GitHub Release 三处是否已存在同名版本,任一存在即拒绝;assertChannelVersionMovesForward保证候选版本严格高于当前通道 tip。 - dependency audit:
bun run audit:high高危依赖审计。 - typecheck:
bun x tsc --noEmit。 - test suite:
bun test --isolate tests,并刻意与 CI 保持一致——把 Worker 密集的api-storage-policy*.test.ts、api-storage.test.ts、api-usage.test.ts从通用分片中剔除、单独隔离运行(源码注释明确记录了这个分组曾导致"CI 全绿但发布门禁失败"的教训,现在门禁与 CI 使用完全相同的分组)。 - privacy scan:
bun run privacy:scan。
3.2 bump、push 与受保护分支的 SSH 钥匙
preflight 通过后,release.ts只修改package.json(npm version <v> --no-git-tag-version,版本标签由 Release 工作流在 npm 发布后创建),然后git add package.json && git commit -m "release: v${version}"并 push。
这里有一个关键设计:main和preview都配置了要求 PR 的分支保护 ruleset,管理员 bypass 是bypass_mode: "pull_request"——这只能合并 PR,不能直接 push(源码注释记录 v2.29.0 的发布正是死在这里)。因此 release.ts 提供了一个专门的写权限 deploy key 通道:
- 设置
OCX_RELEASE_SSH_KEY指向注册为该 rulesetDeployKeybypass actor 的私钥,仅 bump push 这一次改用 SSH 目标 push(通过GIT_SSH_COMMAND注入-o IdentitiesOnly=yes,防止 ssh 先尝试主维护者身份导致再次被拒); OCX_RELEASE_SSH_REPO可覆盖目标仓库(发布 fork 时使用),且必须是非凭证形态的ssh://或git@host:owner/repo;- 源码中大量校验(拒绝带 userinfo 凭证的 HTTPS remote、拒绝 scp-like 密码形态、对 key 路径做 shell 引号转义)都是为了一个目标:fail-closed——进程即使中途崩溃,分支保护也从未被削弱,撤掉一把 key 就能关闭通道而不触碰仓库配置。
未设置OCX_RELEASE_SSH_KEY时,push 行为与普通推送完全一致,对贡献者/CI 克隆无影响。
3.3 双门禁等待与 dispatch
push 完成后,release.ts依次等待两个工作流在发布 commit 上变绿:
- Cross-platform CI(ci.yml):轮询 20 分钟、10 秒间隔,成功或失败都会立即返回;
- Service lifecycle(service-lifecycle.yml):因为 bump 必然触碰
package.json(service-lifecycle 的触发路径),而release.yml的 service gate 要求同一 SHA 上已有成功的 Service lifecycle run,必须先等它,避免 dispatch 竞态。
等待之后还有一个live-remote 守卫:dispatch 前通过git ls-remote重新读取远端分支的真实 head,一旦发现"等待 CI 期间分支被移动"(本地 remote-tracking ref 可能已过期),立即中止发布——因为workflow_dispatch解析的是可变分支,这是拒绝发布未经审计新提交的最后机会。
随后执行 dispatch:
gh workflow run release.yml --ref <branch> \ -f version=<version> -f tag=<tag> \ -f expected-sha=<releaseSha> -f dry-run=<bool>expected-sha参数把"必须发布哪个不可变 commit"传给 Release 工作流(release.yml 会在发布前校验版本与分支是否匹配,若分支移动则失败)。最后waitForReleaseWorkflowRun找到对应 SHA 的 run 并gh run watch --exit-status --interval 10跟踪到结束。
3.4 dry-run 与 --publish 的二阶段语义
release.ts的默认是dry-run(dryRun = !args.includes("--publish")),但 dry-run 也会真实 bump、commit、push,目的是让 Release 工作流在真实发布 commit上完整演练。因此对同一版本二次执行--publish时,package.json已处于目标版本,脚本会将其视为"已满足"(跳过npm version的 no-change 错误),commit 也已存在则直接复用——这正是 dry-run 后能无缝转正的原因。
四、本次训练的实际结果
按上述协议执行后的最终状态(均来自原文档记录,全部为 2026-07-22 的实况):
- preview 线:dev(
29763560)合并进 preview(9a140a20)→release.ts 2.7.33-preview.20260722 --publish→ bump 提交6c112450→ci.yml+service-lifecycle.yml双绿 → Release run29904313855success → npmpreview=2.7.33-preview.20260722,GitHub Releasev2.7.33-preview.20260722。 - main 线:main 快进到 preview tip(
6c112450)→release.ts 2.7.33 --publish→ bump 提交6d6bef8b→ 双门禁 success → Release success → npmlatest=2.7.33,tagv2.7.33。 - 收敛:dev 快进到 main tip(
6d6bef8b)并 push,preview 也 push 到6d6bef8b——远端dev/preview/main三个分支同一 SHA6d6bef8b,本地 HEAD 回到 dev,工作树干净。 - 最终 npm dist-tags:
latest=2.7.33、preview=2.7.33-preview.20260722。
dev 分支 push 后按协议惯例还确认了 dev-branch 的 CI run 结果(尽管同一 SHA 在 main 上已绿,仍按规矩单独确认 dev run)。
五、可复用的发布核对清单
综合原文档与源码,一次完整的 opencodex 发布列车可归纳为以下核对点:
- readiness 前置:dev 通过本地门禁(isolate 测试 + tsc + lint/privacy/locale)、远程 Cross-platform CI、敌对评审三层判定,参见 030_deploy_readiness.md。
- 版本决策:检查 preview
package.json与 npm 实际 dist-tags 的差异;未部署的 bump 直接升线(本次即跳过 2.7.32 走 2.7.33);preview 用X.Y.Z-preview.YYYYMMDD形态,main 用稳定 SemVer。 - 顺序执行:preview 合并 dev → preview 发版 → main 快进 + 发版 → dev/preview 收敛到同一 SHA →
npm view @bitkyc08/opencodex dist-tags --json终验。 - 门禁确认:
ci.yml与service-lifecycle.yml必须在同一发布 commit 上双绿;dispatch 必须携带expected-sha;发布后核对 GitHub Release tag 与 npm dist-tag 一一对应。
参考文件
- 发布列车执行记录(本文主体):040_release_train.md
- 部署 readiness 判定:030_deploy_readiness.md
- 发布辅助脚本:scripts/release.ts
- 版本线与推演逻辑:scripts/version-line.ts
- Release 工作流(dry-run 默认、dist-tag 选项、
expected-sha守卫):release.yml - 双门禁:ci.yml、service-lifecycle.yml
【免费下载链接】opencodex
Universal provider proxy for OpenAI Codex & Claude Code — use any LLM (Claude, Gemini, Grok, DeepSeek, Ollama…) with Codex CLI, App, SDK, and Claude Code
相关推荐
opencodex 双通道发布流水线实战:从 dev 合并到 npm 的 latest/preview 双 dist-tag 发布
opencodex 双通道发布流水线实战:从 dev 合并到 npm 的 latest/preview 双 dist tag 发布 opencodex(Univ
OpenCodex v2.7.21 发布火车全解析:从 dev 同步、PR 集成加固到双通道 npm 发布的工程实践
OpenCodex v2.7.21 发布火车全解析:从 dev 同步、PR 集成加固到双通道 npm 发布的工程实践 本文以 OpenCodex(Univers
opencodex 预览通道(Preview)受控发布实战:从 `preview` 分支到 npm dist-tag 的端到端流程
opencodex 预览通道(Preview)受控发布实战:从 preview 分支到 npm dist tag 的端到端流程 导读 本文以 opencodex
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考