Repository navigation
Declare what each frame projection reads and returns, and fix two cop… #88
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
| # 每次 push / PR 跑一遍仓库自己声明的验证。 | |
| # | |
| # 三件事按「快 → 慢」排:格式检查不需要装依赖之外的东西, | |
| # typecheck 和 test 依赖 devDependencies,所以 npm install 只做一次。 | |
| # | |
| # **不跑真实后端。** 判定后端和生成后端都需要 key 或本地模型, | |
| # CI 里没有 —— 用 `--rule` + 空 DEEPSEEK_API_KEY 走离线路径, | |
| # 它覆盖的是「loop 能不能跑完」,不是「判定准不准」。 | |
| name: CI | |
| on: | |
| push: | |
| pull_request: | |
| jobs: | |
| check: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-node@v4 | |
| with: | |
| # 类型剥离 v22.6 引入、v22.18 才默认开启。 | |
| # 命令里显式带 --experimental-strip-types,所以 22.6 起都能跑。 | |
| node-version: '22.18' | |
| - run: npm install | |
| - run: npm run check | |
| - run: npm run typecheck | |
| - run: npm test | |
| # `npm test` 里已经有 `tests/conformance.test.ts`(它跑那 15 个突变、4 个阴性 | |
| # 对照,并断言锚点恰好命中一次),所以这一条**不是**唯一一道防线。 | |
| # 单独跑一次的另一个理由:给人看的那张表在 CI 日志里能直接读, | |
| # 而测试失败信息里只有断言。 | |
| - run: npm run conformance | |
| # ★ README 让人跑的第一条适配命令。加这一步之前,**没有任何东西跑过它**: | |
| # 既不在 CI 里,也没有测试调它 —— 它坏掉不会有任何人知道,直到有人照着 | |
| # README 敲下那一行。正是 §8.16 记的「声明了却没有消费方」。 | |
| # 现在它同时被 `tests/external-host.test.ts` 和这一步覆盖。 | |
| - run: npm run external-host | |
| # 为什么需要这一步:`package.json` 的 `exports` 指向 `dist/`, | |
| # 而上面每一步(check / typecheck / test,以及下面的 demo)走的都是 `src/`。 | |
| # 也就是说 —— **使用者唯一会拿到的那一面,恰好是仓库里唯一没人验证的一面。** | |
| # `dist/` 在 `.gitignore` 里,所以它过期了不会有任何东西报警。 | |
| # 实测:一次源码分层重构之后,`dist/` 少了 7 个模块、多了 1 个孤儿(`types.js`), | |
| # 而当时的 check / typecheck / test / demo **全部照常全绿**。 | |
| # | |
| # 比的是**运行时导出面**而不是文件名,所以 `src/` 将来拆成目录也不会误报。 | |
| # | |
| # ★ **每一个入口都要比**。加 `jevloop/contract` 时如果只比包根,这个新入口 | |
| # 就会原样重演上面那段理由 —— 使用者会拿到的第二个面没人验证。 | |
| # 少列一个入口 = 少验证一个面,所以入口清单写在这里,加一个就要加一行。 | |
| - name: dist/ 与 src/ 的导出面必须一致(每个入口) | |
| run: | | |
| npm run build | |
| node --input-type=module -e ' | |
| const load = async (p) => { | |
| try { return Object.keys(await import(p)).sort() } | |
| catch (e) { console.error(` 无法加载 ${p}:${e.message}`); process.exit(1) } | |
| } | |
| const ENTRIES = [ | |
| ["./dist/index.js", "./src/index.ts"], | |
| ["./dist/contract.js", "./src/contract.ts"], | |
| ] | |
| let bad = false | |
| for (const [d, s] of ENTRIES) { | |
| const a = await load(d) | |
| const b = await load(s) | |
| const onlyA = a.filter((x) => !b.includes(x)) | |
| const onlyB = b.filter((x) => !a.includes(x)) | |
| if (onlyA.length || onlyB.length) { | |
| console.error(` ${d} 与 ${s} 的导出面不一致 —— 构建产物已过期,或有模块没被编译进去`) | |
| if (onlyA.length) console.error(" 仅 dist 有:", onlyA.join(", ")) | |
| if (onlyB.length) console.error(" 仅 src 有:", onlyB.join(", ")) | |
| bad = true | |
| continue | |
| } | |
| console.log(` ${s} 与 ${d} 一致(${a.length} 个运行时导出)`) | |
| } | |
| if (bad) process.exit(1) | |
| ' | |
| - name: 离线冒烟(loop 能跑完) | |
| run: DEEPSEEK_API_KEY= npm run demo -- --rule |