Repository navigation
Commit daf52bc
ci(ui-merge): port the four objectui gates that guard published @object-ui packages into root CI (#26854)
<!-- claude-projects-attribution: {"github_login":"os-steve"} -->
_Requested by **Steve** · [project
thread](https://claude.ai/code/project/chan_01FdL1XeUyvVNxLreh3F4Qi9?thread=cmsg_01FdL1XeUyvVNxLreh3F4Qi9UWsef2B9ahn2NkfVDtnHnS)_
Part of #26717 (the four "port" rows: 23, 21, 7 `test:dist`, 22. The
five port-or-retire rows 19, 18, 7-docs, 17 and 26 are measured below
and left for the next card.)
Clause-②: no
Metadata-protocol: none
## 维护者速读
- 改了什么:把保护已发布 `@object-ui/*` 包的四个 objectui 检查接回根 CI。`Build & E2E UI`
多跑两步:Console 首屏体积预算(`check:eager-closure`)和 components
的构建产物测试(`test:dist`),都复用这个 job 已有的构建。新增一个只在 main 的 push/定时批次跑的 job「UI
Published Artifacts」:先跑一次和发布相同的 `pnpm build`,再跑两个检查,一个查发布包里有没有测试文件,一个查每个
ESM 入口能不能被 Node 直接加载。`scripts/release-publish.sh` 在 `changeset publish`
之前先扫一遍,扫出问题就拒绝发布。
- 为什么改:objectui 并入后,这四个检查没有在任何地方跑过。18.2.0 发 `@object-ui/*`
时也没有经过发布前的那道扫描。
- 风险与回滚:`Build & E2E UI` 是必需检查,多出的两步会挡 PR。这两步在 main 上实测是绿的,首屏体积还有 481 KB
余量。新 job 不是必需检查,PR 和合并队列都不跑它。但发布审批前读的全量批次里会有它,它红了算 main-red。回滚就是 revert
本 PR。
- 先看:`.github/workflows/ci.yml` 末尾新增的 `ui-published-artifacts`
job;`scripts/release-publish.sh`
新增的拒发段;`ui/scripts/__tests__/check-published-dist-tooling.test.ts`
改写的接线测试(这一处要你拍板,见决策卡)。
- 两分钟验证:`node scripts/release-verify-npm.mjs --self-test`,预期 169 例全过,其中第
16 组有 12 例,包括「扫出测试文件就拒绝发布、changeset publish 不会运行」。
- 需要你做的:批准本 PR(Tier H),并在线程里回答行 23 的位置问题(推荐 B,本 PR 就是按 B 做的)。
## What changes
Rows of the objectstack#25697 workflow roster, ported from the objectui
workflows deleted by #26058. Invocations were read at that PR's parent
`be789779` (`published-dist-gate.yml`, `node-esm-load-gate.yml`,
`performance-budget.yml`, `ci.yml` "Test (dist pins)").
| Row | Gate | Where it runs now | Step name |
|---|---|---|---|
| 22 | `check:eager-closure` | `Build & E2E UI`, over the Console build
the job already makes. On a pull request, a step before it resolves the
base branch's ceiling constants (objectui#6245 freshness half), ported
from `performance-budget.yml`. | `check:eager-closure — resolve the
base-branch ceiling constants`, `check:eager-closure — weigh the
Console's eager closure against its budget` |
| 7 | `test:dist` on `@object-ui/components` | `Build & E2E UI`. The
root turbo task's `dependsOn: ["build"]` replays the registry build the
SDUI manifest step already ran. | `test:dist — run the built-artifact
pins of @object-ui/components` |
| 23 | `check:published-dist` | New full-battery job `UI Published
Artifacts`, after `pnpm build`, with `--no-build`. Also
`scripts/release-publish.sh`, before npm's record starts, refusing on a
non-zero exit. | `check:published-dist — scan every published @object-ui
tarball for tooling files` |
| 21 | `check:node-esm-load` (load leg) | Same job, same build,
`--no-build`. It runs whatever the scan above said. |
`check:node-esm-load — import every published ESM entry under plain
Node` |
- **`ui-published-artifacts`** (`UI Published Artifacts`): `if:` is
Build & E2E UI's condition plus `github.event_name != 'pull_request' &&
github.event_name != 'merge_group'`, the full-battery spelling. So a
pull request and a queue entry skip it, `main`'s push and 4-hourly
schedule runs carry it, and `scheduled-full-run.mjs --release-gate`
reads it before a publish approval. It is not a required context. Its
setup is spelled out (Node, pnpm, a restore-only store cache, `pnpm
install --frozen-lockfile`) rather than the `setup-workspace` composite,
because `check-pre-install-import-graph.mjs` reads a job with no literal
`pnpm install` as running entirely before `node_modules` exists, and
both gates import `typescript`. It restores the build-core / ui-deps
turbo seeds (restore-only). `timeout-minutes: 60`, objectui's bound for
each of the two builds it replaces.
- **Why `pnpm build` + `--no-build`.** The gate's own build mode is not
the release's build. `check:published-dist` builds `turbo run build
--filter=!@object-ui/site` from `ui/`, which under the monorepo is 128
tasks including `@objectstack/docs`; the release builds `pnpm build` (no
docs). `check:node-esm-load`'s build mode runs `pnpm build` in `ui/`,
which has no `build` script (measured:
`ERR_PNPM_RECURSIVE_EXEC_FIRST_FAIL Command "build" not found`, exit
254). Building once with the release's command and grading with
`--no-build` is the order the publish path runs the scan in.
`--no-build` cannot pass vacuously: a package with nothing under its
build output is `no-build-output`, both gates assert a floor, and the
ESM gate's provenance leg still refuses an artifact whose turbo log
names another tree.
- **`scripts/release-publish.sh`** runs `node
ui/scripts/check-published-dist-tooling.mjs --no-build` after the
`npm_config_globalconfig` unset and before `--start-npm-record`, so the
scan's `npm pack --dry-run` runs never land in the record
`release-verify-npm.mjs` reads back. A non-zero exit prints an
`::error::` and exits 1 before `changeset publish`.
- **THE TWO SPEEDS** header in `ci.yml` now names four full-battery
jobs.
### Forced test edits
- `scripts/release-verify-npm.mjs` battery 16 (floor 8 → 12). Its run of
`release-publish.sh` would otherwise run the real scan over an unbuilt
tree. It now stubs `npm` on `PATH` (answering `npm pack --dry-run
--json` with one build-output file, plus a `dist/__tests__/*.test.js`
when asked; the stub writes a debug log into `npm_config_logs_dir` like
real npm). New cases: the scan packs every published package before
`changeset`; npm's record holds only the publish's log; a tarball with
tooling material exits 1 with `Refusing to publish`; and then
`changeset` and `git` are never called. The existing call-order case
filters out the `npm pack` lines.
- `ui/scripts/__tests__/check-published-dist-tooling.test.ts` (own
commit, option B on the thread's decision card). The pin `is NOT wired
into any per-PR workflow` read the ruling (objectui#4846 comment
5307574139: no per-PR full-repo build) per workflow FILE, so no job of
`ci.yml` (which subscribes `pull_request`) could carry the scan. It now
reads per JOB: in any workflow that subscribes `pull_request` or
`merge_group`, a job that runs the scan must deny both events in its own
`if:`, and the loop must find at least one carrier. The retired `runs
nightly` pin comes back as `runs in ci.yml's full battery, after the
release's build, with --no-build`. If option A or C is chosen instead,
this commit is dropped and the job moves or loses its first step.
## Why
The roster kept these four as "kept, follow-up port", and #26058 deleted
their only runners. Since then nothing has run them: 18.2.0 published
`@object-ui/*` without the published-dist scan, and a Console first-load
regression would land unmeasured. Card #26717 has the business case.
## Tests
Measured on head `f0665932` (the final head `8090d1fa` differs on this
PR's paths only in the new job's setup steps, spelled out instead of the
composite; the gate commands, scripts and `release-publish.sh` are
unchanged) in this worktree, after a cold `pnpm build` (116 tasks,
13m50s under `os-verify-lock`, shared box):
<details><summary>The four gates as the CI steps spell them (all exit
0)</summary>
- `pnpm --dir ui run check:published-dist --no-build`: exit 0, 241 s.
`Inspected 36 published package(s): 5799 tarball file(s), 5654 of them
build output, 0 tooling artifact(s) in build output.`
- `pnpm --dir ui run check:node-esm-load --no-build`: exit 0, 81 s.
`Provenance leg: 35 of 35 gradable entries were built by this tree.`
`Load leg: 33 of 36 published ESM entries imported and evaluated.`
(create-plugin is bin-only; plugin-dashboard and plugin-map are the
ruled `.css` boundary.)
- `VITE_BASE_PATH=/console/ pnpm --dir ui/apps/console exec vite build`,
then `pnpm --dir ui run check:eager-closure`: exit 0. `Console eager
closure is 1950.2 KB gzipped across 89 of 2392 chunks (budget: 2431.6
KB, headroom: 481.4 KB).` Per-chunk, membership and sensitivity halves
all ✅; freshness not applicable off a pull request.
- `pnpm exec turbo run test:dist --filter=@object-ui/components`: exit
0, `Test Files 2 passed (2)`, `Tests 8 passed (8)`.
- PR freshness step rehearsed: its `run:` extracted from this `ci.yml`,
run under `bash -e` with `PR_BASE_SHA=8b276385`, `BASE_REF=main`: exit
0, then the checker with `GITHUB_EVENT_NAME=pull_request` and those five
variables: exit 0, `Ceiling freshness: all 3 ceiling constants are
unchanged on main since this checkout's base`.
- `node scripts/release-verify-npm.mjs --self-test`: `OK … 169 cases
pass across 16 batteries`.
- `pnpm --dir ui exec vitest run
scripts/__tests__/check-published-dist-tooling.test.ts`: 33 passed.
</details>
<details><summary>Counter-probes (each defect turned its step red, then
was restored)</summary>
One locked script, every mutation restored by trap and checked by sha256
or `git diff HEAD`:
- Row 23: `ui/packages/core/dist/probe-26717.test.d.ts` written into a
built dist. `check:published-dist --no-build` exit 1: `@object-ui/core
[tooling-in-published-output] packages/core/dist/probe-26717.test.d.ts`.
The release-path spelling `node
ui/scripts/check-published-dist-tooling.mjs --no-build` exit 1 too.
Restored (file absent). Battery 16's tooling case is the same refusal
through `release-publish.sh`.
- Row 21: `export * from './probe26717extless'` appended to built
`ui/packages/react/dist/index.js`. `check:node-esm-load --no-build` exit
1: `✗ @object-ui/components (via @object-ui/react): ERR_MODULE_NOT_FOUND
…/react/dist/probe26717extless`, 21 lines naming it. Restored, sha
matches.
- Row 22: `eager-closure.json` grown by 700,000 gzip bytes.
`check:eager-closure` exit 1: `❌ Console eager closure is 2633.8 KB
gzipped across 89 chunks — 202.2 KB over the 2431.6 KB budget.`
Restored, sha matches; re-run exit 0.
- Row 7: through `scripts/ablation-replace.mjs`, `namespace: 'page'` →
`namespace: 'probe26717'` on `page:header`'s registration in
`ui/packages/components/src/renderers/layout/containers.tsx`. `turbo run
test:dist --filter=@object-ui/components` rebuilt the package and exited
1: `× registers page:header from the BUILT bundle`. Restored (`git diff
HEAD` empty); re-run exit 0.
- Release path, mutation-free: battery 16's `a tarball carrying tooling
material fails the script` and `changeset publish never runs` cases.
CI run ids per gate go on the card once a `main` push run and this PR's
run exist: this PR's run covers the two `Build & E2E UI` steps; `UI
Published Artifacts` first runs on the push that lands this.
</details>
## Acceptance notes
- **The five rows left for the next card**, measured on `origin/main`
`8b276385`: no root workflow runs any of them.
- Row 19 `lockfile-integrity` (`check:lockfile-integrity`): root
`validate-deps.yml` has no duplicate-dependency step (its steps are
changeset-fixed, override-consistency, vendor-export-contract-resolve,
OSV and outdated), so no overlap to retire it on. One ui test reads the
script.
- Row 18 `lockfile-dedupe` (`check:lockfile-dedupe`): no root workflow
runs `pnpm dedupe --check`; #25920 measured 145 runs / 90 days, 0
failures. One ui test reads it.
- Row 7-docs `@object-ui/site`: no root workflow builds it, and root
`pnpm build` excludes `./ui*/apps/site`.
- Row 17 `live-e2e` (`test:e2e:live:ci`): no root carrier; one ui test
reads `playwright.live.config`.
- Row 26 `shadcn-check` (`shadcn:check`): no root carrier; three ui
tests read `shadcn-sync`.
- **The rest of objectui's budget step is not ported.** It also held the
entry chunk to 350 KB gzipped, ran `check-eager-locale-catalogues.mjs`
(alias `check:eager-locale-catalogues`), `check:sdui-registration-pins`,
a package size report and a PR comment. None of them runs in root CI
today. The card names `check:eager-closure` only; the rest is next-card
material.
- **Found, not fixed here:** `pnpm --dir ui run check:node-esm-load`
without `--no-build` exits 1 in the monorepo (`ui/package.json` has no
`build` script). And `check:published-dist`'s own build mode builds
`@objectstack/docs`, unlike the release it claims to match. Both are ui
script defects; this PR routes around neither: CI and the release call
the gates with `--no-build` after the release's own build. Fixing the
scripts' build modes is a ui-lane card (and `ui/package.json` is held by
open #26556).
- `release.yml`'s comment over the publish step still describes
`release-publish.sh` as `changeset publish` followed by one tag push; it
now runs the scan first. Not edited here (outside the claimed surface).
- The `check-node-esm-load.mjs` docblock says `--no-build` "is wired
into nothing"; this PR wires it. Not edited (ui script, outside the
surface).
- No new required context. `Build & E2E UI` (required) gains three
steps, about 1 to 2 minutes on top of its measured 312 s run (schedule
run 40537439467) against its 35-minute bound.
## Verification
Final head `8090d1fa` (the branch with `origin/main` 0ea90c7 merged in;
no overlapping files). After a fresh `pnpm build` (13m22s), the sweep
ran every command `node scripts/pm/dispatch-gates.mjs --commands`
derives for this change set (4 paths) under `os-verify-lock` (VERDICT
command-exit 0, 44m35s).
<details><summary>Gate sweep and reconciliation</summary>
- 71 derived commands, 71 run, all `exit 0`. Among them: `pnpm --dir ui
exec vitest run scripts/` (178 files, 5066 tests passed, 2 skipped),
`node scripts/release-verify-npm.mjs --self-test` (169 cases across 16
batteries), `pnpm check:published-files`, and `check:pm-dispatch-gates`
(fast tier, 1935 cases).
- `node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack
--ran <ledger>`: `✓ 71 derived famil(ies) accounted for — 71 run, 0
NOT-MEASURED`, 0 UNRUN.
- Not run locally: `pnpm lint` and the full test suites, which are CI's
run.
</details>
## Human Minimum
- [x] 1. Schemas start as Zod; `packages/spec` holds no runtime logic.
(not touched)
- [x] 2. Every name I added uses the casing for its kind.
- [ ] 3. No formatter ran; `pnpm lint` passes. (no formatter ran; `pnpm
lint` is CI's run)
- [x] 4. One issue, one branch off an up-to-date `main`, one pull
request.
- [x] 5. A change that publishes carries a changeset. (nothing
publishes: workflow, root scripts and a ui test; `skip-changeset`)
- [x] 6. `packages/spec` touched: n/a.
- [x] 7. No hand edits to `content/docs/references/`,
`packages/*/CHANGELOG.md` or `content/docs/releases/`.
- [x] 8. Title, body and comments are in English (the 维护者速读 block is the
repo's Chinese brief).
- [ ] The *Before Submitting* list in CONTRIBUTING.md is done. (the
local subset per the dispatch contract; the full suite is CI's)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
https://claude.ai/code/session_01ADHkmNiJfZmoXAbfcJSAfx
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent d7dd47a commit daf52bc
4 files changed
Lines changed: 264 additions & 19 deletions
File tree
- .github/workflows
- scripts
- ui/scripts/__tests__
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
117 | 117 | | |
118 | 118 | | |
119 | 119 | | |
120 | | - | |
| 120 | + | |
121 | 121 | | |
122 | 122 | | |
123 | 123 | | |
124 | | - | |
125 | | - | |
126 | | - | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
127 | 127 | | |
128 | 128 | | |
129 | 129 | | |
| |||
4237 | 4237 | | |
4238 | 4238 | | |
4239 | 4239 | | |
| 4240 | + | |
| 4241 | + | |
| 4242 | + | |
| 4243 | + | |
| 4244 | + | |
| 4245 | + | |
| 4246 | + | |
| 4247 | + | |
| 4248 | + | |
| 4249 | + | |
| 4250 | + | |
| 4251 | + | |
| 4252 | + | |
| 4253 | + | |
| 4254 | + | |
| 4255 | + | |
| 4256 | + | |
| 4257 | + | |
| 4258 | + | |
| 4259 | + | |
| 4260 | + | |
| 4261 | + | |
| 4262 | + | |
| 4263 | + | |
| 4264 | + | |
| 4265 | + | |
| 4266 | + | |
| 4267 | + | |
| 4268 | + | |
| 4269 | + | |
| 4270 | + | |
| 4271 | + | |
| 4272 | + | |
| 4273 | + | |
| 4274 | + | |
| 4275 | + | |
| 4276 | + | |
| 4277 | + | |
| 4278 | + | |
| 4279 | + | |
| 4280 | + | |
| 4281 | + | |
| 4282 | + | |
| 4283 | + | |
| 4284 | + | |
| 4285 | + | |
| 4286 | + | |
| 4287 | + | |
| 4288 | + | |
| 4289 | + | |
| 4290 | + | |
| 4291 | + | |
| 4292 | + | |
| 4293 | + | |
| 4294 | + | |
| 4295 | + | |
| 4296 | + | |
| 4297 | + | |
| 4298 | + | |
| 4299 | + | |
| 4300 | + | |
| 4301 | + | |
| 4302 | + | |
| 4303 | + | |
| 4304 | + | |
| 4305 | + | |
| 4306 | + | |
| 4307 | + | |
4240 | 4308 | | |
4241 | 4309 | | |
4242 | 4310 | | |
| |||
4289 | 4357 | | |
4290 | 4358 | | |
4291 | 4359 | | |
| 4360 | + | |
| 4361 | + | |
| 4362 | + | |
| 4363 | + | |
| 4364 | + | |
| 4365 | + | |
| 4366 | + | |
| 4367 | + | |
| 4368 | + | |
| 4369 | + | |
| 4370 | + | |
| 4371 | + | |
| 4372 | + | |
| 4373 | + | |
| 4374 | + | |
| 4375 | + | |
| 4376 | + | |
| 4377 | + | |
| 4378 | + | |
| 4379 | + | |
| 4380 | + | |
| 4381 | + | |
| 4382 | + | |
| 4383 | + | |
| 4384 | + | |
| 4385 | + | |
| 4386 | + | |
| 4387 | + | |
| 4388 | + | |
| 4389 | + | |
| 4390 | + | |
| 4391 | + | |
| 4392 | + | |
| 4393 | + | |
| 4394 | + | |
| 4395 | + | |
| 4396 | + | |
| 4397 | + | |
| 4398 | + | |
| 4399 | + | |
| 4400 | + | |
| 4401 | + | |
| 4402 | + | |
| 4403 | + | |
| 4404 | + | |
| 4405 | + | |
| 4406 | + | |
| 4407 | + | |
| 4408 | + | |
| 4409 | + | |
| 4410 | + | |
| 4411 | + | |
| 4412 | + | |
| 4413 | + | |
| 4414 | + | |
| 4415 | + | |
| 4416 | + | |
| 4417 | + | |
| 4418 | + | |
| 4419 | + | |
| 4420 | + | |
| 4421 | + | |
| 4422 | + | |
| 4423 | + | |
| 4424 | + | |
| 4425 | + | |
| 4426 | + | |
| 4427 | + | |
| 4428 | + | |
| 4429 | + | |
| 4430 | + | |
| 4431 | + | |
| 4432 | + | |
| 4433 | + | |
| 4434 | + | |
| 4435 | + | |
| 4436 | + | |
| 4437 | + | |
| 4438 | + | |
| 4439 | + | |
| 4440 | + | |
| 4441 | + | |
| 4442 | + | |
| 4443 | + | |
| 4444 | + | |
| 4445 | + | |
| 4446 | + | |
| 4447 | + | |
| 4448 | + | |
| 4449 | + | |
| 4450 | + | |
| 4451 | + | |
| 4452 | + | |
| 4453 | + | |
| 4454 | + | |
| 4455 | + | |
| 4456 | + | |
| 4457 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
60 | 60 | | |
61 | 61 | | |
62 | 62 | | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
63 | 78 | | |
64 | 79 | | |
65 | 80 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
317 | 317 | | |
318 | 318 | | |
319 | 319 | | |
320 | | - | |
| 320 | + | |
321 | 321 | | |
322 | 322 | | |
323 | 323 | | |
| |||
3014 | 3014 | | |
3015 | 3015 | | |
3016 | 3016 | | |
3017 | | - | |
| 3017 | + | |
3018 | 3018 | | |
3019 | | - | |
| 3019 | + | |
| 3020 | + | |
| 3021 | + | |
| 3022 | + | |
| 3023 | + | |
3020 | 3024 | | |
3021 | 3025 | | |
3022 | 3026 | | |
| |||
3030 | 3034 | | |
3031 | 3035 | | |
3032 | 3036 | | |
| 3037 | + | |
| 3038 | + | |
| 3039 | + | |
| 3040 | + | |
| 3041 | + | |
| 3042 | + | |
| 3043 | + | |
| 3044 | + | |
| 3045 | + | |
| 3046 | + | |
| 3047 | + | |
| 3048 | + | |
3033 | 3049 | | |
3034 | 3050 | | |
| 3051 | + | |
3035 | 3052 | | |
3036 | | - | |
| 3053 | + | |
3037 | 3054 | | |
3038 | 3055 | | |
3039 | 3056 | | |
| |||
3049 | 3066 | | |
3050 | 3067 | | |
3051 | 3068 | | |
| 3069 | + | |
3052 | 3070 | | |
3053 | 3071 | | |
3054 | 3072 | | |
| |||
3059 | 3077 | | |
3060 | 3078 | | |
3061 | 3079 | | |
| 3080 | + | |
3062 | 3081 | | |
3063 | 3082 | | |
3064 | 3083 | | |
| |||
3073 | 3092 | | |
3074 | 3093 | | |
3075 | 3094 | | |
3076 | | - | |
| 3095 | + | |
| 3096 | + | |
| 3097 | + | |
| 3098 | + | |
| 3099 | + | |
| 3100 | + | |
| 3101 | + | |
| 3102 | + | |
| 3103 | + | |
3077 | 3104 | | |
3078 | 3105 | | |
3079 | 3106 | | |
3080 | 3107 | | |
| 3108 | + | |
| 3109 | + | |
| 3110 | + | |
| 3111 | + | |
| 3112 | + | |
3081 | 3113 | | |
3082 | 3114 | | |
3083 | 3115 | | |
| |||
0 commit comments