Handle optional source-binding configuration in structural package upgrades

This commit is contained in:
Timothy J. Aveni
2026-10-07 05:07:09 -07:00
parent 5c618e0e41
commit 192a22c936
2 changed files with 34 additions and 2 deletions
+3 -2
View File
@@ -167,11 +167,12 @@ export const planStructure = async (rootPath: string, request: StructuralRequest
resolveResource, resolveResource,
}); });
if (compiled.resource.kind === "package") { if (compiled.resource.kind === "package") {
const configuration = JSON.parse(await fs.readFile(path.join(resourceRoot, "quixos.check.json"), "utf8")); const configurationSource = await read(resourceRoot, "quixos.check.json");
const configuration = configurationSource === null ? undefined : JSON.parse(configurationSource);
// Execution bindings are immutable build outputs owned by checkedArtifacts, // Execution bindings are immutable build outputs owned by checkedArtifacts,
// not source edits produced by a structural preview. // not source edits produced by a structural preview.
const artifacts = const artifacts =
configuration.backend === "execution" !configuration || configuration.backend === "execution"
? [] ? []
: [ : [
{ {
+31
View File
@@ -13,6 +13,37 @@ import {
} from "../src/capability-language/structural-plan.js"; } from "../src/capability-language/structural-plan.js";
const execFile = promisify(callback); const execFile = promisify(callback);
test("package structural validation does not require source-generated bindings", async (context) => {
const root = await fs.mkdtemp(path.join(os.tmpdir(), "qx-structural-package-test-"));
context.after(() => fs.rm(root, { recursive: true, force: true }));
await execFile("git", ["-C", root, "init"]);
await fs.writeFile(path.join(root, ".gitignore"), ".quixos/\n");
await fs.writeFile(
path.join(root, "quixos.lock"),
`quixos-lock version 1 { quixos source { repository "https://example.test/quixos.git"; commit "${"a".repeat(40)}"; } }`,
);
const before = 'package Test id "package:test" revision "package:test@1" {}';
await fs.writeFile(path.join(root, "package.qx"), before);
const request: StructuralRequest = {
kind: "package",
source: { repository: "https://example.test/package.git", commit: "b".repeat(40) },
files: [{ file: "package.qx", expected: before, replace: `${before}\n// updated` }],
};
const plan = await planStructure(root, request);
assert.deepEqual(
plan.changes.map((change) => change.file),
["package.qx"],
);
assert.equal(plan.validation, "resource-graph");
await fs.writeFile(path.join(root, "quixos.check.json"), "not valid json");
await assert.rejects(() => planStructure(root, request), SyntaxError);
await fs.writeFile(path.join(root, "quixos.check.json"), '{"backend":"execution"}');
assert.deepEqual(
(await planStructure(root, request)).changes.map((change) => change.file),
["package.qx"],
);
});
test("structural plans validate the graph, journal originals, and reject stale edits", async (context) => { test("structural plans validate the graph, journal originals, and reject stale edits", async (context) => {
const root = await fs.mkdtemp(path.join(os.tmpdir(), "qx-structural-test-")); const root = await fs.mkdtemp(path.join(os.tmpdir(), "qx-structural-test-"));
context.after(() => fs.rm(root, { recursive: true, force: true })); context.after(() => fs.rm(root, { recursive: true, force: true }));