Files
frames/packages/gcp-cloud-run/check-dockerfile-workspaces.test.mjs
zooqueen 75094e0d60 frames: publish the toolchain as @hanzo/frames
@hanzo/frame is taken by the hero video primitive — one full-bleed video
element — which has held that name on npm since it shipped. This repo is the
video toolchain: a CLI, a renderer, a studio, a player. Plural is what it is,
so the CLI publishes as @hanzo/frames and the workspace packages follow it
(@hanzo/frames-core, @hanzo/frames-engine, and the rest).

The binary stays `frames`, and now actually ships. The manifest mapped it to
bin/frames.mjs while the file on disk was bin/hyperframes.mjs, so a global
install produced no working command.

The paths that name a package to fetch were pointing elsewhere. The update
check read registry.npmjs.org/frames, a different author's package, and the
npm, bun and pnpm installers would have pulled it over the CLI; the render
image installed the upstream hyperframes; scaffolded projects and the docs
pinned the unscoped name. All of them now name @hanzo/frames.
2026-08-13 18:10:14 -07:00

145 lines
5.0 KiB
JavaScript

import assert from "node:assert/strict";
import { describe, it } from "node:test";
import {
checkDockerfileWorkspaces,
findRuntimeWorkspaceDirectories,
listDockerfileWorkspaceIssues,
readWorkspacePackages,
} from "./check-dockerfile-workspaces.mjs";
const workspaces = [
workspace("core", "@hanzo/frames-core", {
"@hanzo/frames-lint": "workspace:*",
"@hanzo/frames-studio-server": "workspace:*",
}),
workspace("engine", "@hanzo/frames-engine", {
"@hanzo/frames-core": "workspace:*",
"@hanzo/frames-parsers": "workspace:*",
}),
workspace("gcp-cloud-run", "@hanzo/frames-gcp-cloud-run", {
"@hanzo/frames-producer": "workspace:*",
}),
workspace("lint", "@hanzo/frames-lint", { "@hanzo/frames-parsers": "workspace:*" }),
workspace("parsers", "@hanzo/frames-parsers"),
workspace("producer", "@hanzo/frames-producer", {
"@hanzo/frames-core": "workspace:*",
"@hanzo/frames-engine": "workspace:*",
}),
workspace("sdk", "@hanzo/frames-sdk"),
workspace("studio-server", "@hanzo/frames-studio-server", {
"@hanzo/frames-core": "workspace:*",
}),
];
const runtimeDirectories = [
"core",
"engine",
"gcp-cloud-run",
"lint",
"parsers",
"producer",
"studio-server",
];
const buildDirectories = [
"core",
"engine",
"lint",
"parsers",
"studio-server",
"producer",
"gcp-cloud-run",
];
function workspace(directory, name, dependencies = {}) {
return { directory, manifest: { name, scripts: { build: "build" }, dependencies } };
}
function dockerfile({
manifests = workspaces.map((candidate) => candidate.directory),
sources = runtimeDirectories,
builds = buildDirectories,
} = {}) {
return [
...manifests.map(
(directory) => `COPY packages/${directory}/package.json packages/${directory}/package.json`,
),
...sources.map((directory) => `COPY packages/${directory}/ packages/${directory}/`),
...builds.map((directory) => `RUN bun run --cwd packages/${directory} build`),
].join("\n");
}
describe("GCP Cloud Run Dockerfile workspace checker", () => {
it("accepts the repository Dockerfile", () => {
assert.equal(checkDockerfileWorkspaces(), readWorkspacePackages().length);
});
it("derives the runtime closure without looping on workspace cycles", () => {
assert.deepEqual([...findRuntimeWorkspaceDirectories(workspaces)].sort(), runtimeDirectories);
});
it("reports a missing workspace manifest", () => {
const manifests = workspaces
.map((candidate) => candidate.directory)
.filter((directory) => directory !== "sdk");
assert.deepEqual(listDockerfileWorkspaceIssues(dockerfile({ manifests }), workspaces), [
"missing workspace manifests: sdk",
]);
const misdirected = dockerfile().replace(
"COPY packages/sdk/package.json packages/sdk/package.json",
"COPY packages/sdk/package.json packages/wrong/package.json",
);
assert.deepEqual(listDockerfileWorkspaceIssues(misdirected, workspaces), [
"missing workspace manifests: sdk",
]);
});
it("reports a missing runtime source copy", () => {
const sources = runtimeDirectories.filter((directory) => directory !== "parsers");
assert.deepEqual(listDockerfileWorkspaceIssues(dockerfile({ sources }), workspaces), [
"missing runtime workspace sources: parsers",
]);
const misdirected = dockerfile().replace(
"COPY packages/parsers/ packages/parsers/",
"COPY packages/parsers/ packages/wrong/",
);
assert.deepEqual(listDockerfileWorkspaceIssues(misdirected, workspaces), [
"missing runtime workspace sources: parsers",
]);
});
it("requires every buildable runtime prerequisite before producer", () => {
const builds = buildDirectories.filter((directory) => directory !== "lint");
assert.deepEqual(listDockerfileWorkspaceIssues(dockerfile({ builds }), workspaces), [
"runtime workspaces must run their full build before producer: lint",
]);
const lateBuilds = buildDirectories.filter((directory) => directory !== "lint");
lateBuilds.splice(lateBuilds.indexOf("producer") + 1, 0, "lint");
assert.deepEqual(
listDockerfileWorkspaceIssues(dockerfile({ builds: lateBuilds }), workspaces),
["runtime workspaces must run their full build before producer: lint"],
);
});
it("does not mistake a build subcommand for a full workspace build", () => {
const input = dockerfile()
.replace(
"RUN bun run --cwd packages/core build",
"RUN bun run --cwd packages/core build:frames-runtime:modular",
)
.concat("\n# bun run --cwd packages/core build");
assert.deepEqual(listDockerfileWorkspaceIssues(input, workspaces), [
"runtime workspaces must run their full build before producer: core",
]);
});
it("requires producer to be built before the adapter", () => {
const builds = buildDirectories.filter((directory) => directory !== "producer");
builds.push("producer");
assert.deepEqual(listDockerfileWorkspaceIssues(dockerfile({ builds }), workspaces), [
"producer must be built before gcp-cloud-run",
]);
});
});