Skip to content

Commit 10469b6

Browse files
committed
bench(pm): compare pnpm 12
1 parent 1e88fa2 commit 10469b6

3 files changed

Lines changed: 120 additions & 42 deletions

File tree

.github/workflows/pm-e2e-bench.yml

Lines changed: 65 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ name: utoopm-e2e-bench
1010
# pull_request / push with `(pm)` in title/msg → e2e jobs
1111
# pull_request labeled `benchmark` → bench-phases (utoo / utoo-npm / bun)
1212
# workflow_dispatch with `target` choice → manual one-off runs
13-
# (pm-bench-all = full PM shootout vs pnpm/yarn/bun/aube;
13+
# (pm-bench-all = full PM shootout vs pnpm/pnpm12/yarn/bun/aube;
1414
# pm-bench-phases = same as the labeled trigger but ad-hoc;
1515
# pm-bench-pcap = tcpdump capture for resolve phase)
1616
#
@@ -40,7 +40,7 @@ on:
4040
type: choice
4141
options: [both, npmjs, npmmirror]
4242
pm_list:
43-
description: '[pm-bench-all] Comma-separated PMs (e.g. utoo,bun); "all" → utoo,pnpm,yarn,bun,aube'
43+
description: '[pm-bench-all] Comma-separated PMs (e.g. utoo,bun); "all" → utoo,pnpm,pnpm12,yarn,bun,aube'
4444
required: false
4545
default: 'utoo,bun'
4646
type: string
@@ -544,6 +544,17 @@ jobs:
544544
uses: oven-sh/setup-bun@v2
545545
with:
546546
bun-version: latest
547+
- name: Install pnpm 12
548+
id: pnpm12
549+
if: contains(github.event.inputs.phases_pm_list, 'pnpm12')
550+
uses: pnpm/setup@v2
551+
with:
552+
version: next-12
553+
dest: ~/setup-pnpm12
554+
install: false
555+
- name: Expose pnpm 12 binary
556+
if: contains(github.event.inputs.phases_pm_list, 'pnpm12')
557+
run: echo "PNPM12_BIN=${{ steps.pnpm12.outputs.dest }}/pnpm" >> "$GITHUB_ENV"
547558
- name: Install pnpm
548559
if: contains(github.event.inputs.phases_pm_list, 'pnpm')
549560
uses: pnpm/action-setup@v4
@@ -580,6 +591,8 @@ jobs:
580591
if [ -n "${UTOO_NPM_BIN:-}" ]; then "$UTOO_NPM_BIN" --version; fi
581592
"$UTOO_NEXT_BIN" --version
582593
bun --version
594+
if command -v pnpm >/dev/null; then env PNPM_CONFIG_PM_ON_FAIL=ignore pnpm --version; fi
595+
if [ -n "${PNPM12_BIN:-}" ]; then env PNPM_CONFIG_PM_ON_FAIL=ignore "$PNPM12_BIN" --version; fi
583596
if command -v aube >/dev/null; then aube --version; fi
584597
- name: Define cache wipe function
585598
run: |
@@ -588,7 +601,7 @@ jobs:
588601
rm -rf /tmp/pm-bench /tmp/pm-bench-results /tmp/pm-bench-output \
589602
/tmp/utoo-bench-cache /tmp/utoo-npm-bench-cache \
590603
/tmp/utoo-next-bench-cache /tmp/utoo-alt-bench-cache /tmp/bun-bench-cache \
591-
/tmp/pnpm-bench-store \
604+
/tmp/pnpm-bench-store /tmp/pnpm12-bench-store \
592605
/tmp/aube-bench-data /tmp/aube-bench-cache \
593606
~/.bun/install/cache ~/.cache/nm 2>/dev/null || true
594607
mkdir -p /tmp/pm-bench-output
@@ -675,6 +688,17 @@ jobs:
675688
uses: oven-sh/setup-bun@v2
676689
with:
677690
bun-version: latest
691+
- name: Install pnpm 12
692+
id: pnpm12
693+
if: contains(github.event.inputs.phases_pm_list, 'pnpm12')
694+
uses: pnpm/setup@v2
695+
with:
696+
version: next-12
697+
dest: ~/setup-pnpm12
698+
install: false
699+
- name: Expose pnpm 12 binary
700+
if: contains(github.event.inputs.phases_pm_list, 'pnpm12')
701+
run: echo "PNPM12_BIN=${{ steps.pnpm12.outputs.dest }}/pnpm" >> "$GITHUB_ENV"
678702
- name: Install pnpm
679703
if: contains(github.event.inputs.phases_pm_list, 'pnpm')
680704
uses: pnpm/action-setup@v4
@@ -693,14 +717,16 @@ jobs:
693717
utoo --version
694718
"$UTOO_NPM_BIN" --version
695719
bun --version
720+
if command -v pnpm >/dev/null; then env PNPM_CONFIG_PM_ON_FAIL=ignore pnpm --version; fi
721+
if [ -n "${PNPM12_BIN:-}" ]; then env PNPM_CONFIG_PM_ON_FAIL=ignore "$PNPM12_BIN" --version; fi
696722
if command -v aube >/dev/null; then aube --version; fi
697723
- name: Define cache wipe function
698724
run: |
699725
cat > /tmp/wipe-bench-state.sh <<'WIPE'
700726
#!/usr/bin/env bash
701727
rm -rf /tmp/pm-bench /tmp/pm-bench-results /tmp/pm-bench-output \
702728
/tmp/utoo-bench-cache /tmp/utoo-npm-bench-cache /tmp/utoo-alt-bench-cache /tmp/bun-bench-cache \
703-
/tmp/pnpm-bench-store \
729+
/tmp/pnpm-bench-store /tmp/pnpm12-bench-store \
704730
/tmp/aube-bench-data /tmp/aube-bench-cache \
705731
~/.bun/install/cache ~/.cache/nm 2>/dev/null || true
706732
mkdir -p /tmp/pm-bench-output
@@ -762,7 +788,7 @@ jobs:
762788
# ============================================================
763789
# PM-BENCH-ALL (manual full PM shootout)
764790
# workflow_dispatch with target=pm-bench-all only — comparing utoo
765-
# against pnpm/yarn/bun/aube on cold + warm installs across
791+
# against pnpm/pnpm12/yarn/bun/aube on cold + warm installs across
766792
# configurable registries is slow, so it's never auto-triggered.
767793
# ============================================================
768794

