chore(build,test): split desktop/mobile from default build; share vitest worker budget

- pnpm build now excludes @fusion/desktop and @fusion/mobile by default
  (recursive build still available as pnpm build:all). Saves time on
  workspace-wide builds that don't need the native shells.
- Hoist the per-package max-worker computation into a shared
  packages/core/src/__test-utils__/vitest-workers.ts util. Every
  vitest.config.ts now calls computeMaxWorkers(), which honors
  VITEST_MAX_WORKERS, FUSION_TEST_TOTAL_WORKERS, and a per-config
  defaultCap, clamped to cpus-1.
- pnpm test sets VITEST_MAX_WORKERS=2 so the workspace run keeps total
  fan-out modest with --workspace-concurrency=2.
- Switch dashboard vitest pool from forks to threads so jsdom/React
  suites share a V8 heap instead of duplicating ~500MB per worker.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
gsxdsm
2026-05-02 15:56:15 -07:00
parent cea3f831bc
commit 7a2da530b9
18 changed files with 90 additions and 79 deletions

View File

@@ -1,15 +1,8 @@
import { defineConfig } from "vitest/config";
import { resolve } from "node:path";
import { cpus } from "node:os";
import { computeMaxWorkers } from "../core/src/__test-utils__/vitest-workers";
// Cap fan-out to 4 so high-core dev machines don't spawn 27+ workers per
// package — that saturates the box when multiple workspace packages test
// concurrently or when the dashboard has agents running tests in parallel.
// Override with VITEST_MAX_WORKERS for explicit fast/serial runs.
const defaultMaxWorkers = Math.min(4, Math.max(1, cpus().length - 1));
const requestedMaxWorkers = Number.parseInt(process.env.VITEST_MAX_WORKERS ?? String(defaultMaxWorkers), 10);
const maxWorkers = Math.max(1, Number.isFinite(requestedMaxWorkers) ? requestedMaxWorkers : defaultMaxWorkers);
process.env.VITEST_MAX_WORKERS = String(maxWorkers);
const maxWorkers = computeMaxWorkers();
export default defineConfig({
resolve: {

View File

@@ -0,0 +1,45 @@
import { cpus } from "node:os";
interface ComputeMaxWorkersOptions {
defaultCap?: number;
}
// Shared worker-budget computation for every package's vitest.config.
//
// Resolution order:
// 1. VITEST_MAX_WORKERS — explicit per-run override, wins unconditionally.
// 2. FUSION_TEST_TOTAL_WORKERS — global budget across the workspace, divided
// by FUSION_TEST_CONCURRENCY (default 1). Lets `pnpm -r` runs cap total
// fan-out instead of multiplying per package.
// 3. defaultCap — small ceiling (2 by default) so a single package run on a
// high-core machine stays gentle.
// All paths clamp to (cpus - 1) so we never oversubscribe.
export function computeMaxWorkers(options: ComputeMaxWorkersOptions = {}): number {
const { defaultCap = 2 } = options;
const cpuCap = Math.max(1, cpus().length - 1);
const explicit = Number.parseInt(process.env.VITEST_MAX_WORKERS ?? "", 10);
if (Number.isFinite(explicit) && explicit > 0) {
const clamped = Math.min(Math.max(1, explicit), cpuCap);
process.env.VITEST_MAX_WORKERS = String(clamped);
return clamped;
}
const totalBudget = Number.parseInt(process.env.FUSION_TEST_TOTAL_WORKERS ?? "", 10);
const concurrency = Math.max(
1,
Number.parseInt(process.env.FUSION_TEST_CONCURRENCY ?? "1", 10) || 1,
);
let workers: number;
if (Number.isFinite(totalBudget) && totalBudget > 0) {
workers = Math.max(1, Math.floor(totalBudget / concurrency));
} else {
workers = defaultCap;
}
workers = Math.min(workers, cpuCap);
process.env.VITEST_MAX_WORKERS = String(workers);
return workers;
}

View File

@@ -1,14 +1,8 @@
import { defineConfig } from "vitest/config";
import { resolve } from "node:path";
import { cpus } from "node:os";
import { computeMaxWorkers } from "./src/__test-utils__/vitest-workers";
// Keep worker fan-out conservative by default. Over-subscribing threads can
// starve Vitest's worker RPC channel and trigger flaky "onTaskUpdate" timeouts
// under heavy SQLite test load.
const defaultMaxWorkers = Math.min(4, Math.max(1, cpus().length - 1));
const requestedMaxWorkers = Number.parseInt(process.env.VITEST_MAX_WORKERS ?? String(defaultMaxWorkers), 10);
const maxWorkers = Math.max(1, Number.isFinite(requestedMaxWorkers) ? requestedMaxWorkers : defaultMaxWorkers);
process.env.VITEST_MAX_WORKERS = String(maxWorkers);
const maxWorkers = computeMaxWorkers();
export default defineConfig({
resolve: {

View File

@@ -1,14 +1,9 @@
import { defineConfig } from "vitest/config";
import react from "@vitejs/plugin-react";
import { resolve } from "node:path";
import { cpus } from "node:os";
import { computeMaxWorkers } from "../core/src/__test-utils__/vitest-workers";
// Cap fan-out to 4 so high-core dev machines don't spawn 27+ workers per
// package — that saturates the box when workspace packages test concurrently.
const defaultMaxWorkers = Math.min(4, Math.max(1, cpus().length - 1));
const requestedMaxWorkers = Number.parseInt(process.env.VITEST_MAX_WORKERS ?? String(defaultMaxWorkers), 10);
const maxWorkers = Math.max(1, Number.isFinite(requestedMaxWorkers) ? requestedMaxWorkers : defaultMaxWorkers);
process.env.VITEST_MAX_WORKERS = String(maxWorkers);
const maxWorkers = computeMaxWorkers({ defaultCap: 1 });
export default defineConfig({
plugins: [react()],
@@ -33,9 +28,11 @@ export default defineConfig({
"./vitest.setup.ts",
],
globalSetup: [resolve(__dirname, "../core/src/__test-utils__/vitest-teardown.ts")],
pool: "forks",
// Threads share a V8 heap so they're much lighter than forks for jsdom +
// React suites; forks duplicated the entire renderer per worker (~500MB).
pool: "threads",
maxWorkers,
poolOptions: { forks: { minForks: 1, maxForks: maxWorkers } },
poolOptions: { threads: { minThreads: 1, maxThreads: maxWorkers } },
fileParallelism: true,
isolate: true,
// Dashboard route and integration-heavy suites can exceed the Vitest

View File

@@ -1,12 +1,8 @@
import { defineConfig } from "vitest/config";
import { cpus } from "node:os";
import { resolve } from "node:path";
import { computeMaxWorkers } from "../core/src/__test-utils__/vitest-workers";
// Cap fan-out to 4 to avoid saturating high-core machines under workspace concurrency.
const defaultMaxWorkers = Math.min(4, Math.max(1, cpus().length - 1));
const requestedMaxWorkers = Number.parseInt(process.env.VITEST_MAX_WORKERS ?? String(defaultMaxWorkers), 10);
const maxWorkers = Math.max(1, Number.isFinite(requestedMaxWorkers) ? requestedMaxWorkers : defaultMaxWorkers);
process.env.VITEST_MAX_WORKERS = String(maxWorkers);
const maxWorkers = computeMaxWorkers();
export default defineConfig({
test: {

View File

@@ -1,12 +1,8 @@
import { defineConfig } from "vitest/config";
import { resolve } from "node:path";
import { cpus } from "node:os";
import { computeMaxWorkers } from "../core/src/__test-utils__/vitest-workers";
// Cap fan-out to 4 to avoid saturating high-core machines under workspace concurrency.
const defaultMaxWorkers = Math.min(4, Math.max(1, cpus().length - 1));
const requestedMaxWorkers = Number.parseInt(process.env.VITEST_MAX_WORKERS ?? String(defaultMaxWorkers), 10);
const maxWorkers = Math.max(1, Number.isFinite(requestedMaxWorkers) ? requestedMaxWorkers : defaultMaxWorkers);
process.env.VITEST_MAX_WORKERS = String(maxWorkers);
const maxWorkers = computeMaxWorkers();
export default defineConfig({
resolve: {

View File

@@ -1,12 +1,8 @@
import { defineConfig } from "vitest/config";
import { cpus } from "node:os";
import { resolve } from "node:path";
import { computeMaxWorkers } from "../core/src/__test-utils__/vitest-workers";
// Cap fan-out to 4 to avoid saturating high-core machines under workspace concurrency.
const defaultMaxWorkers = Math.min(4, Math.max(1, cpus().length - 1));
const requestedMaxWorkers = Number.parseInt(process.env.VITEST_MAX_WORKERS ?? String(defaultMaxWorkers), 10);
const maxWorkers = Math.max(1, Number.isFinite(requestedMaxWorkers) ? requestedMaxWorkers : defaultMaxWorkers);
process.env.VITEST_MAX_WORKERS = String(maxWorkers);
const maxWorkers = computeMaxWorkers();
export default defineConfig({
test: {

View File

@@ -1,13 +1,9 @@
import { defineConfig } from "vitest/config";
import { cpus } from "node:os";
import { resolve } from "node:path";
import { fileURLToPath } from "node:url";
import { computeMaxWorkers } from "../core/src/__test-utils__/vitest-workers";
// Cap fan-out to 4 to avoid saturating high-core machines under workspace concurrency.
const defaultMaxWorkers = Math.min(4, Math.max(1, cpus().length - 1));
const requestedMaxWorkers = Number.parseInt(process.env.VITEST_MAX_WORKERS ?? String(defaultMaxWorkers), 10);
const maxWorkers = Math.max(1, Number.isFinite(requestedMaxWorkers) ? requestedMaxWorkers : defaultMaxWorkers);
process.env.VITEST_MAX_WORKERS = String(maxWorkers);
const maxWorkers = computeMaxWorkers();
export default defineConfig({
resolve: {