Skip to content

chore(ci): 核对发布产物,并停止发布测试文件 - #191

Closed
lbb00 wants to merge 3 commits into
mainfrom
chore/publish-contract
Closed

chore(ci): 核对发布产物,并停止发布测试文件#191
lbb00 wants to merge 3 commits into
mainfrom
chore/publish-contract

Conversation

@lbb00

@lbb00 lbb00 commented Sep 2, 2026

Copy link
Copy Markdown
Collaborator

问题

有两类问题,本仓的构建、测试、门禁全都看不见,只在包发布出去、被别人装上之后才暴露:

  1. exports / main / types / bin 指向的文件没进 tarball。 files 少写一个目录就会这样。安装方一 import 就是 ERR_MODULE_NOT_FOUND,而本仓 workspace 里同一个 import 一直是好的——它解析到的是源码目录,不是 tarball。
  2. tarball 里混进测试文件。 它们不是 API 的一部分,却出现在安装方的 node_modules 里,占体积,也让人以为可以 import。

改动

.github/scripts/check-publish-contract.js:对 NPM_PACKAGES 里每个可发布包,拿它真实的 npm pack --dry-run 清单核对上面两件事——不是读 files 字段推测。exports 里的 subpath pattern(./shared/*)只要求至少命中一个文件。--ignore-scripts 保证只取清单、不重跑各包的 prepack 构建。

接进 CI(test job 里,排在构建之后、pawl 之前),本地可跑 pnpm run check:publish-contract。它的单元测试落在 .github/scripts/*.test.js,CI 现有的 “Build & test repository tooling” 步骤会自动跑到。

跑出来的结果

第一次跑,5 个包在发测试文件:

测试文件数
devkit 72(编译后的 dist/*.test.js 及其 .d.ts / .map
inspect 19
fs-core 18
view-anchor 7
design 3

入口缺失一项都没有——那半边检查是纯守卫。

files"!" 规则排掉后全绿。devkit 的 tarball 从 131 个文件降到 59 个;dist/watch-rebuild.testutil.* 这类名字里带 test 但不是测试的文件仍然保留(*.test.* 要求 .test. 是完整一段,已验证)。

验证

  • node --test .github/scripts/check-publish-contract.test.js:7 项全过,覆盖入口收集(条件对象 / main / types / bin)、入口缺失被报出、subpath pattern 至少命中一个、pattern 里的点号按字面匹配、测试文件被报出、名字里带 test 的正常文件不误报。
  • node .github/scripts/check-publish-contract.js:改 files 前 exit 1 并列出上表,改完 exit 0。
  • ./scripts/gate.sh 全绿。

顺带一提(本 PR 没动)

packages/devkit/src/project-watcher.test.ts 里 “fires onChange for edits inside a dist directory” 在整仓并发跑满时失败过一次,单独跑两次都是 14/14 过。像是负载下的时序抖动,不是这个 PR 引入的。

两类问题本仓的构建和测试都看不见,只在包发布出去之后才暴露:
exports/main/types/bin 指向的文件没进 tarball(`files` 少写一个目录
就会这样,workspace 里读源码目录所以一直是好的),以及 tarball 里混
进测试文件。

check-publish-contract.js 拿每个可发布包真实的 `npm pack --dry-run`
清单来核对这两件事,而不是读 `files` 字段猜。subpath pattern 只要求
至少命中一个文件。

跑下来 5 个包在发测试文件:devkit 72 个(编译后的 dist/*.test.js
及其 .d.ts/.map)、inspect 19 个、fs-core 18 个、view-anchor 7 个、
design 3 个。用 files 的 "!" 规则排掉,devkit 的 tarball 从 131 个
文件降到 59 个,`watch-rebuild.testutil.*` 这类不是测试的文件仍然
保留。
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 2, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-02T11:47:43.105326Z 9b843ab PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9b843ab6e7

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread .github/workflows/ci.yml Outdated
Comment on lines +180 to +181
- name: Check publish contract
run: node .github/scripts/check-publish-contract.js

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Build every package before checking its tarball

In the inspected test-and-pawl job, a clean checkout reaches this command after pnpm turbo run test, but the test task in turbo.json:43-46 depends only on ^build, which builds workspace dependencies rather than each tested package itself. @dimina-kit/devtools is included in NPM_PACKAGES, no workspace package depends on it, and its dist is untracked, so this command reports all of its entry points as missing and fails CI on every PR. Add an explicit build for every checked package before this step, or otherwise make the package's own build a prerequisite.

AGENTS.md reference: AGENTS.md:L23-L23

Useful? React with 👍 / 👎.

Comment on lines +109 to +110
const pkgJson = JSON.parse(readFileSync(join(process.cwd(), dir, 'package.json'), 'utf8'))
const problems = checkPackedFiles(pkgJson, packedPathsOf(dir))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Validate the manifest that pnpm actually publishes

The release path invokes pnpm publish in .github/scripts/publish-packages.js:60-62, and pnpm replaces manifest fields such as main, types, and exports with their publishConfig values, but this code validates the unmodified source manifest against an npm-generated file list. For design and view-anchor, the source entries point to src while the published entries point to dist; consequently the checker prints success even when dist is entirely absent, allowing exactly the broken published-entry condition this guard is intended to catch. Inspect pnpm's actual packed manifest or overlay the supported publishConfig fields before collecting targets.

AGENTS.md reference: AGENTS.md:L24-L24

Useful? React with 👍 / 👎.

return new RegExp(`^${literals.join('.+')}$`)
}

const TEST_FILE = /(^|\/)(__tests__|__mocks__|fixtures|test-fixtures|types-fixture)\/|\.(test|spec)\.[^/]+$/

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Exclude test-prefixed scripts from published packages

This detector only recognizes test directories and names containing .test. or .spec., so the publishable compiler package's scripts/test-*.js files are not classified as tests. Because packages/compiler/package.json:29-32 publishes the entire scripts directory, an actual npm pack --dry-run still includes 21 regression-test scripts such as test-npm-scan.js and test-pool-hardening.js, leaving one of the artifact leaks this change is intended to eliminate. Extend the classification and the compiler package exclusions to cover these test-prefixed files.

AGENTS.md reference: AGENTS.md:L23-L23

Useful? React with 👍 / 👎.

Comment on lines +40 to +43
export function entryTargets(pkgJson) {
const targets = collectTargets(pkgJson.exports, [])
if (typeof pkgJson.main === 'string') targets.push(normalize(pkgJson.main))
if (typeof pkgJson.types === 'string') targets.push(normalize(pkgJson.types))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Check package imports and TypeScript version mappings

entryTargets collects only exports, main, types, and bin, even though current publishable manifests contain other resolver targets: packages/fs-core/package.json:28-32 maps the runtime import #worker-lib/wal-codec.js, which is used by sync/binary-sidecar.ts, and packages/devtools/package.json uses typesVersions for its public subpath declarations. If any of those mapped files is omitted from a tarball, runtime or TypeScript consumers break while this contract check remains green. Include imports and typesVersions targets in the packed-file validation.

AGENTS.md reference: AGENTS.md:L24-L24

Useful? React with 👍 / 👎.

return JSON.parse(raw.slice(start))[0].files.map((file) => file.path)
}

if (import.meta.url === `file://${process.argv[1]}`) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Run the checker on Windows and URL-escaped paths

When this script is launched on Windows, or from a checkout path containing spaces or other URL-escaped characters, import.meta.url is a canonical file URL while file://${process.argv[1]} is built from an unescaped native path (for example, file:///C:/... versus file://C:\...). The equality is therefore false, so pnpm run check:publish-contract exits successfully without checking any package. Compare fileURLToPath(import.meta.url) with a resolved process.argv[1], or use an equivalent platform-safe main-module check.

Useful? React with 👍 / 👎.

lbb00 and others added 2 commits September 2, 2026 21:30
CI 里这一步是红的:它跑在 `turbo run test` 之后,而 test 只依赖 `^build`,
只构建被别人依赖的包。仓库里没人依赖 @dimina-kit/devtools,它的 dist 一直是
空的,检查就把它声明的 11 个入口全报成"没进 tarball"。这一步现在自己先跑一次
`turbo run build`(本地实测 1 分钟,绝大部分命中 turbo 缓存)。

同时修掉检查本身漏看的三处:

- pnpm publish 会用 publishConfig 里的同名字段覆盖发出去的 package.json。
  design 和 view-anchor 正是这么写的——源码里 main 指向 ./src/index.ts,发布出去
  指向 ./dist/index.js。原来按源码字段核对,恰恰把这两个最需要检查的包放行了。
- imports、typings、typesVersions 指向的文件同样要在 tarball 里,之前没收集。
- test- 打头的脚本也算测试文件(packages/compiler 的 scripts/ 里有 21 个跟着
  发出去了),除非它被声明成入口——故意当 API 发的测试辅助工具照旧放行。

另外把判断"是否被直接运行"的 `file://` 字符串拼接换成路径比较:Windows 的
`file:///C:/…` 和路径里的空格/中文都会让原来的写法对不上,检查会静默不跑。
CI 的 test-and-pawl job 装依赖只装了仓库根。@dimina-kit/devtools 的整包 build 第一步
是 build:container,要构建 dimina/fe 里的容器,而 dimina/fe 是另一个独立的 pnpm
workspace,没装依赖,构建直接报 Cannot find package '@vitejs/plugin-vue'——这一步于是
在每个 PR 上都挂。

devtools 声明出去的 22 个入口全部出自 build:main 和 build:preload,都不碰 dimina/fe。
所以把 devtools 从 turbo 那一轮排除,单独跑这两步;容器和 native-host 不在入口里,
少了它们不影响这个检查要核对的东西。每个包仍然是按真实打包清单严格核对,没有降级。

顺带补上三处漏检:
- browser 字段(字符串形式和替换表)也是入口,之前完全没看。
- test/、tests/、__snapshots__/、fixture/ 目录下的普通文件也算测试文件;之前只认
  __tests__ 和 .test./.spec.,`test/helper.js` 会被发出去。
- publishConfig 里的 files 和 directory 只有 pnpm 认、`npm pack` 不认,出现了就明确
  报错,而不是给出一个悄悄失真的结论。

入口文件不在磁盘上时单独报"没构建",不再刷一串"没进 tarball" 把原因埋掉。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@lbb00

lbb00 commented Sep 3, 2026

Copy link
Copy Markdown
Collaborator Author

合并进 #195,内容一致,关闭这个单独的 PR。

@lbb00 lbb00 closed this Sep 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant