Files
openclaw/src/docker-image-digests.test.ts
Sally O'Malley 57f19f0d5c container builds: opt-in extension deps via OPENCLAW_EXTENSIONS build arg (#32223)
* Docker: opt-in extension deps via OPENCLAW_EXTENSIONS build arg

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: sallyom <somalley@redhat.com>

* CI: clarify extension smoke scope

* Tests: allow digest-pinned multi-stage FROM lines

* Changelog: note container extension preinstall option

---------

Signed-off-by: sallyom <somalley@redhat.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
2026-03-06 12:18:42 -05:00

62 lines
2.1 KiB
TypeScript

import { readFile } from "node:fs/promises";
import { resolve } from "node:path";
import { fileURLToPath } from "node:url";
import { describe, expect, it } from "vitest";
import { parse } from "yaml";
const repoRoot = resolve(fileURLToPath(new URL(".", import.meta.url)), "..");
const DIGEST_PINNED_DOCKERFILES = [
"Dockerfile",
"Dockerfile.sandbox",
"Dockerfile.sandbox-browser",
"scripts/docker/cleanup-smoke/Dockerfile",
"scripts/docker/install-sh-e2e/Dockerfile",
"scripts/docker/install-sh-nonroot/Dockerfile",
"scripts/docker/install-sh-smoke/Dockerfile",
"scripts/e2e/Dockerfile",
"scripts/e2e/Dockerfile.qr-import",
] as const;
type DependabotDockerGroup = {
patterns?: string[];
};
type DependabotUpdate = {
"package-ecosystem"?: string;
directory?: string;
schedule?: { interval?: string };
groups?: Record<string, DependabotDockerGroup>;
};
type DependabotConfig = {
updates?: DependabotUpdate[];
};
describe("docker base image pinning", () => {
it("pins selected Dockerfile FROM lines to immutable sha256 digests", async () => {
for (const dockerfilePath of DIGEST_PINNED_DOCKERFILES) {
const dockerfile = await readFile(resolve(repoRoot, dockerfilePath), "utf8");
const fromLine = dockerfile
.split(/\r?\n/)
.find((line) => line.trimStart().startsWith("FROM "));
expect(fromLine, `${dockerfilePath} should define a FROM line`).toBeDefined();
expect(fromLine, `${dockerfilePath} FROM must be digest-pinned`).toMatch(
/^FROM\s+\S+@sha256:[a-f0-9]{64}(?:\s+AS\s+\S+)?$/,
);
}
});
it("keeps Dependabot Docker updates enabled for root Dockerfiles", async () => {
const raw = await readFile(resolve(repoRoot, ".github/dependabot.yml"), "utf8");
const config = parse(raw) as DependabotConfig;
const dockerUpdate = config.updates?.find(
(update) => update["package-ecosystem"] === "docker" && update.directory === "/",
);
expect(dockerUpdate).toBeDefined();
expect(dockerUpdate?.schedule?.interval).toBe("weekly");
expect(dockerUpdate?.groups?.["docker-images"]?.patterns).toContain("*");
});
});