Files
fusion/scripts/__tests__/dockerfile-workspace-manifests.test.mjs
gsxdsm 28e8c0abc9 FN-8524: cover workspace manifests in Docker builds
Ensure the builder install layer includes every selected workspace manifest.

- Copy the five omitted plugin manifests before frozen installation.
- Validate pre-install Dockerfile coverage against pnpm workspace entries.
- Document the manifest coverage requirement and focused test command.

Files changed:
 Dockerfile                                         |   8 +-
 docs/docker.md                                     |   3 +-
 .../dockerfile-workspace-manifests.test.mjs        | 111 +++++++++++++--------
 3 files changed, 78 insertions(+), 44 deletions(-)

Fusion-Task-Id: FN-8524

Fusion-Task-Lineage: 16a4d9df-ff08-4051-9e53-0da414ef6a85

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-22 22:04:32 -07:00

105 lines
4.3 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",
);
});