The coding agents Fusion drives reach for `rg` as their primary search tool. It was absent from the image, so inside a container they silently fall back to slower or partial search while working fine on a developer machine that has it installed. Operator asked for it by default. Installed alongside git and ca-certificates in the runner stage, and covered by the same runner-stage guard so it cannot quietly drop out again. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
135 lines
5.8 KiB
JavaScript
135 lines
5.8 KiB
JavaScript
import test from "node:test";
|
|
import assert from "node:assert/strict";
|
|
import { globSync, readFileSync } from "node:fs";
|
|
import path from "node:path";
|
|
import { fileURLToPath } from "node:url";
|
|
import YAML from "yaml";
|
|
|
|
const __filename = fileURLToPath(import.meta.url);
|
|
const __dirname = path.dirname(__filename);
|
|
const repoRoot = path.resolve(__dirname, "../..");
|
|
|
|
function normalizeDockerSource(source) {
|
|
return source.replace(/^\.\//, "").replace(/\/$/, "");
|
|
}
|
|
|
|
function readWorkspacePackageManifestPaths(root = repoRoot) {
|
|
const workspacePath = path.join(root, "pnpm-workspace.yaml");
|
|
const workspace = YAML.parse(readFileSync(workspacePath, "utf8"));
|
|
const entries = Array.isArray(workspace?.packages) ? workspace.packages : [];
|
|
const manifestPaths = new Set();
|
|
|
|
for (const entry of entries) {
|
|
if (typeof entry !== "string" || entry.startsWith("!")) {
|
|
continue;
|
|
}
|
|
|
|
for (const manifest of globSync(`${entry.replace(/\/$/, "")}/package.json`, {
|
|
cwd: root,
|
|
nodir: true,
|
|
})) {
|
|
manifestPaths.add(manifest.split(path.sep).join("/"));
|
|
}
|
|
}
|
|
|
|
return manifestPaths;
|
|
}
|
|
|
|
function readBuilderPreInstallCopySources(dockerfile) {
|
|
const builderStart = dockerfile.match(/^FROM\s+.*\s+AS\s+builder\s*$/im);
|
|
assert.ok(builderStart?.index !== undefined, "Dockerfile must define a builder stage");
|
|
|
|
const afterBuilder = dockerfile.slice(builderStart.index + builderStart[0].length);
|
|
const nextStage = afterBuilder.search(/^FROM\s+/im);
|
|
const builderStage = nextStage === -1 ? afterBuilder : afterBuilder.slice(0, nextStage);
|
|
const install = builderStage.match(/RUN\s+pnpm\s+install\s+--frozen-lockfile\b/);
|
|
assert.ok(install?.index !== undefined, "builder stage must run pnpm install --frozen-lockfile");
|
|
|
|
const copied = [];
|
|
for (const match of builderStage.slice(0, install.index).matchAll(/^COPY\s+(?:--\S+\s+)*(.*?)\s+\S+\s*$/gm)) {
|
|
const sources = match[1].trim().split(/\s+/).map(normalizeDockerSource);
|
|
copied.push(...sources);
|
|
}
|
|
return copied;
|
|
}
|
|
|
|
function findMissingWorkspaceManifests(manifests, copySources) {
|
|
return [...manifests].filter((manifest) => !copySources.some((source) => (
|
|
source === manifest || source === "." || manifest.startsWith(`${source}/`)
|
|
))).sort();
|
|
}
|
|
|
|
function readDockerfileCopiedManifestPaths() {
|
|
const dockerfile = readFileSync(path.join(repoRoot, "Dockerfile"), "utf8");
|
|
const copied = readBuilderPreInstallCopySources(dockerfile);
|
|
return { copied, dockerfile };
|
|
}
|
|
|
|
test("Dockerfile builder pre-install copies cover every current workspace manifest", () => {
|
|
const expected = readWorkspacePackageManifestPaths();
|
|
const { copied } = readDockerfileCopiedManifestPaths();
|
|
|
|
assert.deepEqual(findMissingWorkspaceManifests(expected, copied), []);
|
|
assert.equal(new Set(copied).size, copied.length, "builder pre-install COPY sources must not be duplicated");
|
|
});
|
|
|
|
test("coverage rejects a selected plugin omitted before frozen install", () => {
|
|
const expected = readWorkspacePackageManifestPaths();
|
|
const omitted = [...expected].sort().find((manifest) => manifest.startsWith("plugins/"));
|
|
assert.ok(omitted, "workspace fixture must include a plugin manifest");
|
|
|
|
const completeSources = [...expected];
|
|
const incompleteSources = completeSources.filter((source) => source !== omitted);
|
|
assert.deepEqual(findMissingWorkspaceManifests(expected, incompleteSources), [omitted]);
|
|
});
|
|
|
|
test("coverage ignores post-install and runner copies while tolerating removed paths", () => {
|
|
const expected = readWorkspacePackageManifestPaths();
|
|
const omitted = [...expected].sort().find((manifest) => manifest.startsWith("plugins/"));
|
|
assert.ok(omitted, "workspace fixture must include a plugin manifest");
|
|
|
|
const builderCopies = [...expected]
|
|
.filter((manifest) => manifest !== omitted)
|
|
.map((manifest) => `COPY ${manifest} ./${manifest}`)
|
|
.join("\n");
|
|
const dockerfile = `FROM node:22-slim AS builder\n${builderCopies}\nRUN pnpm install --frozen-lockfile\nCOPY ${omitted} ./${omitted}\nFROM node:22-slim AS runner\nCOPY ${omitted} ./${omitted}`;
|
|
const copied = readBuilderPreInstallCopySources(dockerfile);
|
|
|
|
assert.deepEqual(findMissingWorkspaceManifests(expected, copied), [omitted]);
|
|
assert.deepEqual(
|
|
findMissingWorkspaceManifests(expected, [...expected, "plugins/not-in-workspace/package.json"]),
|
|
[],
|
|
"removed or nonexistent COPY paths must not affect selected workspace coverage",
|
|
);
|
|
});
|
|
|
|
/*
|
|
FNXC:DockerRun 2026-08-18-05:35:
|
|
The runner stage MUST install ca-certificates. The slim base ships none, and git verifies TLS
|
|
against the system store, so without it every HTTPS clone dies with "server certificate
|
|
verification failed. CAfile: none CRLfile: none" and project setup is impossible in Docker.
|
|
|
|
This regressed unnoticed because Node carries its OWN bundled CA store: the dashboard, model APIs
|
|
and OAuth token exchanges all worked, so nothing looked wrong until the first clone. Nothing else
|
|
in the image exercises the system trust store, which is exactly why it needs a guard rather than
|
|
relying on someone noticing.
|
|
*/
|
|
test("runner stage installs ca-certificates alongside git", () => {
|
|
const dockerfile = readFileSync(path.join(repoRoot, "Dockerfile"), "utf8");
|
|
const runnerStage = dockerfile.slice(dockerfile.indexOf("FROM node:22-slim AS runner"));
|
|
assert.ok(runnerStage.length > 0, "runner stage must exist");
|
|
|
|
const aptInstall = runnerStage.match(/apt-get install[^\n]*(?:\\\n[^\n]*)*/)?.[0] ?? "";
|
|
assert.match(aptInstall, /\bgit\b/, "runner stage must install git");
|
|
assert.match(
|
|
aptInstall,
|
|
/\bca-certificates\b/,
|
|
"runner stage must install ca-certificates — git cannot verify HTTPS remotes without a system CA bundle",
|
|
);
|
|
assert.match(
|
|
aptInstall,
|
|
/\bripgrep\b/,
|
|
"runner stage must install ripgrep — the coding agents Fusion drives use `rg` as their primary search tool",
|
|
);
|
|
});
|