diff --git a/Dockerfile b/Dockerfile index 0cbab3c3f9b9..cb9593693b02 100644 --- a/Dockerfile +++ b/Dockerfile @@ -116,13 +116,16 @@ ARG OPENCLAW_BUNDLED_PLUGIN_DIR # workspace tree subset used during `pnpm install`. The build stage only copied # the root, `ui`, and opted-in plugin manifests into the install layer, so # prune must not rediscover unrelated workspaces from the later full source -# copy. -RUN printf 'packages:\n - .\n - ui\n' > /tmp/pnpm-workspace.runtime.yaml && \ +# copy. Restore the source workspace config after prune so runtime pnpm keeps +# patch metadata and package-manager policy. +RUN cp pnpm-workspace.yaml /tmp/pnpm-workspace.source.yaml && \ + printf 'packages:\n - .\n - ui\n' > /tmp/pnpm-workspace.runtime.yaml && \ for ext in $(printf '%s\n' "$OPENCLAW_EXTENSIONS" | tr ',' ' '); do \ printf ' - %s/%s\n' "$OPENCLAW_BUNDLED_PLUGIN_DIR" "$ext" >> /tmp/pnpm-workspace.runtime.yaml; \ done && \ cp /tmp/pnpm-workspace.runtime.yaml pnpm-workspace.yaml && \ CI=true NPM_CONFIG_FROZEN_LOCKFILE=false pnpm prune --prod && \ + cp /tmp/pnpm-workspace.source.yaml pnpm-workspace.yaml && \ node scripts/postinstall-bundled-plugins.mjs && \ OPENCLAW_EXTENSIONS="$OPENCLAW_EXTENSIONS" node scripts/prune-docker-plugin-dist.mjs && \ find dist -type f \( -name '*.d.ts' -o -name '*.d.mts' -o -name '*.d.cts' -o -name '*.map' \) -delete && \ diff --git a/src/dockerfile.test.ts b/src/dockerfile.test.ts index d91702edb724..5871dd67c22d 100644 --- a/src/dockerfile.test.ts +++ b/src/dockerfile.test.ts @@ -143,14 +143,29 @@ describe("Dockerfile", () => { }); it("keeps package manager patch files in runtime images", async () => { - const dockerfile = await readFile(dockerfilePath, "utf8"); + const dockerfile = collapseDockerContinuations(await readFile(dockerfilePath, "utf8")); const pnpmWorkspace = YAML.parse(await readFile(pnpmWorkspacePath, "utf8")) as { patchedDependencies?: Record; }; + const saveSourceWorkspace = "cp pnpm-workspace.yaml /tmp/pnpm-workspace.source.yaml"; + const usePruneWorkspace = "cp /tmp/pnpm-workspace.runtime.yaml pnpm-workspace.yaml"; + const pruneProd = "CI=true NPM_CONFIG_FROZEN_LOCKFILE=false pnpm prune --prod"; + const restoreSourceWorkspace = "cp /tmp/pnpm-workspace.source.yaml pnpm-workspace.yaml"; + const finalWorkspaceCopy = + "COPY --from=runtime-assets --chown=node:node /app/pnpm-workspace.yaml ."; expect(Object.keys(pnpmWorkspace.patchedDependencies ?? {})).not.toHaveLength(0); - expect(dockerfile).toContain( - "COPY --from=runtime-assets --chown=node:node /app/pnpm-workspace.yaml .", + expect(dockerfile).toContain(saveSourceWorkspace); + expect(dockerfile).toContain(usePruneWorkspace); + expect(dockerfile).toContain(restoreSourceWorkspace); + expect(dockerfile).toContain(finalWorkspaceCopy); + expect(dockerfile.indexOf(saveSourceWorkspace)).toBeLessThan( + dockerfile.indexOf(usePruneWorkspace), + ); + expect(dockerfile.indexOf(usePruneWorkspace)).toBeLessThan(dockerfile.indexOf(pruneProd)); + expect(dockerfile.indexOf(pruneProd)).toBeLessThan(dockerfile.indexOf(restoreSourceWorkspace)); + expect(dockerfile.indexOf(restoreSourceWorkspace)).toBeLessThan( + dockerfile.indexOf(finalWorkspaceCopy), ); expect(dockerfile).toContain( "COPY --from=runtime-assets --chown=node:node /app/patches ./patches",