From cfddc6b4bbc56029ad1ee7a85e22dcd596ac95ee Mon Sep 17 00:00:00 2001 From: AstroHan Date: Sat, 1 Aug 2026 17:05:52 +0800 Subject: [PATCH] fix(ci): provision sandbox whenever the cli workspace runs packages/cli/src/__tests__/runtime-bootstrap.test.ts executes real sandboxed shell tools, so the bubblewrap + user-namespace setup is required whenever the cli workspace is selected through the dependency closure, not only for direct cli/runtime edits. A storage-only change (storage -> runtime -> headless -> cli) or a headless change (headless -> cli) selected the cli tests without provisioning the sandbox, failing with 'Command sandbox is required but unavailable'. runtimeSandbox now keys off workspaces.includes('packages/cli') instead of directWorkspaces; regression test covers both closure paths. --- scripts/ci-test-plan.mjs | 13 ++++++------- scripts/ci-test-plan.test.mjs | 14 ++++++++++++++ 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/scripts/ci-test-plan.mjs b/scripts/ci-test-plan.mjs index 6cd4a3c08d..6ca38cd28b 100644 --- a/scripts/ci-test-plan.mjs +++ b/scripts/ci-test-plan.mjs @@ -101,7 +101,7 @@ export function planTests(changedFiles, options = {}) { e2e: true, full: true, headless: graph.dirs.includes('packages/headless'), - runtimeSandbox: graph.dirs.includes('packages/runtime'), + runtimeSandbox: graph.dirs.includes('packages/cli'), scriptMode: 'full', storageStress: graph.dirs.includes('packages/storage'), workspaces: [...graph.dirs], @@ -159,12 +159,11 @@ export function planTests(changedFiles, options = {}) { e2e: directWorkspaces.has('apps/desktop') || directWorkspaces.has('packages/ui'), full: false, headless: workspaces.includes('packages/headless'), - runtimeSandbox: - directWorkspaces.has('packages/runtime') || - // runtime-bootstrap.test.ts (packages/cli) executes real sandboxed shell - // tools, so it needs the bubblewrap + user-namespace setup even though - // the change is scoped to the cli workspace. - files.some((path) => path === 'packages/cli/src/__tests__/runtime-bootstrap.test.ts'), + // packages/cli/src/__tests__/runtime-bootstrap.test.ts executes real sandboxed + // shell tools, so the bubblewrap + user-namespace setup is required whenever + // the cli workspace runs in the dependency closure, not only for direct + // cli/runtime edits (e.g. a storage-only change still selects cli via runtime). + runtimeSandbox: workspaces.includes('packages/cli'), scriptMode, storageStress, workspaces, diff --git a/scripts/ci-test-plan.test.mjs b/scripts/ci-test-plan.test.mjs index e0e390249e..0391dc597c 100644 --- a/scripts/ci-test-plan.test.mjs +++ b/scripts/ci-test-plan.test.mjs @@ -32,6 +32,20 @@ test('stress and specialized script checks run only for their owning surfaces', assert.deepEqual(measurement.workspaces, []); }); +test('sandbox is flagged whenever the cli workspace runs in the closure', () => { + // packages/cli/src/__tests__/runtime-bootstrap.test.ts executes real sandboxed + // shell tools, so any change whose dependency closure selects packages/cli must + // provision the sandbox — not only direct cli/runtime edits. + for (const path of [ + 'packages/cli/src/__tests__/runtime-bootstrap.test.ts', + 'packages/runtime/src/shell-tools.ts', + 'packages/storage/src/session-store.ts', + 'packages/headless/src/cell-output.ts', + ]) { + assert.equal(planTests([path], { graph }).runtimeSandbox, true, path); + } +}); + test('global and unknown production changes fail safe to the complete suite', () => { for (const path of ['package-lock.json', 'scripts/ci-test-plan.mjs', 'new-root/file.ts']) { const plan = planTests([path], { graph });