@@ -799,9 +825,20 @@ jobs:
799825
run: |
800826
PM_LIST="${{ github.event.inputs.pm_list || 'utoo,bun' }}"
801827
if [ "$PM_LIST" = "all" ]; then
802-
PM_LIST="utoo,pnpm,yarn,bun,aube"
828+
PM_LIST="utoo,pnpm,pnpm12,yarn,bun,aube"
803829
fi
804830
echo "list=$PM_LIST" >> $GITHUB_OUTPUT
831+
- name: Install pnpm 12
832+
id: pnpm12
833+
if: contains(steps.pm.outputs.list, 'pnpm12')
834+
uses: pnpm/setup@v2
835+
with:
836+
version: next-12
837+
dest: ~/setup-pnpm12
838+
install: false
839+
- name: Expose pnpm 12 binary
840+
if: contains(steps.pm.outputs.list, 'pnpm12')
841+
run: echo "PNPM12_BIN=${{ steps.pnpm12.outputs.dest }}/pnpm" >> "$GITHUB_ENV"
805842
- name: Install pnpm
806843
if: contains(steps.pm.outputs.list, 'pnpm')
807844
uses: pnpm/action-setup@v4
@@ -825,7 +862,11 @@ jobs:
825862
hyperfine --version
826863
IFS=',' read -ra PMS <<< "${{ steps.pm.outputs.list }}"
827864
for pm in "${PMS[@]}"; do
828-
echo "$pm: $($pm --version 2>&1 || echo 'not installed')"
865+
if [ "$pm" = "pnpm12" ]; then
866+
echo "$pm: $(env PNPM_CONFIG_PM_ON_FAIL=ignore "$PNPM12_BIN" --version 2>&1 || echo 'not installed')"
867+
else
868+
echo "$pm: $($pm --version 2>&1 || echo 'not installed')"
869+
fi
829870
done
830871
- name: Run benchmark
831872
run: |
@@ -865,9 +906,20 @@ jobs:
865906
run: |
866907
PM_LIST="${{ github.event.inputs.pm_list || 'utoo,bun' }}"
867908
if [ "$PM_LIST" = "all" ]; then
868-
PM_LIST="utoo,pnpm,yarn,bun,aube"
909+
PM_LIST="utoo,pnpm,pnpm12,yarn,bun,aube"
869910
fi
870911
echo "list=$PM_LIST" >> $GITHUB_OUTPUT
912+
- name: Install pnpm 12
913+
id: pnpm12
914+
if: contains(steps.pm.outputs.list, 'pnpm12')
915+
uses: pnpm/setup@v2
916+
with:
917+
version: next-12
918+
dest: ~/setup-pnpm12
919+
install: false
920+
- name: Expose pnpm 12 binary
921+
if: contains(steps.pm.outputs.list, 'pnpm12')
922+
run: echo "PNPM12_BIN=${{ steps.pnpm12.outputs.dest }}/pnpm" >> "$GITHUB_ENV"
871923
- name: Install pnpm
872924
if: contains(steps.pm.outputs.list, 'pnpm')
873925
uses: pnpm/action-setup@v4
@@ -891,7 +943,11 @@ jobs:
891943
hyperfine --version
892944
IFS=',' read -ra PMS <<< "${{ steps.pm.outputs.list }}"
893945
for pm in "${PMS[@]}"; do
894-
echo "$pm: $($pm --version 2>&1 || echo 'not installed')"
946+
if [ "$pm" = "pnpm12" ]; then
947+
echo "$pm: $(env PNPM_CONFIG_PM_ON_FAIL=ignore "$PNPM12_BIN" --version 2>&1 || echo 'not installed')"
948+
else
949+
echo "$pm: $($pm --version 2>&1 || echo 'not installed')"
950+
fi
895951
done
896952
- name: Run benchmark
897953
run: |

