// The Docker build context is load-bearing in two directions, and both // failures are silent. // // Excluding too little: a worktree left under `.claude/` is copied into the // image, vitest globs its `test/` tree as well as the real one, and the // containerised `make check` runs the whole suite twice over while reporting // success. A compiled `bin/quak` is ~100 MB of context nobody needs. // // Excluding too much: Prettier 3 reads `.gitignore` as a default ignore file, // so dropping it from the context silently changes which files // `make fmt-check` looks at inside the image compared to the host. // // Neither shows up as a build failure, so they are asserted here. import { describe, expect, it } from "vitest"; import { existsSync, readFileSync } from "node:fs"; import { fileURLToPath } from "node:url"; import { join } from "node:path"; const repoRoot = fileURLToPath(new URL("../../", import.meta.url)); const patterns = (name: string): string[] => readFileSync(join(repoRoot, name), "utf-8") .split("\n") .map((line) => line.trim()) .filter((line) => line !== "" && !line.startsWith("#")); const dockerignore = patterns(".dockerignore"); describe(".dockerignore", () => { // Everything here is either generated, enormous, or secret. `.claude/` is // the correctness one: see the header comment and issue #25. it.each([ ".claude/", ".quak/", "bin/quak", "node_modules", "coverage", "dist", ".vitest-cache/", ".nyc_output/", "*.tsbuildinfo", ])("keeps %s out of the build context", (pattern) => { expect(dockerignore).toContain(pattern); }); it("leaves .gitignore in the build context for prettier", () => { expect(dockerignore).not.toContain(".gitignore"); }); // Both images are built from this same context, and the lint image runs // eslint and prettier across it. BuildKit lets a `.dockerignore` // shadow the root one for a single build; such a file would silently give // the lint build a different, unreviewed context — and eslint's flat config // does not ignore dot-directories, so a stray `.claude/` worktree would be // linted. it.each(["Dockerfile", "Dockerfile.lint"])( "is not shadowed by a per-Dockerfile ignore file for %s", (name) => { expect(existsSync(join(repoRoot, `${name}.dockerignore`))).toBe( false, ); }, ); });