// 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 test // phase 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 the lint // phase's prettier check looks at compared to `make fmt-check` on 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"); }); // BuildKit lets a `Dockerfile.dockerignore` shadow the root one; such a // file would silently give the build a different, unreviewed context — // and eslint's flat config does not ignore dot-directories, so a stray // `.claude/` worktree would be linted. it("is not shadowed by a Dockerfile.dockerignore", () => { expect(existsSync(join(repoRoot, "Dockerfile.dockerignore"))).toBe( false, ); }); });