FEATURED · 精选文章

alchemy 跨版本升级回归测试:cross-version-test 剖析与实战指南

发布时间 / 2026/9/14 16:01:49
来源 / 创域科博编辑部
栏目 / 资讯中心
alchemy 跨版本升级回归测试:cross-version-test 剖析与实战指南 alchemy 跨版本升级回归测试cross-version-test 剖析与实战指南【免费下载链接】t3code项目地址: https://gitcode.com/GitHub_Trending/t3/t3code导读本篇技术指南围绕 alchemy-effect 仓库中 test/cross-version-test 这一专项测试夹具展开介绍它如何通过原地升级同一个 Cloudflare Worker 应用的方式系统性地捕获 alchemy 从 v2.0.0-beta.39 到当前主干分支的版本间升级回归。读完本文你将理解 alchemy 账户级 Cloudflare state store 的版本戳version-stamp机制、两种升级路径逐级升级与直接跳级的编排逻辑掌握该测试的运行命令、全部 CLI 参数、已知坏路径的根因以及如何为它新增一个测试版本。背景为什么要做跨版本升级测试alchemy 的 Cloudflare 集成依赖一个账户级、带版本戳的单例——Cloudflare state store由alchemy cloudflare bootstrap负责在版本之间迁移。由于它是账户级共享资源任何栈在部署时都会与它交互而 alchemy 本身处于 2.0.0 beta 的快速迭代期state store 从 v4 一路演进到 v7旧版本客户端读新格式、新版本客户端写旧格式都可能产生线上不兼容。cross-version-test 的核心思路非常朴素但有效部署一个最小可验证应用——一个 state store 一个 Worker然后用一系列 alchemy 版本原地升级同一个应用固定 Stack 名、固定 Worker 物理名、同一账户每次升级后都去验证线上 Worker 是否真的在服务新版本从而把版本间升级路径上的回归暴露出来。测试编排run.ts 的两大场景编排入口是 run.ts它串行back to back绝不并行执行两类测试因为它们共享同一个账户级 state store 和同一个固定 Worker 名并行必然互相踩踏场景一upgrade —— 逐级 1-by-1 升级先部署最老的版本然后在同一个应用上按顺序逐级升级v4 → v5 → v6 → v7 → current让 state store 一个版本一个版本地向上走。这模拟了生产环境中最常见的渐进式升级节奏。场景二jump —— 直接跳到最新对每个旧版本先部署它然后跳过中间版本直接升级到最新当前分支例如 state store 从 v4 一步跳到 v7。这模拟了多年不升级、一次性大跨越的场景专门考验大跳级时的读写兼容性。每次都验证marker 机制两个场景在每一次部署之后都会断言线上 Worker 返回的marker必须等于刚部署版本的 marker以此证明运行中的代码确实被原地替换了而不是旧版本还在服务。每个阶段的 Worker 都会烘焙一个专属 marker例如 01-beta.39/src/worker.ts 中的const MARKER 01-beta.39以及 05-current/src/worker.ts 中的const MARKER 05-current。Worker 的响应体同时携带alchemy版本号与stateStoreVersion方便事后核对。每个场景独占全新 state store每个单元整个 upgrade 链算一个单元每个 jump 各自是一个单元都拥有一个全新的 state storerunner 在单元前后都会拆除 storeWorker secrets。也就是说 upgrade 链在自己的 store 上跑完并销毁然后每个 jump 再部署全新的 store。若想加快速度、降低隔离性可以传--reuse-store跳过拆除、跨单元共享一个 store。目录布局五个阶段的独立应用cross-version-test/ run.ts # 编排器 —— 按顺序执行各阶段 test/ 01-beta.39/ # alchemy2.0.0-beta.39 (npm 上最后一个带 state store v4 的发布) 02-beta.44/ # alchemy2.0.0-beta.44 (npm 上最后一个带 state store v5 的发布) 03-beta.45/ # alchemy2.0.0-beta.45 (npm 上最后一个带 state store v6 的发布) 04-beta.59/ # alchemy2.0.0-beta.59 (npm 上最新 v2state store v7) 05-current/ # 当前分支workspace 源码state store v7每个阶段目录都是一个独立的 alchemy 应用由三部分构成alchemy.run.ts——所有阶段完全相同见 05-current/alchemy.run.ts固定使用Alchemy.Stack(CrossVersionApp, ...)、同一个 Cloudflare state store、同一个 Worker 逻辑 ID。注释中明确说明共享 Stack 名 state store Worker 逻辑 ID外加 stage 与账户正是让后续每次部署变成对同一应用的原位升级而非新建应用的关键。src/worker.ts—— 仅marker不同旧版本如 beta.39的main用import.meta.filename而 HEAD 分支用import.meta.url见 worker.ts 注释。Worker 固定物理名cross-version-test-worker保证每次都命中同一个 Cloudflare Worker。test/integ.test.ts—— 一个示例风格的集成测试详见下文。npm 阶段与 workspace 阶段npm 阶段01、02、03、04在package.json中精确锁定alchemy版本并同时锁定匹配的effect版本通过bun install获得各自隔离的node_modules。effect的锁定至关重要alchemy 浮动在effect4.0 beta 线上旧 alchemy 配上过新的effect会直接崩溃。例如 01-beta.39/package.json 锁定effect: 4.0.0-beta.6603-beta.45/package.json 锁定effect: 4.0.0-beta.74。锁定值取该 alchemy 版本的 peer-dependency 下限beta.39 beta.44 →effect4.0.0-beta.66beta.45 →beta.74beta.59 →beta.84。文档特别记录了反面教材beta.45 搭配effect≥ beta.84 会因SchemaAST变更而崩溃。workspace 阶段05-currentpackage.json 中刻意不声明任何依赖通过 monorepo 根目录的node_modules解析alchemy/effect从而运行当前分支源码packages/alchemy。编排器在此跳过bun install直接调用 workspace 的 alchemy CLI。所有阶段共享同一身份——Stack(CrossVersionApp)、固定 Workername、同一账户——这正是每次部署都是原地升级的原因。另外TEST 2 使用stage-jump作为阶段名以便与 TEST 1 的 state 相互隔离。运行方式与全部参数在仓库根目录.repos/alchemy-effect执行# 从仓库根目录运行 —— 依次执行两个测试 bun run test/cross-version-test/run.ts --profile cloudflare-profile # 或 ALCHEMY_PROFILEcloudflare-profile bun run test/cross-version-test/run.ts # 只运行其中一个测试 bun run test/cross-version-test/run.ts --profile p --test upgrade bun run test/cross-version-test/run.ts --profile p --test jump需要说明的是README 中记录的是--test参数形式而当前 run.ts 源码 实际实现的是--group g取值sequential或jump两者对应同一能力——只跑某一类升级路径实际使用以源码为准。Flags 一览Flag默认值含义--profile p$ALCHEMY_PROFILECloudflare 认证 profile~/.alchemy/profiles.json。必填无默认值防止误操作错误账户。--group g全部只运行某一组的边sequential或jumpREADME 中记作--test namesupgrade、jump。--stage sxver应用的 alchemy 阶段名TEST 2 使用stage-jump。--only dirs全部逗号分隔的阶段目录如01-beta.39,02-beta.44。--no-install关npm 阶段跳过bun install复用已有安装。--keep关保留最后部署的应用与 state store跳过最后一个单元的拆除。--reuse-store关跨单元复用同一个 state store更快、隔离性更差而不是每单元一个全新 store。--settle sec25state store 拆除后、重新部署前等待 workers.dev 传播删除的时间秒。避免全新 bootstrap 看到刚删除 Worker 的陈旧版本。--boot-retries n3bootstrap 期间对瞬时 404/500/version-not-ready 的重试次数。编排器的健壮性设计源码层面run.ts 中有几处值得注意的实现细节强制通过force-through模式EDGES中每条升级边都在自己的全新 store 上独立尝试失败只记录、绝不中断整个运行最后打印 PASS/FAIL 汇总和机器可读的RESULTS_JSON块供下游解析见 run.ts。标记某条边为 KNOWN BAD 的方式就是把它从EDGES里注释掉并附上原因。CItrue 防挂起子进程环境注入CI: true让 state store 版本不匹配时的交互式 Yes/No 提示直接失败退出而不是在非 TTY 管道上永久挂起同时stdio关闭 stdin任何漏网提示读到 EOF 都会取消而非阻塞见 run.ts。本地 staging 状态清理clearLocalBootstrapState会删除stageDir/.alchemy/state/CloudflareStateStore防止上次被中途 kill 的 bootstrap 在云端资源已拆除后恢复到陈旧的本地后端只有 bootstrap 用本地状态应用本身走远端 store所以删除是安全的。瞬态错误重试TRANSIENT正则覆盖Decode error / not ready / version not ready / not found / 404 / 500 / 502 / 503 / fetch failed / ECONN / ETIMEDOUT等配合--settle与--boot-retries吸收部署传播窗口而真正的非瞬态失败如 v5→v6 的500 GET … DecodeError在第一次尝试就会抛出。部署采用--adopt接管已存在的同名固定 Worker 而不是报错是原地升级得以成立的关键。每版本独立集成测试每个阶段目录还自带独立的test/integ.test.ts与examples/*/test/integ.test.ts结构一致在beforeAll中部署该栈、对 Worker 发 HTTP 请求并断言线上marker、在afterAll中销毁。以 05-current/test/integ.test.ts 为例它使用独立的stage: integ避免与编排器的xver状态互相污染并通过最多 20 次、每次 1 秒的轮询容忍 workers.dev 路由传播的瞬态 404/5xx。在阶段目录内单独运行cd test/cross-version-test/test/05-current ALCHEMY_PROFILEprofile bun test # bun test 有缓冲运行结束前无实时输出注意这些测试通过账户级 state store部署因此 store 必须先处于与该目录 alchemy 匹配的版本v4beta.39、v5beta.44、v6beta.45、v7beta.59/current。要么先运行run.ts它会按阶段 bootstrap要么手动先 bootstrapcd test/cross-version-test/test/01-beta.39 bun run alc -- cloudflare bootstrap --profile profile # 把 store 升到 v4 bun test拆除 state storerun.ts会在单元之间自动拆除 state store除非--reuse-store最后还会移除最后一个单元的 store除非--keep所以一次正常运行后账户是干净的。手动移除 state storealchemy-state-storeWorker、bearer-token 与 encryption-key 两个 secrets、以及可能已变空的 Secrets Store的方法——例如在--keep运行之后或运行被中断之后cd test/cross-version-test/test/05-current # 使用当前分支的 CLI bun run alc -- cloudflare teardown --profile profilecloudflare teardown是cloudflare bootstrap的逆操作与这套测试夹具同期加入。它是幂等的且只删除 alchemy 创建的资源——如果 Secrets Store 中还持有外来 secrets会被原样保留。⚠️ 必须使用专用 Cloudflare 账户Cloudflare state store 是账户级的会被该账户/profile 上的每个栈共享。阶段01会 bootstrap state storev4如果账户上原本已有更高版本这会降级store——可能破坏使用同一账户的其他栈。请务必把它指向专用/一次性 Cloudflare 账户。runner 会在单元之间和运行结束时拆除 state store因此正常一次运行后账户是干净的。此外由于--profile没有默认值、缺省即报错退出run.ts也天然防止了误操作到错误账户。已知坏路径Known-bad upgrade paths有两条升级路径被确认是坏的因此不做测试在run.ts的EDGES中注释掉。两者都是确定性的、多次运行均可复现路径状态原因v4 → v5✅ 可用v5 → v6❌已知坏v6beta.45读不了 pre-v6 的 state——500 GET DecodeErrorv6 → v7✅ 可用v5/v6/v7 → worktree✅ 可用v4 → worktree❌已知坏v4 store 无法升级到当前—— 写入时415细节v5 → v6beta.44 → beta.45store 升到 v6 之后读取 v5 及更早写入的记录会报500 GET … DecodeError。beta.45 的 legacy-record 读取createdAt/updatedAt 重塑PR #427是坏的在 v7 中已修复——v5 → worktree跳过 beta.45可用。不要让 pre-v6 的 state 经过 beta.45。v4 → worktreebeta.39 → current当前 state-store 客户端写不了 v4 格式的 store——升级在PUT …/StateStoreEncryptionKey上报415 Unsupported Media Type。v4beta.37–39早于 v5 的 RPC state-store 重写其 store HTTP API 与当前版本在 wire 层面不兼容。应先把 v4 store 升到 ≥v5v5/v6/v7 → worktree均可用。其余路径全部通过。bootstrap 一个刚重新部署的 state-store Worker 时出现的瞬时404/500属预期现象bootstrap hoist 时 Worker 尚未就绪会被逐步重试吸收。扩展新增版本或场景新增一个test/NN-label/目录拷贝现有目录即可在package.json中锁定对应的alchemy版本npm 阶段还要同步锁定匹配的effect版本在src/worker.ts中按需设置marker与main形式旧版用import.meta.filenameHEAD 用import.meta.url在run.ts的STAGES数组中追加条目保持Stack(CrossVersionApp)与 Workername完全一致确保升级的是同一个应用。版本 → state store 版本对照表alchemystate store 版本锁定的effect备注beta.29 … 301—引入版本门控#156beta.31 … 322—beta.33 … 363—beta.37 … 394beta.66最后一个带 v4 的发布 beta.39beta.40 … 445beta.66最后一个带 v5 的发布 beta.44beta.456beta.74唯一带 v6 的发布beta.46 … 597beta.84beta.46 升到 v7PR #477npm 上最后一个 v7 beta.59 最新当前分支7workspace这张对照表不仅是测试夹具的配置依据也是 alchemy 生产升级排期时的权威参考——它直接标出了哪些版本之间可以无缝升级、哪些必须绕行如 v5 不能经过 beta.45 直达 current。小结cross-version-test 的价值在于把升级路径回归从偶发事故变成可重复、可断言、可机器解析的确定性测试固定身份原地升级 marker 校验 每单元全新 store force-through 汇总构成了一个对 beta 期快速迭代的框架而言极其实用的安全网。理解它的编排逻辑、运行参数与已知坏路径既能帮助你安全地使用 alchemy 的 Cloudflare 集成也为在 monorepo 中构建同类跨版本升级回归测试提供了可复用的范式。相关资源README · 编排器 run.ts · 当前分支 Worker · 当前分支集成测试【免费下载链接】t3code项目地址: https://gitcode.com/GitHub_Trending/t3/t3code创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
RELATED — 相关阅读

相关资讯

LATEST — 最新资讯

最新发布

TODAY — 本日精选

新闻

WEEKLY — 本周精选

新闻

MONTHLY — 本月精选

新闻