chore(ci): nightly rerun-safety gate, job timeouts, compiled-tests-in-dist guard - #4156
Merged
Merged
Conversation
…-dist guard Three CI changes, all lessons #4065 taught the hard way. CI configuration only. 1. Nightly rerun-safety gate. Every job in this repo runs on a fresh clone, which makes CI structurally incapable of seeing a suite that pollutes its own working tree and therefore passes exactly once — CI always runs pass #1, so it is always green. #4065 sat in the repo through every CI run it ever had and surfaced only because somebody ran the full suite twice in one checkout while doing unrelated work, where it looked like THEIR change had broken something. The new job runs the full suite twice in one tree with `--force` (turbo would otherwise replay the cache and report green without executing anything) and fails if pass 2 disagrees with pass 1. It also prints any `.objectstack/` directories left between passes, so a failure names a file instead of reading as flakiness. 2. `timeout-minutes` on all eight ci.yml jobs. There were none, so every job inherited GitHub's 6-hour default. On PR #4100 the Test Core job hung with no output for 80 minutes and would have held a runner for six hours — and the whole time the PR read as "still running" rather than broken, which is the worst failure mode a gate can have. Ceilings are ~3-4x the healthy observed duration, so a genuinely slow run still passes. 3. A build-output guard against compiled test files. A package built with plain `tsc` that does not exclude tests emits `dist/**/*.test.js`. `files: ["dist"]` then publishes them to npm — and, worse, a package with no vitest config COLLECTS those compiled copies alongside its sources, so every `src/**/*.test.ts` also runs as a stale `dist/**/*.test.js` frozen at the last build. @objectstack/cli shipped exactly that (81 test files / 849 tests where its sources hold 58 / 581) until #4065 excluded them. That silently defeats edits: a fix to a source test appears not to work because the run is still executing the pre-fix duplicate. Everything else builds with tsup, which emits only declared entry points — so this gate exists to stop the NEXT tsc-built package repeating it, not to re-check the one already fixed. Verified: both workflow files parse as YAML and every extracted `run:` block is valid bash; the dist guard reports clean on a correctly-built tree and catches a planted `dist/**/*.test.js`. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L5q3BZMpdiAueHf2emhDY9
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
os-zhuang
marked this pull request as ready for review
July 30, 2026 13:09
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
#4100(#4065)的后续。三处 CI 改动,全是那次排查换来的教训。只动 CI 配置,不碰任何包。
1. Nightly rerun-safety —— 补上 CI 的结构性盲区
这个仓库每个 job 都跑在 fresh clone 上,这让 CI 在结构上不可能看见一类缺陷:污染自己工作树、因而只能通过一次的套件。CI 永远在跑第 1 遍,所以永远是绿的。
#4065正是如此。InMemoryDriver默认persistence: 'auto',在 Node 下就是文件适配器——于是一个用固定 id 播种两行的套件把它们写进.objectstack/data/memory-driver.json,下一次运行先载回来再断言四行,第 N 次读到 2N 行。它带着这个缺陷通过了历史上每一次 CI,最后是有人在做不相干的工作时在同一个 checkout 里跑了第二遍才浮出来——而且当时看起来像是他自己的改动把东西弄坏了。新 job 在同一棵树里把全量套件跑两遍,第二遍与第一遍不一致就失败。
--force:不加的话 turbo 第二遍会直接回放缓存、什么都不执行就报绿——恰好是这里最不想要的答案。.objectstack/目录(仅信息,不判定)。这样一次失败给出的是一个具体文件,而不是「疑似 flaky」。其中有些是合法的(example app 自己的项目目录),所以由第二遍来做断言,这一步只做报告。2. 给
ci.yml八个 job 补timeout-minutes原本一个都没有(
grep -c timeout-minutes= 0),全部继承 GitHub 的 6 小时默认值。就在 #4100 上,
Test Core出现过 80 分钟无输出——若不干预会占住 runner 六小时,而且整个过程 PR 显示的是「还在跑」而不是「坏了」,这是一个门禁最糟糕的失败形态。(那一次事后证明是 API 状态陈旧、job 其实已完成,但 6 小时无上限这件事本身仍然要修。)上限取健康耗时的 3–4 倍,真正偏慢的运行仍能通过:
3. 禁止 dist 里出现编译后的测试
用裸
tsc构建且没排除测试的包会产出dist/**/*.test.js。两个代价,第二个才是危险的那个:files: ["dist"]会把测试发布到 npm。src/**/*.test.ts又以一份停留在上次构建时刻的dist/**/*.test.js再跑一遍。这会静默地让改动失效:对源码测试的修复看起来「没生效」,因为跑的仍是修复前的编译副本——#4100 里我就被这个骗了一轮,改对了、单文件验证通过、全量仍红。它同样允许源码测试被改成通过、而陈旧副本继续断言旧行为,两边都不显眼。
@objectstack/cli当时就是这样(81 个测试文件 / 849 个用例,而源码只有 58 / 581),已在 #4100 修掉。仓库其余部分用 tsup 构建,只产出声明的入口——所以这道门是为了挡住下一个 tsc 构建的包,不是复查已修好的那个。验证
run:块都是合法 bashdist/**/*.test.js后确实捕获(非空洞断言)关联
pnpm test只能跑绿一次:InMemoryDriver 默认把数据落盘,datasource-autoconnect 测试在第二次运行必然失败 #4065 / fix(driver-memory): persistence opt-in again + move the remaining suites to in-memory SQLite (#4065) #4100(来源)sys_notification但不声明它——靠 platform-objects 恰好也加载才能工作 #4154(同批发现的sys_notification归属错位,另行处理)Generated by Claude Code