bench/pm-bench-phases.sh

Lines changed: 31 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ if [ -n "${UTOO_ALT_ENV:-}" ] && [[ ",${PM_LIST:-}," != *",utoo-alt,"* ]]; then
2020
PM_LIST="${PM_LIST:+$PM_LIST,}utoo-alt"
2121
fi
2222
IFS=',' read -ra PACKAGE_MANAGERS <<< "${PM_LIST:-utoo,bun}"
23+
PNPM_BIN=${PNPM_BIN:-pnpm}
24+
PNPM12_BIN=${PNPM12_BIN:-pnpm12}
2325

2426
BENCH_DIR=${BENCH_DIR:-/tmp/pm-bench}
2527
RESULTS_DIR=${RESULTS_DIR:-/tmp/pm-bench-results}
@@ -35,6 +37,7 @@ UTOO_ALT_CACHE="${UTOO_ALT_CACHE:-/tmp/utoo-alt-bench-cache}"
3537
BUN_CACHE="${BUN_CACHE:-/tmp/bun-bench-cache}"
3638
export BUN_INSTALL_CACHE_DIR="$BUN_CACHE"
3739
PNPM_STORE="${PNPM_STORE:-/tmp/pnpm-bench-store}"
40+
PNPM12_STORE="${PNPM12_STORE:-/tmp/pnpm12-bench-store}"
3841
AUBE_DATA="${AUBE_DATA:-/tmp/aube-bench-data}"
3942
AUBE_CACHE="${AUBE_CACHE:-/tmp/aube-bench-cache}"
4043

