Skip to content

Commit a8a7572

Browse files
elrrrrrrrclaude
andauthored
chore: migrate to utoo CI (#5830)
## Summary Migrate the main CI workflow from pnpm to [utoo (`ut`)](https://github.com/utooland/utoo). The E2E workflow keeps pnpm for now — it requires `catalog:` / `workspace:` protocol resolution and `publishConfig` overrides during pack which `ut pm-pack` does not yet provide. ## Install speedup The direct win of this migration: **`ut install --from pnpm` is 2.2–3.6× faster than `pnpm install`** across all test matrix platforms. | Platform | pnpm install | ut install | Speedup | |---|---|---|---| | ubuntu-22 | 16.8s | **5.2s** | 3.2× | | ubuntu-24 | 17.7s | **4.9s** | 3.6× | | macos-22 | 26.3s | **9.5s** | 2.8× | | macos-24 | 30.1s | **9.1s** | 3.3× | | windows-22 | 31.1s | **12.0s** | 2.6× | | windows-24 | 28.4s | **12.7s** | 2.2× | Every test matrix job re-installs from scratch, so this 15–20s savings per job compounds into the per-job reductions below. ## Overall CI wall-clock On a clean run (no reruns), the full workflow shaves **~1 minute off wall-clock** (25m34s → 24m32s, ~4% faster). The critical path is still bounded by Windows Test (~20–24m), so faster jobs elsewhere can't meaningfully shrink wall-clock. Flaky reruns on the critical path can degrade practical times further. The win of this PR is **per-job speedup** (especially macOS, where Test jobs drop 6–8 minutes each) and **developer-machine ergonomics** (`ut install` on your laptop is also 3× faster). ## CI changes - `.github/workflows/ci.yml`: replace `pnpm/action-setup` + `pnpm install` with `utooland/setup-utoo` + `ut install --from pnpm` - `package.json`: convert pnpm-specific scripts to ut equivalents (`ut run --workspaces`, `ut run --workspace <name>`) - `site:build` / `site:dev` use `cd site && npm run build/dev` so Cloudflare Pages works (its env has neither pnpm nor utoo) - `.gitignore`: add `.utoo.toml` and `.claude/` - `.oxfmtrc.json`: ignore root `package.json` (ut rewrites it with npm-style `workspaces` field) ## egg-bin fixes ### CJS resolution (`baseCommand.ts`) `ts-node`, `tsconfig-paths` are CJS packages without `exports`. On flat-hoisted layouts (npm/utoo), `importResolve` (which uses `import.meta.resolve`) fails because the ESM resolver doesn't auto-append `.js` for bare subpaths. Fix: use `createRequire` for these CJS packages. ### Self-test fixture opt-out (`test.ts` + `coffee.ts`) Under flat-hoisting, egg-bin's own test fixtures reach `@eggjs/mock` and `@eggjs/tegg-vitest` via monorepo root hoist even though the fixture projects don't use them. This caused every test fork to load both frameworks unnecessarily (~90s for mock, ~7s/fork for tegg runner → ~70s for the test-bin suite). Solution: the test harness (`coffee.ts`) sets `EGG_BIN_SELF_TEST_FIXTURE=1` when spawning egg-bin against its own fixtures. `test.ts` checks this env var and skips both mock and tegg-runner auto-detection. For real projects, behaviour is unchanged: both frameworks are resolve-then-use. The tegg runner also falls back to egg-bin's own dirname for E2E scenarios (cnpmcore uses tegg transitively via egg without declaring it directly). ## Build perf: publint pack `publint` runs `pnpm pack` by default against each of ~80 packages. On main CI (utoo env, no pnpm binary) this fails. On E2E (pnpm env, symlinked node_modules) npm pack is ~10× slower per package — `create-egg` alone took 85s vs 1s under pnpm. Fix: `tsdown.config.ts` reads `PUBLINT_PACK` env var, defaulting to `npm` (works in main CI). E2E workflow sets `PUBLINT_PACK=pnpm` so it stays fast there. ## Dependency fixes | Change | Why | |---|---| | `oxlint-tsgolint: ^0.15.0` → `^0.18.1` | Peer requirement of oxlint 1.60.0 | | Add `@types/content-type`, `@types/koa-compose` to root devDeps | oxlint type-check needs these at workspace root, not just hoisted from sub-packages | ## Other source adaptations | File | What | |---|---| | `packages/cluster/test/options.test.ts` | Accept flat-hoisted `node_modules/egg` path | | `packages/tsconfig/test/index.test.ts` | Use `require.resolve` instead of hardcoded path | | `tegg/plugin/controller`, `tegg/plugin/mcp-proxy` | Remove `@ts-expect-error` for types discoverable under flat hoisting | ## Per-job timing (clean run, no reruns) | Job | next | chore-ut-ci | Delta | |---|---|---|---| | typecheck | 2m7s | **1m39s** | -28s | | Test (ubuntu, 22) | 14m52s | 15m20s | +28s | | Test (ubuntu, 24) | 13m38s | **13m28s** | -10s | | Test (macos, 22) | 21m5s | **14m40s** | **-6m25s** | | Test (macos, 24) | 18m54s | **10m38s** | **-8m16s** | | Test (windows, 22) | 25m24s | **24m23s** | -1m1s | | Test (windows, 24) | 23m7s | **20m59s** | -2m8s | | Test bin (ubuntu) | 2m19s | **2m2s** | -17s | | Test bin (windows) | 3m51s | **2m56s** | -55s | | Test scripts (ubuntu, 22) | 1m41s | **1m13s** | -28s | | Test scripts (ubuntu, 24) | 1m36s | **1m16s** | -20s | | cnpmcore E2E | 6m0s | **5m58s** | -2s | | examples E2E | 2m35s | **2m25s** | -10s | Almost every job is faster; the small Test (ubuntu 22) +28s regression is within run-to-run variance. ## Test plan - [x] typecheck - [x] Test matrix (ubuntu/macos/windows × Node 22/24) - [x] Test bin, Test scripts - [x] E2E (cnpmcore + examples) - [x] Cloudflare Pages 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit ## Release Notes * **Chores** * Migrated build and dependency management tooling from pnpm to utoo across the monorepo. * Updated CI workflows, workspace configurations, and npm scripts to support the new tooling. * Added build configuration for egg-bin TypeScript compilation. * Enhanced test infrastructure for environment isolation and compatibility. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 490f849 commit a8a7572

File tree

16 files changed

+123
-73
lines changed

16 files changed

+123
-73
lines changed

.github/workflows/ci.yml

Lines changed: 24 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
# CI workflow for egg monorepo
12
name: CI
23

34
on:
@@ -27,31 +28,31 @@ jobs:
2728
- name: Checkout repository
2829
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
2930

30-
- name: Install pnpm
31-
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4
31+
- name: Setup utoo
32+
uses: utooland/setup-utoo@3a51006d0b66afcc32d1b9177a4b200b74f4a8cb # main
3233

3334
- name: Set up Node.js
3435
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6
3536
with:
3637
node-version: '24'
3738

3839
- name: Install dependencies
39-
run: pnpm install --no-frozen-lockfile
40+
run: ut install --from pnpm
4041

4142
- name: Run lint
42-
run: pnpm run lint
43+
run: ut run lint
4344

4445
- name: Run typecheck
45-
run: pnpm run typecheck
46+
run: ut run typecheck
4647

4748
- name: Run format check
48-
run: pnpm run fmtcheck
49+
run: ut run fmtcheck
4950

5051
- name: Run build
51-
run: pnpm run build
52+
run: ut run build
5253

5354
- name: Run site build
54-
run: pnpm run site:build
55+
run: ut run site:build
5556

5657
test:
5758
strategy:
@@ -150,24 +151,24 @@ jobs:
150151
# & mysqladmin -u root password root
151152
& mysql -uroot -e "CREATE DATABASE IF NOT EXISTS test;"
152153
153-
- name: Install pnpm
154-
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4
154+
- name: Setup utoo
155+
uses: utooland/setup-utoo@3a51006d0b66afcc32d1b9177a4b200b74f4a8cb # main
155156

156157
- name: Set up Node.js
157158
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6
158159
with:
159160
node-version: ${{ matrix.node }}
160161

161162
- name: Install dependencies
162-
run: pnpm install --no-frozen-lockfile
163+
run: ut install --from pnpm
163164

164165
- name: Run tests
165-
run: pnpm run ci
166+
run: ut run ci
166167

167168
- name: Run example tests
168169
if: ${{ matrix.os != 'windows-latest' }}
169170
run: |
170-
pnpm run example:test:all
171+
ut run example:test:all
171172
172173
- name: Code Coverage
173174
# skip on windows, it will hangup on codecov
@@ -194,21 +195,21 @@ jobs:
194195
- name: Checkout repository
195196
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
196197

197-
- name: Install pnpm
198-
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4
198+
- name: Setup utoo
199+
uses: utooland/setup-utoo@3a51006d0b66afcc32d1b9177a4b200b74f4a8cb # main
199200

200201
- name: Set up Node.js
201202
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6
202203
with:
203204
node-version: ${{ matrix.node }}
204205

205206
- name: Install dependencies
206-
run: pnpm install --no-frozen-lockfile
207+
run: ut install --from pnpm
207208

208209
- name: Run tests
209210
run: |
210-
pnpm build --workspace ./tools/egg-bin
211-
pnpm run --filter ./tools/egg-bin ci
211+
ut run build -- --workspace ./tools/egg-bin
212+
ut run ci --workspace @eggjs/bin
212213
213214
- name: Code Coverage
214215
# skip on windows, it will hangup on codecov https://github.com/codecov/codecov-action/issues/1787
@@ -235,21 +236,21 @@ jobs:
235236
- name: Checkout repository
236237
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
237238

238-
- name: Install pnpm
239-
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4
239+
- name: Setup utoo
240+
uses: utooland/setup-utoo@3a51006d0b66afcc32d1b9177a4b200b74f4a8cb # main
240241

241242
- name: Set up Node.js
242243
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6
243244
with:
244245
node-version: ${{ matrix.node }}
245246

246247
- name: Install dependencies
247-
run: pnpm install --no-frozen-lockfile
248+
run: ut install --from pnpm
248249

249250
- name: Run tests
250251
run: |
251-
pnpm build
252-
pnpm run --filter=./tools/scripts ci
252+
ut run build -- --workspace ./tools/scripts
253+
ut run ci --workspace tools/scripts
253254
254255
- name: Code Coverage
255256
if: ${{ matrix.os != 'windows-latest' }}

.github/workflows/e2e-test.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,11 @@ jobs:
153153
run: pnpm install --no-frozen-lockfile
154154

155155
- name: Build all packages
156+
env:
157+
# publint pack defaults to npm (main CI env has no pnpm); in E2E we
158+
# already have pnpm installed and npm pack against pnpm's symlinked
159+
# node_modules is ~10x slower, so prefer pnpm pack here
160+
PUBLINT_PACK: pnpm
156161
run: pnpm build
157162

158163
- name: Pack packages into tgz

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,3 +118,5 @@ tegg/plugin/tegg/test/fixtures/apps/**/*.js
118118
ecosystem-ci/cnpmcore
119119
ecosystem-ci/examples
120120
pnpm-lock.yaml
121+
.utoo.toml
122+
.claude/

.oxfmtrc.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@
88
"packages/core/test/fixtures/load_dirs/syntax_error/*",
99
"packages/core/test/fixtures/syntaxerror/*",
1010
"packages/core/test/fixtures/load_context_syntax_error/**/*",
11-
"CHANGELOG.md"
11+
"CHANGELOG.md",
12+
"/package.json"
1213
],
1314
"experimentalSortImports": {
1415
"groups": [

package.json

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -13,26 +13,25 @@
1313
],
1414
"type": "module",
1515
"scripts": {
16-
"clean-dist": "pnpm -r --parallel exec rimraf dist",
17-
"clean": "pnpm -r --parallel run clean && pnpm clean-dist",
16+
"clean-dist": "ut run clean --workspaces --if-present",
1817
"build": "tsdown",
19-
"prelint": "pnpm clean-dist",
18+
"prelint": "ut run clean-dist",
2019
"lint": "oxlint --type-aware --type-check --quiet",
2120
"fmt": "oxfmt",
22-
"typecheck": "pnpm clean && pnpm -r run typecheck",
21+
"typecheck": "ut run clean-dist && ut run typecheck --workspaces --if-present",
2322
"fmtcheck": "oxfmt --check .",
24-
"pretest": "pnpm run clean && pnpm -r run pretest",
23+
"pretest": "ut run clean-dist && ut run pretest --workspaces --if-present",
2524
"test": "vitest run --bail 1 --retry 2 --testTimeout 20000 --hookTimeout 20000",
26-
"test:cov": "pnpm run test --coverage",
27-
"preci": "pnpm -r --parallel run pretest",
28-
"ci": "pnpm run test --coverage",
29-
"site:dev": "pnpm --filter=site run dev",
30-
"site:build": "pnpm --filter=site run build",
25+
"test:cov": "ut run test -- --coverage",
26+
"preci": "ut run pretest --workspaces --if-present",
27+
"ci": "ut run test -- --coverage",
28+
"site:dev": "cd site && npm run dev",
29+
"site:build": "cd site && npm run build",
3130
"puml": "puml . --dest ./site",
32-
"example:dev:commonjs": "pnpm --filter=helloworld-commonjs run dev",
33-
"example:dev:typescript": "pnpm --filter=helloworld-typescript run dev",
34-
"example:dev:tegg": "pnpm --filter=helloworld-tegg run dev",
35-
"example:test:all": "pnpm --filter=helloworld-* run test",
31+
"example:dev:commonjs": "ut run dev --workspace helloworld-commonjs",
32+
"example:dev:typescript": "ut run dev --workspace helloworld-typescript",
33+
"example:dev:tegg": "ut run dev --workspace helloworld-tegg",
34+
"example:test:all": "ut run test --workspace 'helloworld-*' --if-present",
3635
"prepare": "husky",
3736
"version:patch": "node scripts/version.js patch",
3837
"version:minor": "node scripts/version.js minor",
@@ -71,6 +70,7 @@
7170
"typescript": "catalog:",
7271
"unplugin-unused": "catalog:",
7372
"urllib": "catalog:",
73+
"utoo": "catalog:",
7474
"vitest": "catalog:"
7575
},
7676
"lint-staged": {

packages/cluster/test/options.test.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -239,10 +239,12 @@ describe('test/options.test.ts', () => {
239239
baseDir,
240240
});
241241
const expectPaths = [
242-
// run int workspace root
242+
// run in workspace root
243243
path.join(__dirname, '../../egg'),
244-
// run in project root
244+
// run in project root (pnpm nested)
245245
path.join(__dirname, '../node_modules/egg'),
246+
// run with flat/hoisted node_modules (e.g. ut install)
247+
path.join(__dirname, '../../../node_modules/egg'),
246248
];
247249
assert(
248250
expectPaths.includes(options.framework),

packages/tsconfig/test/index.test.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
import fs from 'node:fs/promises';
2+
import { createRequire } from 'node:module';
23
import path from 'node:path';
34

45
import coffee from 'coffee';
56
import { test, expect } from 'vitest';
67

8+
const require = createRequire(import.meta.url);
9+
710
test('should tsc build work', async () => {
8-
const tsc = path.join(import.meta.dirname, '..', 'node_modules', 'typescript', 'bin', 'tsc');
11+
const tsc = require.resolve('typescript/bin/tsc');
912
const fixturePath = path.join(import.meta.dirname, 'fixtures/apps/ts-proj');
1013
const tsconfigPath = path.join(fixturePath, 'tsconfig.json');
1114
console.log('%s -p %s, cwd: %s', tsc, tsconfigPath, fixturePath);

packages/utils/test/import.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,8 +107,8 @@ describe('test/import.test.ts', () => {
107107
assert.equal(err.name, 'ImportResolveError');
108108
assert.equal(err.filepath, 'tsconfig-paths-demo-not-exists/register');
109109
assert.deepEqual(err.paths, [getFilepath('cjs/node_modules/inject')]);
110-
assert.match(err.stack ?? '', /Cannot find package/);
111-
assert.match(err.message, /Cannot find package/);
110+
assert.match(err.stack ?? '', /Cannot find (package|module)/);
111+
assert.match(err.message, /Cannot find (package|module)/);
112112
return true;
113113
},
114114
);

pnpm-workspace.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,7 @@ catalog:
213213
urijs: ^1.19.11
214214
urllib: ^4.8.2
215215
utility: ^2.5.0
216+
utoo: ^1
216217
vary: ^1.1.2
217218
vitepress: 2.0.0-alpha.15
218219
vitepress-plugin-llms: ^1.10.0
@@ -227,6 +228,9 @@ catalogs:
227228
path-to-regexp1:
228229
path-to-regexp: ^1.9.0
229230

231+
onlyBuiltDependencies:
232+
- utoo
233+
230234
minimumReleaseAge: 1440
231235

232236
minimumReleaseAgeExclude:

tools/egg-bin/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,8 @@
5151
}
5252
},
5353
"scripts": {
54+
"build": "tsdown -c tsdown.config.ts",
5455
"typecheck": "tsgo --noEmit",
55-
"pretest": "tsdown",
5656
"test": "vitest run",
5757
"cov": "vitest run --coverage",
5858
"ci": "npm run cov"

0 commit comments

Comments
 (0)