Files
openclaw/src/dockerfile.test.ts
Vincent Koc 3f3f66a5f7 Docker: trim runtime image payload (#40307)
* Docker: shrink runtime image payload

* Docker: add runtime pnpm opt-in

* Docker: collapse helper entrypoint chmod layers

* Docker: restore bundled pnpm runtime

* Update CHANGELOG.md
2026-03-08 16:07:04 -07:00

70 lines
3.3 KiB
TypeScript

import { readFile } from "node:fs/promises";
import { join, resolve } from "node:path";
import { fileURLToPath } from "node:url";
import { describe, expect, it } from "vitest";
const repoRoot = resolve(fileURLToPath(new URL(".", import.meta.url)), "..");
const dockerfilePath = join(repoRoot, "Dockerfile");
describe("Dockerfile", () => {
it("uses shared multi-arch base image refs for all root Node stages", async () => {
const dockerfile = await readFile(dockerfilePath, "utf8");
expect(dockerfile).toContain(
'ARG OPENCLAW_NODE_BOOKWORM_IMAGE="node:22-bookworm@sha256:b501c082306a4f528bc4038cbf2fbb58095d583d0419a259b2114b5ac53d12e9"',
);
expect(dockerfile).toContain(
'ARG OPENCLAW_NODE_BOOKWORM_SLIM_IMAGE="node:22-bookworm-slim@sha256:9c2c405e3ff9b9afb2873232d24bb06367d649aa3e6259cbe314da59578e81e9"',
);
expect(dockerfile).toContain("FROM ${OPENCLAW_NODE_BOOKWORM_IMAGE} AS ext-deps");
expect(dockerfile).toContain("FROM ${OPENCLAW_NODE_BOOKWORM_IMAGE} AS build");
expect(dockerfile).toContain("FROM ${OPENCLAW_NODE_BOOKWORM_IMAGE} AS base-default");
expect(dockerfile).toContain("FROM ${OPENCLAW_NODE_BOOKWORM_SLIM_IMAGE} AS base-slim");
expect(dockerfile).toContain("current multi-arch manifest list entry");
expect(dockerfile).not.toContain("current amd64 entry");
});
it("installs optional browser dependencies after pnpm install", async () => {
const dockerfile = await readFile(dockerfilePath, "utf8");
const installIndex = dockerfile.indexOf("pnpm install --frozen-lockfile");
const browserArgIndex = dockerfile.indexOf("ARG OPENCLAW_INSTALL_BROWSER");
expect(installIndex).toBeGreaterThan(-1);
expect(browserArgIndex).toBeGreaterThan(-1);
expect(browserArgIndex).toBeGreaterThan(installIndex);
expect(dockerfile).toContain(
"node /app/node_modules/playwright-core/cli.js install --with-deps chromium",
);
expect(dockerfile).toContain("apt-get install -y --no-install-recommends xvfb");
});
it("prunes runtime dependencies after the build stage", async () => {
const dockerfile = await readFile(dockerfilePath, "utf8");
expect(dockerfile).toContain("FROM build AS runtime-assets");
expect(dockerfile).toContain("CI=true pnpm prune --prod");
expect(dockerfile).toContain(
"COPY --from=runtime-assets --chown=node:node /app/node_modules ./node_modules",
);
});
it("normalizes plugin and agent paths permissions in image layers", async () => {
const dockerfile = await readFile(dockerfilePath, "utf8");
expect(dockerfile).toContain("for dir in /app/extensions /app/.agent /app/.agents");
expect(dockerfile).toContain('find "$dir" -type d -exec chmod 755 {} +');
expect(dockerfile).toContain('find "$dir" -type f -exec chmod 644 {} +');
});
it("Docker GPG fingerprint awk uses correct quoting for OPENCLAW_SANDBOX=1 build", async () => {
const dockerfile = await readFile(dockerfilePath, "utf8");
expect(dockerfile).toContain('== "fpr" {');
expect(dockerfile).not.toContain('\\"fpr\\"');
});
it("keeps runtime pnpm available", async () => {
const dockerfile = await readFile(dockerfilePath, "utf8");
expect(dockerfile).toContain("ENV COREPACK_HOME=/usr/local/share/corepack");
expect(dockerfile).toContain(
'corepack prepare "$(node -p "require(\'./package.json\').packageManager")" --activate',
);
});
});