@@ -177,6 +180,7 @@ capture_footprint() {
177180
utoo-alt) cache=$UTOO_ALT_CACHE ;;
178181
bun) cache=$BUN_CACHE ;;
179182
pnpm) cache=$PNPM_STORE ;;
183+
pnpm12) cache=$PNPM12_STORE ;;
180184
aube) cache="$AUBE_DATA $AUBE_CACHE" ;;
181185
esac
182186
printf '{"cache":%d,"node_modules":%d,"lockfile":%d}\n' \
@@ -197,7 +201,7 @@ PROJECT_DIR="$BENCH_DIR/$PROJECT"
197201
# aube consumes pnpm-style workspace metadata. Synthesize the same workspace
198202
# list used by the legacy comparison, and disable its provenance downgrade
199203
# policy so the benchmark measures installation rather than policy rejection.
200-
if printf '%s\n' "${PACKAGE_MANAGERS[@]}" | grep -Eq '^(aube|pnpm)$'; then
204+
if printf '%s\n' "${PACKAGE_MANAGERS[@]}" | grep -Eq '^(aube|pnpm|pnpm12)$'; then
201205
node - "$PROJECT_DIR" <<'NODE'
202206
const fs = require("fs");
203207
const dir = process.argv[2];
@@ -228,6 +232,7 @@ write_prepare() {
228232
utoo-alt) cache=$UTOO_ALT_CACHE ;;
229233
bun) cache=$BUN_CACHE ;;
230234
pnpm) cache=$PNPM_STORE ;;
235+
pnpm12) cache=$PNPM12_STORE ;;
231236
aube) cache="$AUBE_DATA $AUBE_CACHE" ;;
232237
esac
233238

