Skip to content

ci: one setup-workspace composite action replaces the setup steps copied into 38 jobs, and pull requests stop saving the pnpm store - #26419

Merged
os-zhuang merged 1 commit into
mainfrom
claude/project-thread-x57bin
Oct 27, 2026
Merged

os-zhuang merged 1 commit into
mainfrom
claude/project-thread-x57bin

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Steve · project thread
Fixes #26083

Clause-②: no
Metadata-protocol: none

维护者速读

  • 改了什么:新建 .github/actions/setup-workspace 组合 action(Node 读 .nvmrc、pnpm、store 缓存、pnpm install --frozen-lockfile),38 个 job 手抄的 setup 串换成它一步;只装 Node 的 job 改读 .nvmrc。
  • 为什么改:同一串五步手抄在约 40 个 job 里,改一次要改 40 处;pnpm store 缓存在 pull_request 上也会存,挤占 10 GB 缓存池、把 main 的 Turbo 种子挤掉。现在只有 push 和 schedule 存。
  • 风险与回滚:纯 CI 配置,不发布任何包;出问题整 PR revert 即可。风险在 store 改为 push/schedule 才存后,PR 首跑可能更常冷装(读 main 的 store 不受影响)。
  • 先看:.github/actions/setup-workspace/action.yml(保存条件那一步)、.github/workflows/release.yml 的 publish job(组合 action 放在 Guard 之后)、.github/workflows/ci.yml 的 ui-deps 保存条件。
  • 两分钟验证:在本 PR 的 CI 任一 job 日志里展开 "Setup workspace",应看到 "Save pnpm store" 被跳过;grep -c "node-version: '22'" .github/workflows/*.yml 只剩 scaffold-e2e.yml 一处。
  • 需要你做的:受管面(.github/workflows/**),请 GOVERNED_APPROVERS 审批后由席位落地。

What changes

  • New composite .github/actions/setup-workspace/action.yml. actions/setup-node with node-version-file: .nvmrc (and package-manager-cache: false, since pnpm is not on PATH yet), then ./.github/actions/setup-pnpm as a step, pnpm store path, actions/cache/restore on the one store key, pnpm install --frozen-lockfile, and actions/cache/save only when github.event_name is push or schedule and the exact key missed (continue-on-error: true). Inputs: install: 'false' (Node and pnpm only, for the jobs that run pnpm but install nothing), install-args (validate-deps keeps --prefer-offline), save-store: 'false' (the jobs that were deliberately restore-only stay so).
  • 38 jobs call it in place of the hand-copied steps, showcase-smoke.yml included: its pnpm/action-setup and setup-node cache: pnpm are gone, so the acquisition census now reads 0 uncached sites. In release.yml's publish job the composite sits where Setup pnpm was, after the Guard step, so nothing is installed before the Guard has passed; that job keeps its own setup-node step (now .nvmrc).
  • Jobs that need Node and nothing else keep their own setup-node step, now node-version-file: .nvmrc.
  • Kept spelled out, each with its reason written beside it:
    • ci.yml's five ui jobs (ui-deps, type-check-ui, test-ui, lint-ui, build-e2e-ui). Pins under ui/scripts/__tests__/ read those steps by text and by name (see Acceptance notes); this PR does not touch ui/**. They switch to node-version-file: .nvmrc, and ui-deps' Save pnpm cache now also requires push or schedule.
    • governed-surface-guard.yml: every toolchain step is gated on the toolchain question and degrades on its own, pinned one by one by check-governed-queue-guard.mjs --self-test. Its store step becomes actions/cache/restore (the workflow runs on pull_request and merge_group only, so it never saves now), and its Node pin reads .nvmrc.
    • release.yml release-integrity: installs the version commit's tree, not the checkout.
    • scaffold-e2e.yml registry-canary: checks nothing out, so there is no .nvmrc to read; the one literal node-version: '22' left, still held to .nvmrc by check:node-version.
  • setup-pnpm is not retired: it becomes a step of the composite, because the ui pins and the two jobs above still call it by path.
  • Gates that read these steps by text: scripts/check-pnpm-acquisition.mjs recognises uses: ./.github/actions/setup-workspace as a cached mechanism (MECHANISMS row, header spelling list, two new self-test cases: recognised, and late acquisition still fails; battery floor 12 to 14). scripts/pm/check-governed-queue-guard.mjs's install-chain pin matches actions/cache/restore@ as well as actions/cache@. No other behaviour changes in either.
  • .github/dependabot.yml: the github-actions entry reads directories: ["/", "/.github/actions/*"].
  • TURBO_* env lines are unchanged and stay per step: hoisting them to job level would hand TURBO_TOKEN to every step of the job. fetch-depth, the Turbo seed keys, the Next.js cache and every job name are unchanged.

Tests

The diff edits two gate scripts, so each one's own self-test ran, plus the derived family list (Verification).

  • node scripts/check-pnpm-acquisition.mjs --self-test: 14 cases pass; pnpm check:pnpm-acquisition: OK, census 45 acquisition site(s), 0 of them uncached (38 workspace, 7 setup-pnpm, 0 action-setup).
  • node scripts/pm/check-governed-queue-guard.mjs --self-test: 393 cases pass, exit 0.

Acceptance notes

Counts re-taken on origin/main at c51aed4 before the edit and on this head after (.github/workflows/*.yml):

reading main this PR
node-version: '22' 60 (59 steps + 1 comment) 2 (registry-canary + the same comment in half-state-patrol.yml)
store key pnpm-store-v3-${{ hashFiles(...) }} 40 7 (the ui jobs' 6 + governed-surface-guard), plus the composite's restore/save pair
pnpm install --frozen-lockfile 43 lines 7 (5 ui jobs, governed-surface-guard, release-integrity's version-tree script), plus the composite
uses: ./.github/actions/setup-workspace 0 38

The ui pins that hold the five ui jobs' steps in place (for the ui lane to move onto the composite later; none edited here):

  • ui/scripts/__tests__/ci-setup-pnpm-wiring.test.ts: each ui job must contain the text uses: ./.github/actions/setup-pnpm before its first pnpm.
  • ui/scripts/__tests__/turbo-cache-save-bound.test.ts: ui-deps saves the store through its own bounded actions/cache/save step paired with a restore that has an id.
  • pnpm install --frozen-lockfile located by text in the job block: check-action-forward-parity, check-i18n-call-site-keys, check-i18n-designer-table-parity, check-i18n-en-drift, check-package-self-import, check-phantom-dependencies, check-unreferenced-sources, check-vi-mock-override-shape (all ui/scripts/__tests__/*.test.ts).

The second Acceptance item (no pnpm-store-* save on a pull_request run) is read from this PR's own CI run and reported on the card.

Verification

Gate families derived by node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack at the head below, each exit captured before any pipe, reconciled with --ran.

Head 2cb5ee1f, merge base c51aed4f.

  • dispatch-gates --ran: 94 derived famil(ies) accounted for — 94 run, 0 NOT-MEASURED (a DERIVED zero — all 94 recorded an exit code and none of them is 3). All 94 exit 0.
  • Four families first answered exit 3 (no built packages in a fresh container: check:dts-closure, check:dual-build-cjs-loads, check:lean-entry-closure, check:sourcemap-no-sources-content) and check:type-check-debt hit the loop's 300 s cap while it built. After turbo run build --filter='./packages/**' under os-verify-lock.sh (VERDICT command-exit 0, 75/75 tasks) all five re-ran to exit 0.
  • pnpm check:pm-dispatch-gates: 1901 cases pass (fast tier), exit 0.
  • pnpm check:node-version: OK (23 setup-node step(s) across 39 workflow(s) and 3 composite action(s), all on Node 22).
  • Not derived, run because the diff edits root ci.yml's ui jobs: every ui/scripts/__tests__/*.test.ts that reads the root workflows (41 files, vitest run --project unit): Test Files 41 passed (41), Tests 1284 passed (1284).
  • Not run locally: the repo-wide scans CI owns (pnpm lint and the rest of Lint & Repo Gates); this PR's check-runs are their reading.

Generated by Claude Code

…ence

.github/actions/setup-workspace sets Node up from .nvmrc, acquires pnpm
through setup-pnpm, restores the pnpm store, runs pnpm install
--frozen-lockfile and saves the store on push and schedule runs only, so no
pull_request or merge_group run writes a pnpm-store entry into the cache
pool. 38 jobs call it; the node-only jobs read .nvmrc directly.

Kept spelled out, each with the reason beside it: ci.yml's five ui jobs
(pinned by name and text under ui/scripts/__tests__), the
governed-surface-guard toolchain (pinned step by step by the queue guard's
self-test; now restore-only), release.yml's release-integrity install of
the version tree, and scaffold-e2e's registry canary (no checkout, so no
.nvmrc). ui-deps saves the store on push and schedule only.

check-pnpm-acquisition recognises the composite (two self-test cases);
the queue guard's install-chain pin reads actions/cache/restore too.
Dependabot reads .github/actions/* as well as the root.

Claude-Session: https://claude.ai/code/session_01ADHkmQLxLez3gFZk5XQ3JX
Co-authored-by: Claude <noreply@anthropic.com>
@objectstack-fleet objectstack-fleet Bot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Oct 27, 2026
@os-zhuang
os-zhuang marked this pull request as ready for review October 27, 2026 03:10
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

Nothing in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 0 changed package(s)), so this run has no opinion about the docs.

What this run could not see

Coarse fallback — 0 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 5507225e3d4ea9c77de80faf6fd613b1666bcfa3 → packageMentionDocs.

@os-zhuang
os-zhuang enabled auto-merge October 27, 2026 03:11
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

席位核对 · ACCEPT · devx seat 2 · 2026-10-27T03:11Z

Seat domain:devx#2 (#20163) · session_01Q4MDXRbEJCNdPAP4SiXwp9 · reviewed head 2cb5ee1f · card #26083, claim 6432280359 (wave 9, delta K; surface amended twice at the thread's premise read). Tier H (check-governed-merges: governed, tier H, humanMerge); +335/−892 over 34 files, one PR, under the 3000-line line. Draft until a GOVERNED_APPROVERS approval.

  • The composite (.github/actions/setup-workspace): Node from .nvmrc with package-manager-cache: false (pnpm is not on PATH yet), pnpm through the existing setup-pnpm, the store key spelled once and unchanged (-v3- + lockfile hash, so main's live entries stay valid), actions/cache/restore always, and a main-phase actions/cache/save only when the event is push or schedule and the exact key missed, continue-on-error. No pull_request or merge_group run can save the store, which is the half of ci: Turbo seeds vs the 10 GB Actions cache pool — measure the pool, then shrink or retire the per-sha seeds #25452's directive (c) this card took. install: 'false' and save-store: 'false' cover the jobs that only need pnpm or deliberately restore only (test-plan and the nightlies).
  • 38 call sites; the exceptions are named, each with its reason: the five ui jobs in ci.yml (pinned by ui/scripts/__tests__/**, which this lane cannot edit; only their Node source changed), governed-surface-guard.yml (its steps are pinned one by one by check-governed-queue-guard --self-test; now restore-only), release.yml's release-integrity (installs the version commit's tree, not the checkout), and scaffold's registry canary (no checkout, so no .nvmrc). Counts on this head match the PR body's table: node-version: '22' 60 → 2 (one is a comment).
  • release.yml publish: the composite runs after the Guard steps, so nothing is installed before the guard decides; its own setup-node earlier in the job is kept and now reads .nvmrc. No step order before the Guard changed.
  • Gates taught, not weakened: check-pnpm-acquisition recognises the composite as a cache-backed acquisition and still fails a job that calls it after its first pnpm (two new self-test cases, floor 12 → 14); check-governed-queue-guard's install-chain regex also matches actions/cache/restore@. dependabot.yml now watches /.github/actions/*. TURBO_* env not hoisted (token exposure), agreed. fetch-depth, Turbo seed keys and the Next.js cache are untouched.

Still owed before landing: green CI on this head, and the os-dev-report's reading from this PR's own run that no pnpm-store-* entry was saved. After merge, the seat asks the ui lane for a card to move the five ui jobs onto the composite together with their ui/ pins (list in the PR body's Acceptance notes).

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

devx seat 2 · ⛔ taken out of the merge queue (converted to draft), not rejected. @os-zhuang's approval stands and nothing on the branch changed.

Test UI (8/16) is red on this head (2cb5ee1f, job 121986631165): ui/scripts/__tests__/doc-version-claims.test.ts L2893 and L2917 read the node-version: lines under .github/workflows as the anchor for the docs' Node claims and require at least 15; this PR leaves 1 (the rest now come from .nvmrc through the composite). The Test UI shards are not in the required set, so the queue would have merged it, and that test would then be red on main for every run that selects it.

The fix is in ui/**, which this lane cannot edit: the test should read .nvmrc (or the composite's node-version-file) as the anchor. The seat is asking the ui lane for that card now, with the full list of ui/ pins this PR meets. When it lands, the card thread merges main and this PR goes back to ready.


Generated by Claude Code

@github-actions

Copy link
Copy Markdown
Contributor

⛔ merge queue 构建失败 — 先分诊,再决定要不要重排

队列构建 40443632873 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集),
所以失败的测试可能在本 PR 没碰过的包里 —— 那不是重排能修的。每次盲目重排都会让排在后面的所有 PR 重建一轮。

分类:failure —— 按下面的日志分诊。

失败的 job(日志抽取,best effort):

  • Test UI (8/16) — 失败步骤: Run the ui tests (shard 8/16)

    × pins every workflow-anchored claim to the version .github/workflows declares 12ms
    × reads every declaration the tree contains, so the anchor cannot shrink unnoticed 44ms
    FAIL   unit  scripts/__tests__/doc-version-claims.test.ts > doc version claims - the workflow-version assertion > pins every workflow-anchored claim to the version .github/workflows declares
      ↳ 失败原因: AssertionError: implausibly few node-version declarations were read from ../.github/workflows - the anchor is thinner than the claims it backs, or the parser stopped reading them: expected 1 to be gre
    FAIL   unit  scripts/__tests__/doc-version-claims.test.ts > doc version claims - the workflow-version assertion > reads every declaration the tree contains, so the anchor cannot shrink unnoticed
      ↳ 失败原因: AssertionError: control for the agreement above: no node-version line was found in .github/workflows at all, so two empty lists just agreed with each other: expected 1 to be greater than or equal to 1
    

↳ 失败原因 是判读的关键:超时(Test timed out in … / Hook timed out in …)多半是负载/时序,不是本 PR 的回归;
断言(AssertionError: …)才指向真实的行为改变。两者的 FAIL 行长得一模一样,只有这一行能区分。

⚠️ 断言这一侧有一类例外,判据是断言在测什么,不是它是不是 AssertionError。 断言的对象是产品行为(一个值、一个形状、一次拒收)⇒ 照上面读:真实的行为改变,去查,⛔ 不要重排掉;
断言的对象是这次实验自身的有效性前提(跑完的耗时、负载下的先后、任何只在时间预算内才成立的条件)⇒ 它跟超时是同一类,同样对负载敏感,重排一次是合法的判别手段。
识别是机械的:断言的消息或它比较的值本身点名了一段时长、一个时间戳、一个耗时计数。实测过的一对 —— AssertionError: SecurityPlugin.init() ran: expected false to be true 测的是产品行为(真回归);
AssertionError: this run took over a second, so second-precision stamps could have differed too: expected 1006 to be less than 1000 测的是实验前提:它守护的那条不变式当时是绿的,同一个 head 原样重排一次即成功。
穿着 AssertionError 外衣的时间测量,仍然是时间测量。(⛔ 这只改「怎么读一次红」,不改「哪些测试可以重排」——后者由别处管。)

跨 PR 相同签名(24h,按失败测试文件聚合):

  • scripts/__tests__/doc-version-claims.test.ts — 24h 窗口内只有本 PR 撞到过,暂不汇总(再有一个不同 PR 撞到就会自动开汇总 issue)。
  • ⚠️ 24h 评论账本没读完(超过 5 页仍未读到窗口尽头),所以上面的「不同 PR 数」是下界,不是全量。

历史信号:

  • 本 PR 过去 24h 无队列失败记录(首次)。
  • 过去 24h 队列共有 9 个失败构建(不含本次)。

分诊清单:

  1. 失败测试在本 PR 改动的包里 → 真回归,修 PR。
  2. 失败测试与本 PR 无关 → 看上面的「跨 PR 相同签名」;已有汇总 issue ⇒ flaky/环境问题实锤,去那张 issue 上谈,修好前重排只会再烧一轮全队列。
  3. 两者都不是 → 可能与同组 PR 语义冲突;等前面的 PR 落地或失败出队后再重排一次即可,不要连续重排。

Generated by Claude Code · merge-queue-triage workflow (#4859)

Merged via the queue into main with commit d15f99d Oct 27, 2026
77 of 78 checks passed
@os-zhuang
os-zhuang deleted the claude/project-thread-x57bin branch October 27, 2026 03:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci/cd size/xl skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

3 participants