fix(docker): make workspace deps portable to Podman (#103814)

Fixes #103741. Reported and verified on amd64 Podman by @sallyom.
This commit is contained in:
Peter Steinberger
2026-07-10 17:33:28 +01:00
committed by GitHub
parent e43f225c81
commit 1dcbcce515
2 changed files with 36 additions and 7 deletions
+7 -4
View File
@@ -32,10 +32,13 @@ ARG OPENCLAW_EXTENSIONS
ARG OPENCLAW_BUNDLED_PLUGIN_DIR
# Copy package.json files for workspace packages used by the install layer.
# Manifest-only bundled plugins remain valid selections but need no workspace metadata.
RUN --mount=type=bind,source=packages,target=/tmp/packages,readonly \
--mount=type=bind,source=${OPENCLAW_BUNDLED_PLUGIN_DIR},target=/tmp/${OPENCLAW_BUNDLED_PLUGIN_DIR},readonly \
--mount=type=bind,source=scripts/lib/docker-plugin-selection.mjs,target=/tmp/docker-plugin-selection.mjs,readonly \
mkdir -p /out/packages "/out/${OPENCLAW_BUNDLED_PLUGIN_DIR}" && \
# Use COPY because build-context bind mounts are unreliable across supported
# Podman/Buildah hosts. Full trees stay in this disposable stage; later stages
# receive only extracted manifests.
COPY scripts/lib/docker-plugin-selection.mjs /tmp/docker-plugin-selection.mjs
COPY packages /tmp/packages
COPY ${OPENCLAW_BUNDLED_PLUGIN_DIR} /tmp/${OPENCLAW_BUNDLED_PLUGIN_DIR}
RUN mkdir -p /out/packages "/out/${OPENCLAW_BUNDLED_PLUGIN_DIR}" && \
for manifest in /tmp/packages/*/package.json; do \
[ -f "$manifest" ] || continue; \
pkg_dir="${manifest%/package.json}"; \
+29 -3
View File
@@ -130,6 +130,35 @@ describe("Dockerfile", () => {
);
});
it("uses portable copies for workspace dependency inputs", async () => {
const dockerfile = await readFile(dockerfilePath, "utf8");
const workspaceDepsStart = dockerfile.indexOf(
"FROM ${OPENCLAW_NODE_BOOKWORM_IMAGE} AS workspace-deps",
);
const workspaceDepsEnd = dockerfile.indexOf("FROM ${OPENCLAW_BUN_IMAGE} AS bun-binary");
expect(workspaceDepsStart).toBeGreaterThan(-1);
expect(workspaceDepsEnd).toBeGreaterThan(workspaceDepsStart);
const workspaceDeps = dockerfile.slice(workspaceDepsStart, workspaceDepsEnd);
const extractionIndex = workspaceDeps.indexOf(
'RUN mkdir -p /out/packages "/out/${OPENCLAW_BUNDLED_PLUGIN_DIR}"',
);
const inputCopies = [
"COPY scripts/lib/docker-plugin-selection.mjs /tmp/docker-plugin-selection.mjs",
"COPY packages /tmp/packages",
"COPY ${OPENCLAW_BUNDLED_PLUGIN_DIR} /tmp/${OPENCLAW_BUNDLED_PLUGIN_DIR}",
];
expect(extractionIndex).toBeGreaterThan(-1);
for (const copy of inputCopies) {
const copyIndex = workspaceDeps.indexOf(copy);
expect(copyIndex, copy).toBeGreaterThan(-1);
expect(copyIndex, copy).toBeLessThan(extractionIndex);
}
expect(workspaceDeps).not.toContain("--mount=type=bind");
});
it("copies install workspace manifests before pnpm install", async () => {
const dockerfile = await readFile(dockerfilePath, "utf8");
const installIndex = dockerfile.indexOf("pnpm install --frozen-lockfile");
@@ -151,9 +180,6 @@ describe("Dockerfile", () => {
expect(packageManifestIndex).toBeGreaterThan(-1);
expect(extensionManifestIndex).toBeGreaterThan(-1);
expect(dockerfile).toContain("for manifest in /tmp/packages/*/package.json");
expect(dockerfile).toContain(
"--mount=type=bind,source=scripts/lib/docker-plugin-selection.mjs,target=/tmp/docker-plugin-selection.mjs,readonly",
);
expect(dockerfile).toContain(
'node /tmp/docker-plugin-selection.mjs "/tmp/${OPENCLAW_BUNDLED_PLUGIN_DIR}" "$OPENCLAW_EXTENSIONS"',
);