@@ -250,15 +255,15 @@ EOF
250255
# Phase 0: full cold install — nothing reused. Lockfile + all caches wiped.
251256
cat >> "$path" <<EOF
252257
rm -f package-lock.json bun.lock aube-lock.yaml yarn.lock pnpm-lock.yaml
253-
rm -rf "$UTOO_CACHE" "$UTOO_NPM_CACHE" "$UTOO_NEXT_CACHE" "$UTOO_ALT_CACHE" "$BUN_CACHE" "$PNPM_STORE" "$AUBE_DATA" "$AUBE_CACHE"
258+
rm -rf "$UTOO_CACHE" "$UTOO_NPM_CACHE" "$UTOO_NEXT_CACHE" "$UTOO_ALT_CACHE" "$BUN_CACHE" "$PNPM_STORE" "$PNPM12_STORE" "$AUBE_DATA" "$AUBE_CACHE"
254259
echo "[prep] phase 0 $pm: full cold (lockfile + caches + node_modules wiped)"
255260
EOF
256261
;;
257262
p1_*)
258263
# Phase 1: cold resolve — wipe lockfiles AND caches so nothing can be reused.
259264
cat >> "$path" <<EOF
260265
rm -f package-lock.json bun.lock aube-lock.yaml yarn.lock pnpm-lock.yaml
261-
rm -rf "$UTOO_CACHE" "$UTOO_NPM_CACHE" "$UTOO_NEXT_CACHE" "$UTOO_ALT_CACHE" "$BUN_CACHE" "$PNPM_STORE" "$AUBE_DATA" "$AUBE_CACHE"
266+
rm -rf "$UTOO_CACHE" "$UTOO_NPM_CACHE" "$UTOO_NEXT_CACHE" "$UTOO_ALT_CACHE" "$BUN_CACHE" "$PNPM_STORE" "$PNPM12_STORE" "$AUBE_DATA" "$AUBE_CACHE"
262267
echo "[prep] phase 1 $pm: cleaned lockfiles + caches + node_modules"
263268
EOF
264269
;;
@@ -274,11 +279,11 @@ rm -rf "$BUN_CACHE"
274279
echo "[prep] phase 3 bun: restored bun.lock, wiped $BUN_CACHE"
275280
EOF
276281
;;
277-
pnpm) cat >> "$path" <<EOF
282+
pnpm|pnpm12) cat >> "$path" <<EOF
278283
rm -f package-lock.json bun.lock aube-lock.yaml yarn.lock
279-
cp -f "$LOCK_STASH/pnpm-lock.yaml" pnpm-lock.yaml
280-
rm -rf "$PNPM_STORE"
281-
echo "[prep] phase 3 pnpm: restored pnpm-lock.yaml, wiped $PNPM_STORE"
284+
cp -f "$LOCK_STASH/${pm}-lock.yaml" pnpm-lock.yaml
285+
rm -rf "$cache"
286+
echo "[prep] phase 3 $pm: restored ${pm}-lock.yaml, wiped $cache"
282287
EOF
283288
;;
284289
aube) cat >> "$path" <<EOF
@@ -306,10 +311,10 @@ cp -f "$LOCK_STASH/bun.lock" bun.lock
306311
echo "[prep] phase 4 bun: restored bun.lock, kept cache"
307312
EOF
308313
;;
309-
pnpm) cat >> "$path" <<EOF
314+
pnpm|pnpm12) cat >> "$path" <<EOF
310315
rm -f package-lock.json bun.lock aube-lock.yaml yarn.lock
311-
cp -f "$LOCK_STASH/pnpm-lock.yaml" pnpm-lock.yaml
312-
echo "[prep] phase 4 pnpm: restored pnpm-lock.yaml, kept store"
316+
cp -f "$LOCK_STASH/${pm}-lock.yaml" pnpm-lock.yaml
317+
echo "[prep] phase 4 $pm: restored ${pm}-lock.yaml, kept store"
313318
EOF
314319
;;
315320
aube) cat >> "$path" <<EOF
@@ -380,17 +385,19 @@ seed_for_phase() {
380385
fi
381386
[ -f bun.lock ] && cp -f bun.lock "$LOCK_STASH/bun.lock"
382387
;;
383-
p3_*:pnpm|p4_*:pnpm)
384-
if [ ! -f pnpm-lock.yaml ] && [ ! -f "$LOCK_STASH/pnpm-lock.yaml" ]; then
388+
p3_*:pnpm|p4_*:pnpm|p3_*:pnpm12|p4_*:pnpm12)
389+
local pnpm_lock_stash="$LOCK_STASH/${pm}-lock.yaml"
390+
if [ ! -f "$pnpm_lock_stash" ]; then
385391
# pnpm's lockfile-only path is not equivalent to its install path for
386392
# every real-world workspace. Seed with a full untimed install, then
387393
# p3's prepare deletes the store while p4 intentionally keeps it.
388-
echo -e " ${CYAN}seed: running full pnpm install to generate pnpm-lock.yaml/store${NC}"
394+
echo -e " ${CYAN}seed: running full $pm install to generate pnpm-lock.yaml/store${NC}"
389395
rm -f package-lock.json bun.lock aube-lock.yaml
396+
rm -f pnpm-lock.yaml
390397
rm -rf node_modules
391-
retry 3 bash -c "$(install_cmd pnpm) >> '$seed_log' 2>&1" || return 1
398+
retry 3 bash -c "$(install_cmd "$pm") >> '$seed_log' 2>&1" || return 1
399+
[ -f pnpm-lock.yaml ] && cp -f pnpm-lock.yaml "$pnpm_lock_stash"
392400
fi
393-
[ -f pnpm-lock.yaml ] && cp -f pnpm-lock.yaml "$LOCK_STASH/pnpm-lock.yaml"
394401
;;
395402
p3_*:aube|p4_*:aube)
396403
if [ ! -f aube-lock.yaml ] && [ ! -f "$LOCK_STASH/aube-lock.yaml" ]; then
@@ -408,7 +415,7 @@ seed_for_phase() {
408415
local expected_lock
409416
case "$pm" in
410417
bun) expected_lock="$LOCK_STASH/bun.lock" ;;
411-
pnpm) expected_lock="$LOCK_STASH/pnpm-lock.yaml" ;;
418+
pnpm|pnpm12) expected_lock="$LOCK_STASH/${pm}-lock.yaml" ;;
412419
aube) expected_lock="$LOCK_STASH/aube-lock.yaml" ;;
413420
*) expected_lock="$LOCK_STASH/package-lock.json" ;;
414421
esac
@@ -427,6 +434,7 @@ seed_for_phase() {
427434
utoo-alt) cache=$UTOO_ALT_CACHE ;;
428435
bun) cache=$BUN_CACHE ;;
429436
pnpm) cache=$PNPM_STORE ;;
437+
pnpm12) cache=$PNPM12_STORE ;;
430438
aube) cache=$AUBE_DATA ;;
431439
esac
432440
if [ ! -d "$cache" ] || [ -z "$(ls -A "$cache" 2>/dev/null)" ]; then
@@ -437,9 +445,9 @@ seed_for_phase() {
437445
if [ "$pm" = bun ]; then
438446
rm -f package-lock.json
439447
[ -f "$LOCK_STASH/bun.lock" ] && cp -f "$LOCK_STASH/bun.lock" bun.lock
440-
elif [ "$pm" = pnpm ]; then
448+
elif [ "$pm" = pnpm ] || [ "$pm" = pnpm12 ]; then
441449
rm -f package-lock.json bun.lock aube-lock.yaml
442-
[ -f "$LOCK_STASH/pnpm-lock.yaml" ] && cp -f "$LOCK_STASH/pnpm-lock.yaml" pnpm-lock.yaml
450+
[ -f "$LOCK_STASH/${pm}-lock.yaml" ] && cp -f "$LOCK_STASH/${pm}-lock.yaml" pnpm-lock.yaml
443451
elif [ "$pm" = aube ]; then
444452
rm -f package-lock.json bun.lock
445453
[ -f "$LOCK_STASH/aube-lock.yaml" ] && cp -f "$LOCK_STASH/aube-lock.yaml" aube-lock.yaml
@@ -462,7 +470,8 @@ install_cmd() {
462470
bun) echo "bun install --ignore-scripts --registry=$REGISTRY" ;;
463471
# Some npm projects set `package-lock=false` in .npmrc. pnpm maps that to
464472
# its own lockfile switch, so force it back on for lock-based p3/p4 parity.
465-
pnpm) echo "pnpm install --ignore-scripts --no-frozen-lockfile --config.lockfile=true --config.package-manager-strict=false --registry=$REGISTRY --store-dir=$PNPM_STORE" ;;
473+
pnpm) echo "env PNPM_CONFIG_PM_ON_FAIL=ignore $PNPM_BIN install --ignore-scripts --no-frozen-lockfile --config.lockfile=true --registry=$REGISTRY --store-dir=$PNPM_STORE" ;;
474+
pnpm12) echo "env PNPM_CONFIG_PM_ON_FAIL=ignore $PNPM12_BIN install --ignore-scripts --no-frozen-lockfile --config.lockfile=true --registry=$REGISTRY --store-dir=$PNPM12_STORE" ;;
466475
aube) echo "env XDG_DATA_HOME=$AUBE_DATA XDG_CACHE_HOME=$AUBE_CACHE NPM_CONFIG_REGISTRY=$REGISTRY aube install --ignore-scripts --reporter silent" ;;
467476
esac
468477
}
@@ -474,7 +483,8 @@ resolve_cmd() {
474483
utoo-next) echo "$UTOO_NEXT_BIN deps --registry=$REGISTRY --cache-dir=$UTOO_NEXT_CACHE" ;;
475484
utoo-alt) echo "env $UTOO_ALT_ENV utoo deps --registry=$REGISTRY --cache-dir=$UTOO_ALT_CACHE" ;;
476485
bun) echo "bun install --lockfile-only --registry=$REGISTRY" ;;
477-
pnpm) echo "pnpm install --lockfile-only --ignore-scripts --config.lockfile=true --config.package-manager-strict=false --registry=$REGISTRY --store-dir=$PNPM_STORE" ;;
486+
pnpm) echo "env PNPM_CONFIG_PM_ON_FAIL=ignore $PNPM_BIN install --lockfile-only --ignore-scripts --config.lockfile=true --registry=$REGISTRY --store-dir=$PNPM_STORE" ;;
487+
pnpm12) echo "env PNPM_CONFIG_PM_ON_FAIL=ignore $PNPM12_BIN install --lockfile-only --ignore-scripts --config.lockfile=true --registry=$REGISTRY --store-dir=$PNPM12_STORE" ;;
478488
aube) echo "env XDG_DATA_HOME=$AUBE_DATA XDG_CACHE_HOME=$AUBE_CACHE NPM_CONFIG_REGISTRY=$REGISTRY aube install --lockfile-only --ignore-scripts --reporter silent" ;;
479489
esac
480490
}
@@ -515,7 +525,7 @@ run_phase_matrix() {
515525
done
516526
[ ${#live_pms[@]} -eq 0 ] && return 0
517527

518-
export PROJECT_DIR UTOO_CACHE BUN_CACHE PNPM_STORE AUBE_DATA AUBE_CACHE
528+
export PROJECT_DIR UTOO_CACHE BUN_CACHE PNPM_STORE PNPM12_STORE AUBE_DATA AUBE_CACHE
519529

520530
# Untimed warmup round: DNS resolver cached, TLS session ticket primed,
521531
# CDN edge POP populated per PM before any timed window opens.

0 commit comments

Comments
 (0)