Build and activate checked component packages through normal source authoring (WI-12)

This commit is contained in:
Timothy J. Aveni
2026-09-21 17:38:40 -07:00
parent e1ad15c620
commit 1e08ccd937
11 changed files with 336 additions and 571 deletions
+20 -44
View File
@@ -12,7 +12,6 @@ import os from "node:os";
import { spawnSync } from "node:child_process";
import { planStructure, applyStructure, resumeStructure, type StructuralRequest } from "./structural-plan.js";
import { scaffoldRecipe, type ScaffoldRecipe } from "./scaffold-recipes.js";
import { isExecutionScaffold } from "./execution-scaffold.js";
import { checkBundleSources } from "../bindings/bundle-policy.js";
import { sealMigrations } from "./migration-seal.js";
import { generatePackageDescriptor } from "../bindings/index.js";
@@ -386,12 +385,19 @@ const main = async () => {
}
if (command === "scaffold-refresh")
throw new Error(
"Refresh is no longer required: edit declarations and typed implementation wiring, then run qx-workspace check. Dependency installation uses scaffold install.",
"Refresh is no longer required: edit declarations and typed implementation wiring, then run qx-workspace check. Dependencies are provided by checkedArtifacts.",
);
if (command === "scaffold-install")
throw Error("Execution dependencies are supplied by checkedArtifacts; run workspace-root check instead");
if (
["scaffold-package", "scaffold-function", "scaffold-query", "scaffold-migration", "scaffold-install"].includes(
command,
)
[
"scaffold-package",
"scaffold-function",
"scaffold-query",
"scaffold-component",
"scaffold-controller",
"scaffold-migration",
].includes(command)
) {
const [root, specFile, ...flags] = args;
let spec: ScaffoldRecipe;
@@ -425,54 +431,24 @@ const main = async () => {
flags.some((flag) => !["--write", "--install"].includes(flag)) ||
(flags.includes("--install") && !flags.includes("--write"))
)
throw new Error(
"usage: quixos-qx scaffold-package|function|query|migration ROOT SPEC_JSON [--write [--install]]",
);
throw new Error("usage: quixos-qx scaffold-package|function|query|component|controller ROOT SPEC_JSON [--write]");
const plan =
command === "scaffold-install"
? undefined
: await planStructure(
root,
await scaffoldRecipe(root, command.slice(9) as "package" | "function" | "query" | "migration", spec),
await scaffoldRecipe(
root,
command.slice(9) as "package" | "function" | "query" | "component" | "controller" | "migration",
spec,
),
process.env.QUIXOS_SNAPSHOT_MAP,
);
if (
flags.includes("--install") &&
(spec.template === "typescript-execution" ||
(await isExecutionScaffold(root, spec.directory ? `${spec.directory}/` : "")))
)
throw new Error(
"Execution package dependencies are supplied by checkedArtifacts; omit --install and run workspace-root check after authoring.",
if (flags.includes("--install"))
throw Error(
"Execution dependencies are supplied by checkedArtifacts; run workspace-root check instead of scaffold install",
);
const applied = plan && flags.includes("--write") ? await applyStructure(plan) : undefined;
if (flags.includes("--install")) {
const cwd = path.resolve(root, spec.directory ?? "");
const toolchain = JSON.parse(await readFile(path.join(cwd, "quixos.toolchain.json"), "utf8"));
if (toolchain.generatedBy !== "qx-scaffold-v1" || typeof toolchain.nixifyPluginUrl !== "string")
throw new Error("Missing scaffold toolchain");
for (const [executable, args] of [
["corepack", ["yarn", "plugin", "import", toolchain.nixifyPluginUrl]],
["corepack", ["yarn", "config", "set", "generateDefaultNix", "false"]],
["corepack", ["yarn", "config", "set", "individualNixPackaging", "true"]],
["corepack", ["yarn", "install"]],
] as const) {
const result = spawnSync(executable, [...args], { cwd, stdio: ["inherit", 2, 2] });
if (result.error || result.status !== 0)
throw new Error(
`Scaffold files retained; ${executable} ${args.join(" ")} failed: ${result.error?.message ?? result.status}`,
);
}
try {
await readFile(path.join(cwd, "yarn-project.nix"));
} catch {
throw new Error(
"Nixify did not generate yarn-project.nix. It skips repositories under the OS temporary directory; use an ordinary workspace checkout and retry installation.",
);
}
await snapshotCommit(cwd);
const locked = spawnSync("nix", ["flake", "lock"], { cwd, stdio: ["inherit", 2, 2] });
if (locked.error || locked.status !== 0) throw new Error("Scaffold files retained; nix flake lock failed");
}
process.stdout.write(
`${JSON.stringify({ ...(plan ? planSummary(plan) : { installed: true }), applied }, null, 2)}\n`,
);