mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 10:40:43 +00:00
161 lines
5.9 KiB
TypeScript
161 lines
5.9 KiB
TypeScript
import { readFile } from "node:fs/promises";
|
|
import { resolve } from "node:path";
|
|
import { fileURLToPath } from "node:url";
|
|
import { beforeAll, describe, expect, it } from "vitest";
|
|
|
|
const repoRoot = resolve(fileURLToPath(new URL(".", import.meta.url)), "..");
|
|
const dockerfilePaths = [
|
|
"Dockerfile",
|
|
"Dockerfile.sandbox",
|
|
"Dockerfile.sandbox-browser",
|
|
"Dockerfile.sandbox-common",
|
|
"scripts/docker/cleanup-smoke/Dockerfile",
|
|
"scripts/docker/install-sh-smoke/Dockerfile",
|
|
"scripts/docker/install-sh-e2e/Dockerfile",
|
|
"scripts/docker/install-sh-nonroot/Dockerfile",
|
|
"scripts/e2e/Dockerfile",
|
|
"scripts/e2e/Dockerfile.qr-import",
|
|
] as const;
|
|
const aptCacheDockerfilePaths = dockerfilePaths.filter(
|
|
(path) => path !== "scripts/e2e/Dockerfile.qr-import" && path !== "scripts/e2e/Dockerfile",
|
|
);
|
|
const shellContinuationDockerfilePaths = dockerfilePaths.filter(
|
|
(path) =>
|
|
path !== "Dockerfile" &&
|
|
path !== "scripts/e2e/Dockerfile" &&
|
|
path !== "scripts/e2e/Dockerfile.qr-import",
|
|
);
|
|
const repoFileCache = new Map<string, Promise<string>>();
|
|
|
|
async function readRepoFile(path: string): Promise<string> {
|
|
let cached = repoFileCache.get(path);
|
|
if (!cached) {
|
|
cached = readFile(resolve(repoRoot, path), "utf8");
|
|
repoFileCache.set(path, cached);
|
|
}
|
|
return cached;
|
|
}
|
|
|
|
function indexOfPattern(source: string, pattern: RegExp): number {
|
|
return source.search(pattern);
|
|
}
|
|
|
|
describe("docker build cache layout", () => {
|
|
beforeAll(async () => {
|
|
await Promise.all(dockerfilePaths.map((path) => readRepoFile(path)));
|
|
});
|
|
|
|
it("keeps the root dependency layer independent from scripts changes", async () => {
|
|
const dockerfile = await readRepoFile("Dockerfile");
|
|
const installIndex = dockerfile.indexOf("pnpm install --frozen-lockfile");
|
|
const copyAllIndex = dockerfile.indexOf("COPY . .");
|
|
const scriptsCopyIndex = dockerfile.indexOf("COPY scripts ./scripts");
|
|
|
|
expect(installIndex).toBeGreaterThan(-1);
|
|
expect(copyAllIndex).toBeGreaterThan(installIndex);
|
|
expect(scriptsCopyIndex === -1 || scriptsCopyIndex > installIndex).toBe(true);
|
|
});
|
|
|
|
it("uses pnpm cache mounts in Dockerfiles that install repo dependencies", async () => {
|
|
for (const path of [
|
|
"Dockerfile",
|
|
"scripts/e2e/Dockerfile.qr-import",
|
|
"scripts/docker/cleanup-smoke/Dockerfile",
|
|
]) {
|
|
const dockerfile = await readRepoFile(path);
|
|
expect(
|
|
dockerfile,
|
|
`${path} should use a shared pnpm store cache under the active user's home`,
|
|
).toMatch(
|
|
/--mount=type=cache,id=openclaw-pnpm-store,target=\/(?:root|home\/appuser)\/\.local\/share\/pnpm\/store,sharing=locked/,
|
|
);
|
|
}
|
|
});
|
|
|
|
it("uses apt cache mounts in Dockerfiles that install system packages", async () => {
|
|
for (const path of aptCacheDockerfilePaths) {
|
|
const dockerfile = await readRepoFile(path);
|
|
expect(dockerfile, `${path} should cache apt package archives`).toContain(
|
|
"target=/var/cache/apt,sharing=locked",
|
|
);
|
|
expect(dockerfile, `${path} should cache apt metadata`).toContain(
|
|
"target=/var/lib/apt,sharing=locked",
|
|
);
|
|
}
|
|
});
|
|
|
|
it("does not leave empty shell continuation lines in sandbox-common", async () => {
|
|
const dockerfile = await readRepoFile("Dockerfile.sandbox-common");
|
|
expect(dockerfile).not.toContain("apt-get install -y --no-install-recommends ${PACKAGES} \\");
|
|
expect(dockerfile).toContain(
|
|
'RUN if [ "${INSTALL_PNPM}" = "1" ]; then npm install -g pnpm; fi',
|
|
);
|
|
});
|
|
|
|
it("does not leave blank lines after shell continuation markers", async () => {
|
|
for (const path of shellContinuationDockerfilePaths) {
|
|
const dockerfile = await readRepoFile(path);
|
|
expect(
|
|
dockerfile,
|
|
`${path} should not have blank lines after a trailing backslash`,
|
|
).not.toMatch(/\\\n\s*\n/);
|
|
}
|
|
});
|
|
|
|
it("keeps the shared e2e image on the packaged tarball install path", async () => {
|
|
const dockerfile = await readRepoFile("scripts/e2e/Dockerfile");
|
|
|
|
expect(dockerfile).not.toContain("pnpm install --frozen-lockfile");
|
|
expect(dockerfile).not.toContain("COPY . .");
|
|
expect(dockerfile).toMatch(
|
|
/^COPY --from=openclaw_package --chown=appuser:appuser openclaw-current\.tgz \/tmp\/openclaw-current\.tgz$/m,
|
|
);
|
|
expect(dockerfile).toContain(
|
|
"npm install -g --prefix /tmp/openclaw-prefix /tmp/openclaw-current.tgz --no-fund --no-audit",
|
|
);
|
|
});
|
|
|
|
it("copies manifests before install in the qr-import image", async () => {
|
|
const dockerfile = await readRepoFile("scripts/e2e/Dockerfile.qr-import");
|
|
const installIndex = dockerfile.indexOf("pnpm install --frozen-lockfile");
|
|
|
|
expect(
|
|
indexOfPattern(
|
|
dockerfile,
|
|
/^COPY(?:\s+--chown=\S+)?\s+package\.json pnpm-lock\.yaml pnpm-workspace\.yaml \.\/$/m,
|
|
),
|
|
).toBeLessThan(installIndex);
|
|
expect(
|
|
indexOfPattern(
|
|
dockerfile,
|
|
/^COPY(?:\s+--chown=\S+)?\s+ui\/package\.json \.\/ui\/package\.json$/m,
|
|
),
|
|
).toBeLessThan(installIndex);
|
|
expect(dockerfile).toContain("This image only exercises the root QR runtime dependency path.");
|
|
expect(
|
|
indexOfPattern(
|
|
dockerfile,
|
|
/^COPY(?:\s+--chown=\S+)?\s+extensions\/memory-core\/package\.json \.\/extensions\/memory-core\/package\.json$/m,
|
|
),
|
|
).toBe(-1);
|
|
expect(indexOfPattern(dockerfile, /^COPY(?:\s+--chown=\S+)?\s+\.\s+\.$/m)).toBeGreaterThan(
|
|
installIndex,
|
|
);
|
|
});
|
|
|
|
it("copies .npmrc before install in the cleanup smoke image", async () => {
|
|
const dockerfile = await readRepoFile("scripts/docker/cleanup-smoke/Dockerfile");
|
|
const installIndex = dockerfile.indexOf("pnpm install --frozen-lockfile");
|
|
|
|
expect(
|
|
indexOfPattern(
|
|
dockerfile,
|
|
/^COPY(?:\s+--chown=\S+)?\s+package\.json pnpm-lock\.yaml pnpm-workspace\.yaml \.npmrc \.\/$/m,
|
|
),
|
|
).toBeLessThan(installIndex);
|
|
expect(indexOfPattern(dockerfile, /^COPY(?:\s+--chown=\S+)?\s+\.\s+\.$/m)).toBeGreaterThan(
|
|
installIndex,
|
|
);
|
|
});
|
|
});
|