diff --git a/.github/workflows/crabbox-hydrate.yml b/.github/workflows/crabbox-hydrate.yml index b58b58bf5509..862ed16e048f 100644 --- a/.github/workflows/crabbox-hydrate.yml +++ b/.github/workflows/crabbox-hydrate.yml @@ -86,6 +86,7 @@ jobs: echo "PNPM_HOME=$PNPM_HOME" echo "PNPM_CONFIG_STORE_DIR=$PNPM_CONFIG_STORE_DIR" echo "PNPM_CONFIG_MODULES_DIR=$PNPM_CONFIG_MODULES_DIR" + echo "CRABBOX_PNPM_MODULES_DIR=$PNPM_CONFIG_MODULES_DIR" echo "PNPM_CONFIG_PACKAGE_IMPORT_METHOD=${PNPM_CONFIG_PACKAGE_IMPORT_METHOD:-}" echo "PNPM_CONFIG_VIRTUAL_STORE_DIR=$PNPM_CONFIG_VIRTUAL_STORE_DIR" } >> "$GITHUB_ENV" @@ -388,7 +389,9 @@ jobs: fi } { - for key in CI GITHUB_ACTIONS GITHUB_WORKSPACE GITHUB_REPOSITORY GITHUB_RUN_ID GITHUB_RUN_NUMBER GITHUB_RUN_ATTEMPT GITHUB_REF GITHUB_REF_NAME GITHUB_SHA GITHUB_EVENT_NAME GITHUB_ACTOR RUNNER_OS RUNNER_ARCH RUNNER_TEMP RUNNER_TOOL_CACHE XDG_CACHE_HOME COREPACK_HOME NODE_BIN OPENCLAW_CRABBOX_DOCKER_AVAILABLE PNPM_HOME PNPM_CONFIG_CHILD_CONCURRENCY PNPM_CONFIG_MODULES_DIR PNPM_CONFIG_NETWORK_CONCURRENCY PNPM_CONFIG_PACKAGE_IMPORT_METHOD PNPM_CONFIG_STORE_DIR PNPM_CONFIG_VERIFY_DEPS_BEFORE_RUN PNPM_CONFIG_VIRTUAL_STORE_DIR PATH; do + # Keep workspace paths out of PNPM_CONFIG_* in the reusable shell. pnpm dlx owns a + # separate project and would otherwise overwrite this workspace's current lockfile. + for key in CI GITHUB_ACTIONS GITHUB_WORKSPACE GITHUB_REPOSITORY GITHUB_RUN_ID GITHUB_RUN_NUMBER GITHUB_RUN_ATTEMPT GITHUB_REF GITHUB_REF_NAME GITHUB_SHA GITHUB_EVENT_NAME GITHUB_ACTOR RUNNER_OS RUNNER_ARCH RUNNER_TEMP RUNNER_TOOL_CACHE XDG_CACHE_HOME COREPACK_HOME NODE_BIN OPENCLAW_CRABBOX_DOCKER_AVAILABLE CRABBOX_PNPM_MODULES_DIR PNPM_HOME PNPM_CONFIG_CHILD_CONCURRENCY PNPM_CONFIG_NETWORK_CONCURRENCY PNPM_CONFIG_PACKAGE_IMPORT_METHOD PNPM_CONFIG_STORE_DIR PNPM_CONFIG_VERIFY_DEPS_BEFORE_RUN PATH; do write_export "$key" done } > "${env_file}.tmp" @@ -514,6 +517,7 @@ jobs: "PNPM_HOME=$env:PNPM_HOME" "PNPM_CONFIG_STORE_DIR=$env:PNPM_CONFIG_STORE_DIR" "PNPM_CONFIG_MODULES_DIR=$env:PNPM_CONFIG_MODULES_DIR" + "CRABBOX_PNPM_MODULES_DIR=$env:PNPM_CONFIG_MODULES_DIR" "PNPM_CONFIG_VIRTUAL_STORE_DIR=$env:PNPM_CONFIG_VIRTUAL_STORE_DIR" "PNPM_CONFIG_CHILD_CONCURRENCY=$env:PNPM_CONFIG_CHILD_CONCURRENCY" "PNPM_CONFIG_NETWORK_CONCURRENCY=$env:PNPM_CONFIG_NETWORK_CONCURRENCY" @@ -614,10 +618,9 @@ jobs: "GITHUB_REF", "GITHUB_REF_NAME", "GITHUB_SHA", "GITHUB_EVENT_NAME", "GITHUB_ACTOR", "RUNNER_OS", "RUNNER_ARCH", "RUNNER_TEMP", "RUNNER_TOOL_CACHE", "XDG_CACHE_HOME", "COREPACK_HOME", "NODE_BIN", - "PNPM_HOME", "PNPM_CONFIG_CHILD_CONCURRENCY", "PNPM_CONFIG_MODULES_DIR", + "PNPM_HOME", "CRABBOX_PNPM_MODULES_DIR", "PNPM_CONFIG_CHILD_CONCURRENCY", "PNPM_CONFIG_NETWORK_CONCURRENCY", "PNPM_CONFIG_STORE_DIR", - "PNPM_CONFIG_VERIFY_DEPS_BEFORE_RUN", "PNPM_CONFIG_VIRTUAL_STORE_DIR", - "PNPM_CONFIG_SIDE_EFFECTS_CACHE", "PATH" + "PNPM_CONFIG_VERIFY_DEPS_BEFORE_RUN", "PNPM_CONFIG_SIDE_EFFECTS_CACHE", "PATH" ) $envLines = foreach ($key in $keys) { $value = [Environment]::GetEnvironmentVariable($key) diff --git a/scripts/crabbox-wrapper.mts b/scripts/crabbox-wrapper.mts index cdd8c7e35395..9836697b7db1 100755 --- a/scripts/crabbox-wrapper.mts +++ b/scripts/crabbox-wrapper.mts @@ -2643,9 +2643,9 @@ function isHydratedNativeWindowsProvider(providerName: string) { function remoteWindowsHydratedNodeModulesBootstrap() { return [ - "$openclawModulesDir = $env:PNPM_CONFIG_MODULES_DIR", + "$openclawModulesDir = if ($env:CRABBOX_PNPM_MODULES_DIR) { $env:CRABBOX_PNPM_MODULES_DIR } else { $env:PNPM_CONFIG_MODULES_DIR }", "if ($openclawModulesDir) {", - 'if (-not (Test-Path $openclawModulesDir)) { throw "PNPM_CONFIG_MODULES_DIR does not exist: $openclawModulesDir" }', + 'if (-not (Test-Path $openclawModulesDir)) { throw "hydrated pnpm modules directory does not exist: $openclawModulesDir" }', '$openclawWorkspaceModules = Join-Path (Get-Location).Path "node_modules"', '$openclawSelfModules = Join-Path $openclawModulesDir "node_modules"', 'if (-not (Test-Path $openclawSelfModules)) { cmd /c mklink /J "$openclawSelfModules" "$openclawModulesDir" | Out-Host; if ($LASTEXITCODE -ne 0) { throw "failed to link hydrated pnpm node_modules" } }', @@ -2657,7 +2657,7 @@ function remoteWindowsHydratedNodeModulesBootstrap() { function remotePosixHydratedNodeModulesBootstrap() { // Knip and other non-pnpm tools walk node_modules, while hydrated boxes keep it external. // Without this link, dead-code scans silently lose consumer edges and report false positives. - return 'if [ -n "${PNPM_CONFIG_MODULES_DIR:-}" ] && [ -d "$PNPM_CONFIG_MODULES_DIR" ] && [ ! -e node_modules ]; then ln -s "$PNPM_CONFIG_MODULES_DIR" node_modules; fi;'; + return 'openclaw_modules_dir="${CRABBOX_PNPM_MODULES_DIR:-${PNPM_CONFIG_MODULES_DIR:-}}"; if [ -n "$openclaw_modules_dir" ] && [ -d "$openclaw_modules_dir" ] && [ ! -e node_modules ]; then ln -s "$openclaw_modules_dir" node_modules; fi;'; } function injectRemoteWindowsHydratedNodeModulesBootstrap( diff --git a/test/scripts/crabbox-wrapper.test.ts b/test/scripts/crabbox-wrapper.test.ts index 4cb3fdfe829c..0152292ac19f 100644 --- a/test/scripts/crabbox-wrapper.test.ts +++ b/test/scripts/crabbox-wrapper.test.ts @@ -583,7 +583,10 @@ function runSuccessfulNativeWindows( function expectHydratedWindowsShell(run: ParsedWrapperRun, command: string): void { expect(run.output.args).toContain("--shell"); - expect(run.remoteCommand).toContain("$openclawModulesDir = $env:PNPM_CONFIG_MODULES_DIR"); + expect(run.remoteCommand).toContain( + "$env:CRABBOX_PNPM_MODULES_DIR) { $env:CRABBOX_PNPM_MODULES_DIR } else { $env:PNPM_CONFIG_MODULES_DIR }", + ); + expect(run.remoteCommand).toContain("hydrated pnpm modules directory does not exist"); expect(run.remoteCommand).toContain('mklink /J "$openclawSelfModules" "$openclawModulesDir"'); expect(run.remoteCommand).toContain( 'mklink /J "$openclawWorkspaceModules" "$openclawModulesDir"', @@ -592,7 +595,7 @@ function expectHydratedWindowsShell(run: ParsedWrapperRun, command: string): voi } const remotePosixHydratedModulesBootstrap = - 'if [ -n "${PNPM_CONFIG_MODULES_DIR:-}" ] && [ -d "$PNPM_CONFIG_MODULES_DIR" ] && [ ! -e node_modules ]; then ln -s "$PNPM_CONFIG_MODULES_DIR" node_modules; fi;'; + 'openclaw_modules_dir="${CRABBOX_PNPM_MODULES_DIR:-${PNPM_CONFIG_MODULES_DIR:-}}"; if [ -n "$openclaw_modules_dir" ] && [ -d "$openclaw_modules_dir" ] && [ ! -e node_modules ]; then ln -s "$openclaw_modules_dir" node_modules; fi;'; function expectHydratedPosixShell( run: Pick, @@ -3033,7 +3036,7 @@ describe("scripts/crabbox-wrapper", () => { "test", ]); expect(output.args).toContain("--shell"); - expect(remoteCommand).toContain("$openclawModulesDir = $env:PNPM_CONFIG_MODULES_DIR"); + expect(remoteCommand).toContain("$env:CRABBOX_PNPM_MODULES_DIR"); expect(remoteCommand).toContain("pnpm --filter '@openclaw/discord' test"); }); diff --git a/test/scripts/package-acceptance-workflow.test.ts b/test/scripts/package-acceptance-workflow.test.ts index 3f83413c2fcf..f2d690e8f36a 100644 --- a/test/scripts/package-acceptance-workflow.test.ts +++ b/test/scripts/package-acceptance-workflow.test.ts @@ -1563,6 +1563,7 @@ describe("package acceptance workflow", () => { ); expect(hydratePnpm.run).toContain('echo "PNPM_CONFIG_STORE_DIR=$PNPM_CONFIG_STORE_DIR"'); expect(hydratePnpm.run).toContain('echo "PNPM_CONFIG_MODULES_DIR=$PNPM_CONFIG_MODULES_DIR"'); + expect(hydratePnpm.run).toContain('echo "CRABBOX_PNPM_MODULES_DIR=$PNPM_CONFIG_MODULES_DIR"'); expect(hydratePnpm.run).toContain( 'echo "PNPM_CONFIG_PACKAGE_IMPORT_METHOD=${PNPM_CONFIG_PACKAGE_IMPORT_METHOD:-}"', ); @@ -1647,7 +1648,10 @@ describe("package acceptance workflow", () => { const markCrabboxReady = workflowStep(hydrate, "Mark Crabbox ready").run; expect(markCrabboxReady).toContain("COREPACK_HOME"); expect(markCrabboxReady).toContain("OPENCLAW_CRABBOX_DOCKER_AVAILABLE"); + expect(markCrabboxReady).toContain("CRABBOX_PNPM_MODULES_DIR"); expect(markCrabboxReady).toContain("PNPM_CONFIG_PACKAGE_IMPORT_METHOD"); + expect(markCrabboxReady).not.toContain("PNPM_CONFIG_MODULES_DIR"); + expect(markCrabboxReady).not.toContain("PNPM_CONFIG_VIRTUAL_STORE_DIR"); expect(workflowStep(hydrate, "Hydrate provider env helper").env).toBeUndefined(); expect(hydrateWindowsDaemon.if).toBe("${{ inputs.crabbox_job == 'hydrate-windows-daemon' }}"); @@ -1705,9 +1709,13 @@ describe("package acceptance workflow", () => { ); expect(hydrateWindowsFetch.run).toContain('"+refs/heads/main:refs/remotes/origin/main"'); expect(workflowStep(hydrateWindowsDaemon, "Mark Crabbox ready").shell).toBe("powershell"); - expect(workflowStep(hydrateWindowsDaemon, "Mark Crabbox ready").run).toContain('"NODE_BIN"'); - expect(workflowStep(hydrateWindowsDaemon, "Mark Crabbox ready").run).toContain('"PNPM_HOME"'); - expect(workflowStep(hydrateWindowsDaemon, "Mark Crabbox ready").run).toContain('"PATH"'); + const markWindowsCrabboxReady = workflowStep(hydrateWindowsDaemon, "Mark Crabbox ready").run; + expect(markWindowsCrabboxReady).toContain('"NODE_BIN"'); + expect(markWindowsCrabboxReady).toContain('"PNPM_HOME"'); + expect(markWindowsCrabboxReady).toContain('"CRABBOX_PNPM_MODULES_DIR"'); + expect(markWindowsCrabboxReady).not.toContain('"PNPM_CONFIG_MODULES_DIR"'); + expect(markWindowsCrabboxReady).not.toContain('"PNPM_CONFIG_VIRTUAL_STORE_DIR"'); + expect(markWindowsCrabboxReady).toContain('"PATH"'); expect(workflowText).toContain("OPENCLAW_CRABBOX_HYDRATE_DOWNLOAD_TIMEOUT_SECONDS:-300"); expect(workflowText).toContain("OPENCLAW_CRABBOX_HYDRATE_DOWNLOAD_RETRIES:-3"); expect(workflowText).toContain("--retry-all-errors");