Rivet Actors 发布机制全解:前端 prod 分支晋升、just release 与“本地切版 + CI 发布”两级流水线
【免费下载链接】actorsRivet Actors are the primitive for stateful workloads. Built for AI agents, collaborative apps, and durable execution.项目地址: https://gitcode.com/GitHub_Trending/riv/actors
本文基于仓库内部发布文档 RELEASING.md,完整拆解 Rivet Actors 的三类发布路径:前端服务经prod分支强推晋升到生产、官网直接从main部署、以及引擎(Engine)与 SDK 通过just release命令触发的完整版本切分流程。读完本文,你能掌握just release各参数的实际行为、本地切版脚本cut-release.ts的十步编排逻辑,以及与之配套的 CI 发布工作流 publish.yaml 中 npm 包、crates.io、R2 制品与 Docker 镜像的产出关系。
发布对象总览:三条互不相同的路径
Rivet Actors 仓库的“发布”并非单一流程,而是按产物拆分为三条路径,各自部署源与触发方式都不同:
| 发布对象 | 部署源 | 触发方式 |
|---|---|---|
前端服务(dashboard.rivet.dev、inspect.rivet.dev等,不含官网) | prod分支 | 本地手动执行promote-prod.sh强推 |
| 官网(website) | main分支 | 随main分支自动部署 |
| 引擎 + SDK(npm 包、crates.io 包、引擎二进制、Docker 镜像) | Git tag + GitHub Actions | 本地just release切版后触发 workflow |
其中最容易混淆的一点在原文档中被明确区分:官网走main而不是prod,也就是说promote-prod.sh推上去的提交只会影响前端服务(Dashboard / Inspector 等),不会影响官网页面。
前端生产发布:promote-prod.sh 与 prod 分支
前端服务的生产发布流程在 RELEASING.md 中定义得非常直接:
# 标准流程:校验后强推当前 main 到 prod 分支 ./scripts/frontend/promote-prod.sh # 跳过校验,直接把当前 ref 推到 prod ./scripts/frontend/promote-prod.sh --force对照脚本实现 promote-prod.sh,可以看到校验逻辑只有三行核心判断:
git fetch origin main if [ "$(git branch --show-current)" != "main" ] || \ [ "$(git rev-parse HEAD)" != "$(git rev-parse origin/main)" ]; then echo "Error: Must be on main branch and up to date with remote (use --force to override)" exit 1 fi git push --force origin HEAD:prod即:默认模式下必须先git fetch origin main,然后断言“当前分支是main”且“本地 HEAD 与远端origin/main完全一致”,校验通过后执行git push --force origin HEAD:prod。--force参数会完全跳过上述校验,直接把当前 ref(注意是HEAD,不一定是main)强推到prod。这个设计意味着你可以临时把某个历史提交推上生产做回滚,但代价是完全绕过了分支与同步性检查,文档将其定位为紧急操作手段。
为什么用分支而不是 tag?
原文档用折叠块解释了这一决策:Railway 不支持基于 tag 部署服务,因此团队把prod分支当作“类 tag”来用——每次发布都是把main的完整历史强推到prod,而不是在prod上积累自己的提交。这解释了为什么脚本用的是--force推送而非普通推送:prod分支的语义就是“最近一次被晋升到生产的快照”。
引擎发布:just release 命令族
引擎(以及配套的 TypeScript / Rust SDK)发布统一入口是 justfile 中的release任务:
just release --patch # 补丁版本(如 1.0.0 -> 1.0.1) just release --minor # 次版本(如 1.0.0 -> 1.1.0) just release --major # 主版本(如 1.0.0 -> 2.0.0) # 发布指定版本 just release --version 1.2.3justfile中该任务只有一行转发逻辑(justfile):
[group('release')] release *ARGS: pnpm --filter=publish release {{ ARGS }}而 scripts/publish/package.json 中release脚本指向tsx src/local/cut-release.ts。也就是说,just release的所有参数最终都传给本地切版编排器 cut-release.ts,其文件头注释明确写着“Linear release cutter — called by humans, never by CI”(线性切版器——只由人调用,绝不被 CI 调用)。
文档记载的全部参数与源码中的实际选项
除文档中的三个 bump 参数外,cut-release.ts的 CLI 定义(cut-release.ts)还暴露了以下选项:
| 选项 | 作用 |
|---|---|
--version <version> | 显式指定版本号(如2.5.0),跳过基于 git tag 的自动计算 |
--major/--minor/--patch | 基于最新稳定 tag 做 semver 递增 |
--latest/--no-latest | 显式控制是否标记为latest(默认由脚本自动判定) |
--dry-run | 不 commit / push / 触发 workflow,但仍会修改源文件 |
-y, --yes | 跳过交互式确认 |
--skip-checks | 跳过本地构建 + 类型检查的快速失败环节 |
文档还记载了“复用上一次发布产物”的用法:
just release --patch --reuse-engine-version 1.0.0用于跳过 Docker 镜像与引擎二进制的重新构建。需要注意的是,在当前仓库的cut-release.ts源码选项中并未检索到--reuse-engine-version参数的定义(该选项可能属于较早版本的发布脚本,或经由其他实现路径生效),因此实操时建议以just release --help的实际输出为准——这也正是原文档“Runjust release --helpfor all available options”这句提示的意义所在。
版本号如何被解析:resolveVersion 与 shouldTagAsLatest
版本号解析逻辑位于 version.ts:
resolveVersion(version.ts):若未给--version,则先git fetch --tags --force拉取全部远端 tag(拉取失败会直接报错“refusing to compute latest flag from stale local tags”,拒绝基于过期本地 tag 计算),过滤出合法 semver 的v*tag,取最高稳定版(无 prerelease 标识)作为基准,再按--major/--minor/--patch执行semver.inc。若仓库中还没有任何版本 tag,会提示改用--version显式指定。shouldTagAsLatest(version.ts):自动判定latest标志的规则是——版本不带 prerelease 标识,且严格大于现有最高稳定 tag。这意味着2.5.0-rc.1这类候选版本绝不会自动抢占latest;发布 rc 版时latest默认保持指向已发布的稳定版。
cut-release 的十步流程:从修改源文件到触发 CI
cut-release.ts的头部注释(cut-release.ts)列出完整步骤,实际实现与之一一对应。把本地这一步读懂,就理解了整个引擎发布的“前半程”:
- 解析目标版本(flags → semver bump → 否则报错);
- 确认
latest标志:显式参数 > 自动判定 > false; - 校验 git 工作区干净(
validateClean); - 打印发布计划:版本、latest 标志、当前分支、上一个版本、最近 10 个版本列表,然后交互确认(
Proceed with release? (yes/no)); - 更新非 package.json 源文件(
updateSourceFiles):改写根 Cargo.toml 的[workspace.package]version,以及examples/**/package.json中对rivetkit/@rivetkit/*的依赖锁定为^<version>; - Cargo 工作区依赖钉版(
bumpCargoVersions):把[workspace.dependencies]中内部 crate 的version = "=X"精确钉版全部同步到新版本——源码注释指出若漏掉这一步,Rust/wasm 构建会因内部 crate pin 版本不一致而解析失败; - 重写所有可发布 package.json 的 version 字段(
bumpPackageJsons,versionOnly: true模式):只改version,保留workspace:*依赖写法,因为 lockfile 依赖这些写法,直接提交字面量版本会破坏pnpm install --frozen-lockfile; - 执行
./scripts/fern/gen.sh重新生成 Fern 文档产物; - 本地类型检查快速失败:运行
pnpm build(针对 rivetkit 与@rivetkit/*,排除 napi/wasm 等平台包)+cargo check --workspace --exclude rivetkit-wasm,把编译错误拦截在触发 CI 之前; - 提交、推送并触发 CI:
git add .后以chore(release): update version to <version>提交;若当前在main分支直接git push,否则优先尝试 Graphite 的gt submit --force --no-edit --publish,失败则回退git push -u origin <branch>;最后通过gh workflow run .github/workflows/publish.yaml -f version=<v> -f latest=<bool> --ref <branch>触发发布工作流。
一个值得注意的细节:--dry-run只跳过第 10 步的 commit/push/trigger,第 5~7 步对源文件的修改依然会发生(源码注释明确写道 “still mutates source files”),因此 dry-run 之后需要手动还原工作区改动或重新检查 diff。
发布后端:publish.yaml 工作流的“另一半”
cut-release.ts触发的是 publish.yaml,这条 workflow 同时承担预览发布(preview publish)与正式切版(release cut)两种模式,两者构建步骤完全一致,只有 npm dist-tag、retag、git tag 和 GitHub Release 的差异:
- trigger=branch(不带 version 的手动派发):npm_tag 为清洗后的分支名,build_mode 为 debug,用于 PR 预览包;
- trigger=release(带 version 派发):npm_tag 默认
latest;若版本号含-rc.则为rc,若latest=false则为next。
CI 侧的执行入口是 bin.ts,注释强调“每个 workflow step 恰好调用一个子命令,workflow 本身就是编排者”。子命令按职责划分:
| 子命令 | 职责 |
|---|---|
context-output | 解析一次 PublishContext(trigger/version/npm_tag/sha/latest/targets),写入$GITHUB_OUTPUT供后续步骤复用 |
bump-versions | 发布时刻才执行完整版bumpPackageJsons:把workspace:*改写为字面量版本、向 meta 包注入optionalDependencies(按 os/cpu/libc 解析平台二进制),并校验没有workspace:/catalog:协议残留——源码注释提到曾因catalog:泄漏发布过不可安装的 rivetkit@2.3.14,所以现在会 fail loudly |
publish-npm | 并行发布 npm 包(默认并发 16、每包重试 3 次) |
publish-crates | 按依赖顺序发布 crates.io(默认每 crate 最多等 10 分钟索引),已存在则跳过,含 crates.io 限流重试 |
upload-r2/copy-r2 | 制品先传rivet/{sha}/engine/,再复制到rivet/{version}/engine/(latest 时加rivet/latest/) |
docker-manifest/docker-retag | 为 commit sha 生成多架构 manifest,再 retag 到版本/latest |
git-tag/gh-release | 强制创建并推送v{version}tag,创建/更新 GitHub Release |
comment-pr | 在预览 PR 上 upsert 安装说明(npm install rivetkit@<tag>、docker pull rivetdev/engine:slim-<sha>等) |
crates.io 发布顺序在源码中被硬编码为依赖拓扑序(bin.ts 的RUST_CRATES列表),注释给出了两条关键约束:rivet-envoy-protocol必须先于精确 pin 它的rivet-depot-client发布,否则 cargo 无法解析;rivetkit-engine-process虽是rivetkit-core的可选依赖,但 cargo 在发布时仍要求它能在 crates.io 上被解析,所以也必须在rivetkit-core之前。
此外 justfile 还提供了一个不走just release的旁路:
just preview-publish <REF> # 等价于: gh workflow run .github/workflows/publish.yaml --ref "<REF>"对任意 ref 手动派发 workflow(不带 version),即可产出该分支的预览包,供 PR 评审或灰度验证使用。
发布前自检清单
结合文档与源码,一次安全的引擎发布应满足:
- 本地工作区干净且 tag 已
git fetch到最新(脚本会强制校验,但人工确认更稳); - 明确本次是稳定版(可自动成为
latest)还是 rc 版(latest默认不切换); - 留意
--dry-run仍会改写Cargo.toml与各package.json,dry-run 后需检查并还原 diff; - 切版提交推送后,确认 workflow 触发成功,并跟踪 context → build → npm/crates/R2/docker → git tag → GitHub Release 各阶段;
- 前端生产变更走
promote-prod.sh,且非必要不使用--force跳过 main 分支与远端同步校验。
小结
Rivet Actors 的发布体系体现了“本地编排 + CI 执行”的清晰分工:RELEASING.md 中简洁的三条路径背后,前端侧是prod分支强推这一最小可用机制(受限于 Railway 的部署能力),引擎侧则是 cut-release.ts 的十步线性切版流程与 publish.yaml 中高度子命令化的 CI 发布流水线。版本解析基于 git tag 的稳定版 semver 递增,latest标志由“无 prerelease 且严格高于现有稳定版”自动判定,npm 与 crates.io 双通道按依赖拓扑序幂等发布,制品(二进制、Docker 镜像、安装脚本)统一沉淀到 R2 的rivet/{sha} → rivet/{version}/latest晋升路径上。理解这套机制后,无论是排查一次失败的发布,还是为新版本选择合适的 bump 策略,都能直接定位到对应源码环节。
【免费下载链接】actorsRivet Actors are the primitive for stateful workloads. Built for AI agents, collaborative apps, and durable execution.项目地址: https://gitcode.com/GitHub_Trending/riv/actors
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考