diff --git a/.agents/notes/implemented/process/2026-07-26-pnpm-action-setup-for-symmetric-ci-caching.i18n.yaml b/.agents/notes/implemented/process/2026-07-26-pnpm-action-setup-for-symmetric-ci-caching.i18n.yaml index 23f0580c07..0b452fcf81 100644 --- a/.agents/notes/implemented/process/2026-07-26-pnpm-action-setup-for-symmetric-ci-caching.i18n.yaml +++ b/.agents/notes/implemented/process/2026-07-26-pnpm-action-setup-for-symmetric-ci-caching.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write -2026-07-26-pnpm-action-setup-for-symmetric-ci-caching.md: 0cc0a09dfe1019d80b08599a245ce4bb044d81c5 -2026-07-26-pnpm-action-setup-for-symmetric-ci-caching.zh.md: 477f8eed61579eb9c4f18701a39307daf098407d +2026-07-26-pnpm-action-setup-for-symmetric-ci-caching.md: d734f6be65affb15f7e56c08b0b6d268002a44d8 +2026-07-26-pnpm-action-setup-for-symmetric-ci-caching.zh.md: 77ea0aa1cc7443ca7cd9e89b652a9c908bab64e5 diff --git a/.agents/notes/implemented/process/2026-07-26-pnpm-action-setup-for-symmetric-ci-caching.md b/.agents/notes/implemented/process/2026-07-26-pnpm-action-setup-for-symmetric-ci-caching.md index 0cc0a09dfe..d734f6be65 100644 --- a/.agents/notes/implemented/process/2026-07-26-pnpm-action-setup-for-symmetric-ci-caching.md +++ b/.agents/notes/implemented/process/2026-07-26-pnpm-action-setup-for-symmetric-ci-caching.md @@ -1,4 +1,4 @@ -# Agent Note: Use pnpm/action-setup for symmetric CI pnpm caching +# Agent Note: Provision CI pnpm via pnpm/action-setup Status: implemented @@ -6,27 +6,27 @@ English | [中文](2026-07-26-pnpm-action-setup-for-symmetric-ci-caching.zh.md) ## Problem -Five workflows repeated a hand-rolled three-step pnpm setup — `corepack enable`, `pnpm store path --silent >> $GITHUB_OUTPUT`, then `actions/cache@v4` keyed on `pnpm-lock.yaml`: `e2e.yml`, `docs-pages.yml`, `pi-ai-provider-e2e.yml`, `build-exe-for-python-sdk.yml`, and the node-compat, serial-linux, and benchmark jobs of `ci.yml` (~40–60 YAML lines total). The maintained equivalent — `pnpm/action-setup@v4` (reads `packageManager` from package.json) plus `actions/setup-node` with `cache: pnpm` — was already proven in-repo in `landlock-run.yml`, and also insulates against corepack's removal from newer Node distributions. +Every workflow hand-provisioned pnpm with `corepack enable`, and five of them further repeated a hand-rolled cache setup — `pnpm store path --silent >> $GITHUB_OUTPUT`, then `actions/cache@v4` keyed on `pnpm-lock.yaml`: `e2e.yml`, `docs-pages.yml`, `pi-ai-provider-e2e.yml`, `build-exe-for-python-sdk.yml`, and the node-compat, serial-linux, and benchmark jobs of `ci.yml` (~40–60 YAML lines of drifting copies). The maintained equivalent — `pnpm/action-setup@v4` (reads `packageManager` from package.json) plus `actions/setup-node` with `cache: pnpm` — was already proven in-repo in `landlock-run.yml`, and corepack's removal from newer Node distributions made every `corepack enable` a known future break. ## Decision -The symmetric-cache setups use `pnpm/action-setup@v4` followed by `actions/setup-node` with `cache: pnpm`, the `landlock-run.yml` pattern: `e2e.yml`, `docs-pages.yml`, `pi-ai-provider-e2e.yml`, `build-exe-for-python-sdk.yml`, and the node-compat and two benchmark jobs of `ci.yml`. The larger-runner benchmark keeps its store cache Linux-only through a conditional `cache:` input, mirroring the required Windows job's deliberate skip; the consolidated benchmark caches on both platforms as before. +`pnpm/action-setup@v4` is the only pnpm provisioning mechanism in CI: no workflow runs `corepack enable`. Caching remains per-job policy on top of it, in three deliberate shapes: -Explicitly NOT converted: - -- the three enterprise-runner PR jobs in `ci.yml` — they deliberately use `actions/cache/restore` only, keeping cache compression/upload off the paid latency-critical path, an asymmetry `setup-node`'s cache cannot express; -- the Windows job, which deliberately skips the store cache; -- the store-cache step of `ci.yml`'s serial-linux job — the job swaps `corepack enable` for `pnpm/action-setup@v4`, but its `pnpm store path` + `actions/cache@v4` steps stay hand-rolled because the master-push serial-linux run is the save side that populates the exact key and path the enterprise restore-only jobs consume; converting the producer to `setup-node`'s own key format would silently starve their restores. +- **Symmetric cache** (restore and save): `actions/setup-node` with `cache: pnpm` — `e2e.yml`, `docs-pages.yml`, `pi-ai-provider-e2e.yml`, `build-exe-for-python-sdk.yml`, and the node-compat and two benchmark jobs of `ci.yml`. The larger-runner benchmark keeps its store cache Linux-only through a conditional `cache:` input; the consolidated benchmark caches on both platforms. +- **Restore-only / producer pairing** (hand-rolled `actions/cache` steps, unchanged): the three enterprise-runner PR jobs restore without saving, keeping cache compression/upload off the paid latency-critical path — an asymmetry `setup-node`'s cache cannot express — and the master-push serial-linux job keeps its `pnpm store path` + `actions/cache@v4` save side, because it populates the exact key and path those restore-only jobs consume; converting the producer to `setup-node`'s key format would silently starve their restores. +- **Cache-less** (no store cache at all): the required Windows job and serial-windows (many-file store extraction is slower than a clean install there), serial-macos, sandbox.yml, and the coverage/consumers enterprise jobs that already restore via the shared enterprise key. ## Alternatives considered -- **Keep the hand-rolled steps.** They work, but they are five drifting copies of setup boilerplate, and the corepack dependency is a known future break. -- **Convert everything including the enterprise jobs.** Rejected: the restore-only asymmetry is a documented latency decision in `ci.yml`'s comments; erasing it to unify tooling inverts the priority. -- **Convert serial-linux's store cache too.** Rejected during implementation: the proposal counted serial-linux among the symmetric setups, but its cache step is the producer half of the enterprise jobs' restore-only pairing — moving it to `setup-node`'s key format is the enterprise conversion by another route. +- **Keep the hand-rolled steps.** They worked, but they were drifting copies of setup boilerplate, and the corepack dependency was a known future break. +- **Convert the enterprise jobs' caching to `cache: pnpm`.** Rejected: the restore-only asymmetry is a documented latency decision in `ci.yml`'s comments; erasing it to unify tooling inverts the priority. +- **Convert serial-linux's store cache.** Rejected during implementation: the original proposal counted serial-linux among the symmetric setups, but its cache step is the producer half of the enterprise jobs' restore-only pairing — moving it to `setup-node`'s key format is the enterprise conversion by another route. +- **Stop at the cache-bearing workflows and leave the other `corepack enable` sites.** Rejected on review follow-up: provisioning and caching are separable concerns, and leaving corepack in the cache-less jobs kept the future break and two provisioning idioms for no benefit. +- **A composite action wrapping action-setup + setup-node.** Rejected for now: the remaining per-job variation (node-version matrices, per-platform conditional caching, the restore-only pairing) is deliberate policy, not boilerplate — a wrapper would grow mirroring inputs or flatten a real asymmetry, and the two-line pair is already near the floor. ## Consequences -- The corepack dependency is gone from every converted workflow; pnpm arrives via the pnpm team's official action, already trusted in-repo (`landlock-run.yml`). -- The cache-key format changed once; one cold run per converted lane repopulates it, after which hit rates match the old steps. The built-in key spans platform, arch, and the lockfile hash but not the Node version, so the node-compat matrix legs share one store entry — safe, because the pnpm store is Node-version-independent. +- The corepack dependency is gone from CI entirely; pnpm arrives via the pnpm team's official action everywhere, and the version pin stays single-sourced in `package.json`'s `packageManager` field. +- The cache-key format changed once for converted lanes; one cold run repopulated it, after which hit rates match the old steps. The built-in key spans platform, arch, and the lockfile hash but not the Node version, so the node-compat matrix legs share one store entry — safe, because the pnpm store is Node-version-independent. - `setup-node`'s built-in pnpm cache restores by exact key only, with no `restore-keys` prefix fallback: a `pnpm-lock.yaml` change starts a converted lane from a cold store instead of seeding from the previous entry. -- About 75 net lines of workflow YAML removed; the enterprise-runner PR jobs and the Windows job are byte-identical, and serial-linux keeps producing the key they restore. +- About 75 net lines of workflow YAML removed. The enterprise-runner PR jobs' and Windows jobs' cache behavior is unchanged (only their provisioning line moved to the action), and serial-linux keeps producing the key the restore-only jobs consume. diff --git a/.agents/notes/implemented/process/2026-07-26-pnpm-action-setup-for-symmetric-ci-caching.zh.md b/.agents/notes/implemented/process/2026-07-26-pnpm-action-setup-for-symmetric-ci-caching.zh.md index 477f8eed61..77ea0aa1cc 100644 --- a/.agents/notes/implemented/process/2026-07-26-pnpm-action-setup-for-symmetric-ci-caching.zh.md +++ b/.agents/notes/implemented/process/2026-07-26-pnpm-action-setup-for-symmetric-ci-caching.zh.md @@ -1,4 +1,4 @@ -# Agent Note: 用 pnpm/action-setup 实现对称的 CI pnpm 缓存 +# Agent Note: 经由 pnpm/action-setup 提供 CI 的 pnpm Status: implemented @@ -6,27 +6,27 @@ Status: implemented ## 问题 -五个工作流曾重复着同一套手写(hand-rolled)的三步 pnpm 设置——`corepack enable`、`pnpm store path --silent >> $GITHUB_OUTPUT`、再加以 `pnpm-lock.yaml` 为缓存键的 `actions/cache@v4`:`e2e.yml`、`docs-pages.yml`、`pi-ai-provider-e2e.yml`、`build-exe-for-python-sdk.yml`,以及 `ci.yml` 的 node-compat、serial-linux 与 benchmark 作业(合计约 40–60 行 YAML)。与之等价、由官方维护的做法——`pnpm/action-setup@v4`(从 package.json 读取 `packageManager`)加带 `cache: pnpm` 的 `actions/setup-node`——当时已在仓库内的 `landlock-run.yml` 中得到验证,同时还能隔绝 corepack 被从较新 Node 发行版中移除的影响。 +每个工作流都曾用 `corepack enable` 手工提供 pnpm,其中五个还各自重复着一套手写(hand-rolled)的缓存设置——`pnpm store path --silent >> $GITHUB_OUTPUT`、再加以 `pnpm-lock.yaml` 为缓存键的 `actions/cache@v4`:`e2e.yml`、`docs-pages.yml`、`pi-ai-provider-e2e.yml`、`build-exe-for-python-sdk.yml`,以及 `ci.yml` 的 node-compat、serial-linux 与 benchmark 作业(约 40–60 行各自漂移的 YAML 副本)。与之等价、由官方维护的做法——`pnpm/action-setup@v4`(从 package.json 读取 `packageManager`)加带 `cache: pnpm` 的 `actions/setup-node`——当时已在仓库内的 `landlock-run.yml` 中得到验证,而 corepack 被从较新 Node 发行版中移除,使每一处 `corepack enable` 都成了已知的未来失效点。 ## 决策 -各对称缓存设置现采用 `pnpm/action-setup@v4` 后接带 `cache: pnpm` 的 `actions/setup-node`,即 `landlock-run.yml` 的模式:`e2e.yml`、`docs-pages.yml`、`pi-ai-provider-e2e.yml`、`build-exe-for-python-sdk.yml`,以及 `ci.yml` 的 node-compat 与两个 benchmark 作业。larger-runner benchmark 通过条件化的 `cache:` 输入让 store 缓存仅限 Linux,与必需 Windows 作业刻意跳过缓存的做法保持一致;consolidated benchmark 与之前一样在两个平台上都启用缓存。 +`pnpm/action-setup@v4` 是 CI 中提供 pnpm 的唯一机制:没有任何工作流运行 `corepack enable`。缓存仍是叠加其上的按作业政策,保持三种刻意的形态: -以下明确未做转换: - -- `ci.yml` 中运行在企业 runner 上的三个 PR(Pull Request)作业——它们刻意只用 `actions/cache/restore`,把缓存压缩/上传挡在付费且延迟敏感的关键路径之外,这种不对称是 `setup-node` 的缓存无法表达的; -- Windows 作业,它刻意跳过 store 缓存; -- `ci.yml` 中 serial-linux 作业的 store 缓存步骤——该作业把 `corepack enable` 换成了 `pnpm/action-setup@v4`,但其 `pnpm store path` + `actions/cache@v4` 步骤仍保持手写,因为 master 推送触发的 serial-linux 运行正是写入侧,负责填充企业只恢复不上传(restore-only)作业所消费的那个精确缓存键与路径;把生产者改成 `setup-node` 自有的键格式,会悄然断供它们的恢复。 +- **对称缓存**(既恢复也保存):带 `cache: pnpm` 的 `actions/setup-node`——`e2e.yml`、`docs-pages.yml`、`pi-ai-provider-e2e.yml`、`build-exe-for-python-sdk.yml`,以及 `ci.yml` 的 node-compat 与两个 benchmark 作业。larger-runner benchmark 通过条件化的 `cache:` 输入让 store 缓存仅限 Linux;consolidated benchmark 在两个平台上都启用缓存。 +- **只恢复不上传/生产者配对**(手写的 `actions/cache` 步骤,保持不变):企业 runner 上的三个 PR(Pull Request)作业只恢复不保存,把缓存压缩/上传挡在付费且延迟敏感的关键路径之外——这种不对称是 `setup-node` 的缓存无法表达的;master 推送触发的 serial-linux 作业保留其 `pnpm store path` + `actions/cache@v4` 写入侧,因为它负责填充那些只恢复不上传(restore-only)作业所消费的精确缓存键与路径;把生产者改成 `setup-node` 的键格式,会悄然断供它们的恢复。 +- **无缓存**(完全不设 store 缓存):必需的 Windows 作业与 serial-windows(在那里解压海量小文件的 store 缓存比干净安装更慢)、serial-macos、sandbox.yml,以及本就经共享企业键恢复的 coverage/consumers 企业作业。 ## 曾考虑的替代方案 -- **保留手写步骤。** 它们能用,但那是五份会各自漂移的设置样板副本,而且对 corepack 的依赖是已知的未来失效点。 -- **连企业作业在内全部转换。** 否决:只恢复不上传的不对称是 `ci.yml` 注释中有记录的延迟决策;为统一工具而抹掉它,属于颠倒优先级。 -- **连 serial-linux 的 store 缓存也转换。** 实现期间否决:提案曾把 serial-linux 计入对称设置,但其缓存步骤是企业作业只恢复不上传配对中的生产者一半——把它改成 `setup-node` 的键格式,等于换条路径做了企业作业的转换。 +- **保留手写步骤。** 它们能用,但那是会各自漂移的设置样板副本,而且对 corepack 的依赖是已知的未来失效点。 +- **把企业作业的缓存也转换成 `cache: pnpm`。** 否决:只恢复不上传的不对称是 `ci.yml` 注释中有记录的延迟决策;为统一工具而抹掉它,属于颠倒优先级。 +- **转换 serial-linux 的 store 缓存。** 实现期间否决:原提案曾把 serial-linux 计入对称设置,但其缓存步骤是企业作业只恢复不上传配对中的生产者一半——把它改成 `setup-node` 的键格式,等于换条路径做了企业作业的转换。 +- **只转换带缓存的工作流,留下其余 `corepack enable` 站点。** 评审跟进时否决:提供 pnpm 与缓存是可分离的关注点,在无缓存作业里留下 corepack 只会保留未来失效点和两套并存的提供方式,毫无收益。 +- **用一个组合 action 包装 action-setup + setup-node。** 暂不采纳:剩余的按作业差异(node 版本矩阵、按平台的条件缓存、只恢复不上传配对)是刻意的政策而非样板——包装层要么长出镜像这些差异的输入,要么抹平一处真实的不对称,而两行的组合已接近下限。 ## 后果 -- 每个已转换的工作流都不再依赖 corepack;pnpm 经由 pnpm 团队的官方 action 提供,该 action 已在仓库内获得信任(`landlock-run.yml`)。 -- 缓存键格式变更了一次;每条已转换泳道各跑一次冷运行以重建缓存,此后命中率与旧步骤持平。内建缓存键涵盖平台、架构与锁文件哈希,但不含 Node 版本,因此 node-compat 矩阵的各条腿共享同一条 store 缓存记录——这是安全的,因为 pnpm store 与 Node 版本无关。 +- corepack 依赖已从 CI 中彻底消失;pnpm 在所有工作流中都经由 pnpm 团队的官方 action 提供,版本锁定继续单一来源于 `package.json` 的 `packageManager` 字段。 +- 已转换泳道的缓存键格式变更了一次;各跑一次冷运行重建缓存后,命中率与旧步骤持平。内建缓存键涵盖平台、架构与锁文件哈希,但不含 Node 版本,因此 node-compat 矩阵的各条腿共享同一条 store 缓存记录——这是安全的,因为 pnpm store 与 Node 版本无关。 - `setup-node` 内建的 pnpm 缓存只按精确键恢复,没有 `restore-keys` 前缀回退:`pnpm-lock.yaml` 一旦变更,已转换泳道会从冷 store 起步,而不是从上一条缓存记录播种。 -- 净删除约 75 行工作流 YAML;企业 runner 上的 PR 作业与 Windows 作业逐字节未动,serial-linux 继续生产它们所恢复的缓存键。 +- 净删除约 75 行工作流 YAML。企业 runner 上的 PR 作业与 Windows 作业的缓存行为未变(只有提供 pnpm 的那一行改用了 action),serial-linux 继续生产只恢复不上传作业所消费的缓存键。 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3ee12e9768..6813b75c56 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -52,14 +52,14 @@ jobs: restore-keys: | ${{ runner.os }}-node-${{ env.PRIMARY_NODE_VERSION }}-pnpm- + - uses: pnpm/action-setup@v4 + - uses: actions/setup-node@v6 with: node-version: ${{ env.PRIMARY_NODE_VERSION }} - - name: Enable corepack and install dependencies - run: | - corepack enable - pnpm install --frozen-lockfile + - name: Install (immutable) + run: pnpm install --frozen-lockfile - name: Run static gates env: @@ -98,13 +98,14 @@ jobs: restore-keys: | ${{ runner.os }}-node-${{ env.PRIMARY_NODE_VERSION }}-pnpm- + - uses: pnpm/action-setup@v4 + - uses: actions/setup-node@v6 with: node-version: ${{ env.PRIMARY_NODE_VERSION }} - - name: Enable corepack, install dependencies, and prepare bubblewrap + - name: Install dependencies and prepare bubblewrap run: | - corepack enable pnpm install --frozen-lockfile & install_pid=$! bash scripts/prepare-ci-bubblewrap.sh & @@ -158,13 +159,14 @@ jobs: restore-keys: | ${{ runner.os }}-node-${{ env.PRIMARY_NODE_VERSION }}-eslint-full- + - uses: pnpm/action-setup@v4 + - uses: actions/setup-node@v6 with: node-version: ${{ env.PRIMARY_NODE_VERSION }} - - name: Enable corepack, install dependencies, and prepare bubblewrap + - name: Install dependencies and prepare bubblewrap run: | - corepack enable pnpm install --frozen-lockfile & install_pid=$! bash scripts/prepare-ci-bubblewrap.sh & @@ -291,17 +293,17 @@ jobs: reg add "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\AppModelUnlock" /t REG_DWORD /f /v "AllowDevelopmentWithoutDevLicense" /d "1" + - uses: pnpm/action-setup@v4 + + # Extracting the many-file pnpm store cache is slower than a clean install, + # and saving it adds more latency after gates, so this job runs cache-less. - uses: actions/setup-node@v6 with: node-version: ${{ env.PRIMARY_NODE_VERSION }} - # Extracting the many-file pnpm store cache is slower than a clean install, - # and saving it adds more latency after gates. - - name: Enable corepack and install (immutable) + - name: Install (immutable) shell: pwsh - run: | - corepack enable - pnpm install --frozen-lockfile + run: pnpm install --frozen-lockfile - name: Run blocking and observational Windows gates concurrently shell: pwsh @@ -374,13 +376,12 @@ jobs: steps: - uses: actions/checkout@v6 + - uses: pnpm/action-setup@v4 + - uses: actions/setup-node@v6 with: node-version: ${{ env.PRIMARY_NODE_VERSION }} - - name: Enable corepack (pnpm) - run: corepack enable - - name: Install (immutable) run: pnpm install --frozen-lockfile @@ -406,14 +407,12 @@ jobs: reg add "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\AppModelUnlock" /t REG_DWORD /f /v "AllowDevelopmentWithoutDevLicense" /d "1" + - uses: pnpm/action-setup@v4 + - uses: actions/setup-node@v6 with: node-version: ${{ env.PRIMARY_NODE_VERSION }} - - name: Enable corepack (pnpm) - shell: pwsh - run: corepack enable - # Master refreshes the small cache that pull requests restore without # putting package-store extraction back on the Windows critical path. - uses: actions/cache@v4 diff --git a/.github/workflows/sandbox.yml b/.github/workflows/sandbox.yml index 51dfe06f1c..0d14914bfd 100644 --- a/.github/workflows/sandbox.yml +++ b/.github/workflows/sandbox.yml @@ -53,13 +53,12 @@ jobs: steps: - uses: actions/checkout@v6 + - uses: pnpm/action-setup@v4 + - uses: actions/setup-node@v6 with: node-version: 24 - - name: Enable corepack (pnpm) - run: corepack enable - - name: Install (immutable) run: pnpm install --frozen-lockfile