Build composable Camino packages with explicit type and source dependencies
This commit is contained in:
+106
-100
@@ -76,106 +76,112 @@ test(
|
||||
},
|
||||
);
|
||||
|
||||
test("pin upgrades publish children before parent locks and resume without republishing completed nodes", async (context) => {
|
||||
const workbench = await fs.mkdtemp(path.join(os.tmpdir(), "qx-upgrade-test-"));
|
||||
context.after(() => fs.rm(workbench, { recursive: true, force: true }));
|
||||
const from = "a".repeat(40),
|
||||
to = "b".repeat(40),
|
||||
framework = "c".repeat(40);
|
||||
const sources = [
|
||||
{
|
||||
kind: "workspace" as const,
|
||||
directory: "root",
|
||||
source: { repository: "https://example.test/workspace.git", commit: from },
|
||||
},
|
||||
{
|
||||
kind: "interface" as const,
|
||||
directory: "resources/Named",
|
||||
source: { repository: "https://example.test/named.git", commit: from },
|
||||
},
|
||||
];
|
||||
const lock = `quixos-lock version 1 { quixos source { repository "https://example.test/quixos.git"; commit "${framework}"; }`;
|
||||
for (const node of sources) {
|
||||
const directory = path.join(workbench, node.directory);
|
||||
await fs.mkdir(directory, { recursive: true });
|
||||
await execFile("git", ["-C", directory, "init"]);
|
||||
await execFile("git", ["-C", directory, "remote", "add", "origin", node.source.repository]);
|
||||
await fs.writeFile(path.join(directory, ".gitignore"), ".quixos/\n");
|
||||
for (const dependencyKind of ["interface", "build"] as const)
|
||||
test(`${dependencyKind} pin upgrades publish children before parent locks and resume without republishing completed nodes`, async (context) => {
|
||||
const workbench = await fs.mkdtemp(path.join(os.tmpdir(), "qx-upgrade-test-"));
|
||||
context.after(() => fs.rm(workbench, { recursive: true, force: true }));
|
||||
const from = "a".repeat(40),
|
||||
to = "b".repeat(40),
|
||||
framework = "c".repeat(40);
|
||||
const sources = [
|
||||
{
|
||||
kind: "workspace" as const,
|
||||
directory: "root",
|
||||
source: { repository: "https://example.test/workspace.git", commit: from },
|
||||
},
|
||||
{
|
||||
kind: dependencyKind,
|
||||
directory: "resources/Named",
|
||||
source: { repository: "https://example.test/named.git", commit: from },
|
||||
},
|
||||
];
|
||||
const lock = `quixos-lock version 1 { quixos source { repository "https://example.test/quixos.git"; commit "${framework}"; }`;
|
||||
for (const node of sources) {
|
||||
const directory = path.join(workbench, node.directory);
|
||||
await fs.mkdir(directory, { recursive: true });
|
||||
await execFile("git", ["-C", directory, "init"]);
|
||||
await execFile("git", ["-C", directory, "remote", "add", "origin", node.source.repository]);
|
||||
await fs.writeFile(path.join(directory, ".gitignore"), ".quixos/\n");
|
||||
await fs.writeFile(
|
||||
path.join(directory, "quixos.lock"),
|
||||
lock +
|
||||
(node.kind === "workspace"
|
||||
? ` ${dependencyKind} Named source { repository "${sources[1].source.repository}"; commit "${from}"; }`
|
||||
: "") +
|
||||
" }",
|
||||
);
|
||||
}
|
||||
await fs.writeFile(
|
||||
path.join(directory, "quixos.lock"),
|
||||
lock +
|
||||
(node.kind === "workspace"
|
||||
? ` interface Named source { repository "${sources[1].source.repository}"; commit "${from}"; }`
|
||||
: "") +
|
||||
" }",
|
||||
path.join(
|
||||
workbench,
|
||||
dependencyKind === "interface" ? "resources/Named/interface.qx" : "resources/Named/types.ts",
|
||||
),
|
||||
dependencyKind === "interface"
|
||||
? 'interface Named id "interface:named" revision "interface:named@1" { value name id "member:name" : string { get id "op:get"; } }'
|
||||
: "export type Name = string;",
|
||||
);
|
||||
}
|
||||
await fs.writeFile(
|
||||
path.join(workbench, "resources/Named/interface.qx"),
|
||||
'interface Named id "interface:named" revision "interface:named@1" { value name id "member:name" : string { get id "op:get"; } }',
|
||||
);
|
||||
await fs.writeFile(
|
||||
path.join(workbench, "root/workspace.qx"),
|
||||
`workspace W id "workspace:w" revision "workspace:w@1" commit "${from}" { import interface Named; atom A id "atom:a"; }`,
|
||||
);
|
||||
const snapshotMap = path.join(workbench, "snapshots.json");
|
||||
await fs.writeFile(
|
||||
snapshotMap,
|
||||
JSON.stringify({
|
||||
resources: [
|
||||
{
|
||||
kind: "interface",
|
||||
repository: sources[1].source.repository,
|
||||
commit: to,
|
||||
directory: path.join(workbench, "resources/Named"),
|
||||
},
|
||||
],
|
||||
}),
|
||||
);
|
||||
const oldMap = process.env.QUIXOS_SNAPSHOT_MAP;
|
||||
process.env.QUIXOS_SNAPSHOT_MAP = snapshotMap;
|
||||
context.after(() => {
|
||||
if (oldMap === undefined) delete process.env.QUIXOS_SNAPSHOT_MAP;
|
||||
else process.env.QUIXOS_SNAPSHOT_MAP = oldMap;
|
||||
await fs.writeFile(
|
||||
path.join(workbench, "root/workspace.qx"),
|
||||
`workspace W id "workspace:w" revision "workspace:w@1" commit "${from}" { ${dependencyKind === "interface" ? "import interface Named;" : ""} atom A id "atom:a"; }`,
|
||||
);
|
||||
const snapshotMap = path.join(workbench, "snapshots.json");
|
||||
await fs.writeFile(
|
||||
snapshotMap,
|
||||
JSON.stringify({
|
||||
resources: [
|
||||
{
|
||||
kind: dependencyKind,
|
||||
repository: sources[1].source.repository,
|
||||
commit: to,
|
||||
directory: path.join(workbench, "resources/Named"),
|
||||
},
|
||||
],
|
||||
}),
|
||||
);
|
||||
const oldMap = process.env.QUIXOS_SNAPSHOT_MAP;
|
||||
process.env.QUIXOS_SNAPSHOT_MAP = snapshotMap;
|
||||
context.after(() => {
|
||||
if (oldMap === undefined) delete process.env.QUIXOS_SNAPSHOT_MAP;
|
||||
else process.env.QUIXOS_SNAPSHOT_MAP = oldMap;
|
||||
});
|
||||
const plan = await planPinUpgrades(workbench, { nodes: sources });
|
||||
await fs.mkdir(path.join(workbench, ".quixos"), { recursive: true });
|
||||
await fs.writeFile(
|
||||
path.join(workbench, ".quixos/resource-graph.json"),
|
||||
JSON.stringify({ resources: [{ ...sources[1], source: { resolver: "git", ...sources[1].source } }] }),
|
||||
);
|
||||
assert.deepEqual(
|
||||
plan.nodes.map((node) => node.directory),
|
||||
["resources/Named", "root"],
|
||||
);
|
||||
let fail = true;
|
||||
const published: string[] = [];
|
||||
const effects: UpgradeEffects = {
|
||||
check: async (node) => {
|
||||
if (node.kind === "workspace" && fail) throw new Error("refactor required");
|
||||
},
|
||||
snapshot: async () => to,
|
||||
publish: async (root) => {
|
||||
published.push(path.relative(workbench, root));
|
||||
},
|
||||
};
|
||||
await assert.rejects(() => applyPinUpgrades(plan, undefined, effects), /refactor required/);
|
||||
assert.deepEqual(published, ["resources/Named"]);
|
||||
assert.match(await fs.readFile(path.join(workbench, "root/quixos.lock"), "utf8"), new RegExp(to));
|
||||
const id = (await fs.readdir(path.join(workbench, ".quixos/upgrades")))
|
||||
.find((name) => name.endsWith(".json"))!
|
||||
.slice(0, -5);
|
||||
fail = false;
|
||||
await fs.appendFile(path.join(workbench, "root/workspace.qx"), "\n// explicit refactor\n");
|
||||
await assert.rejects(() => applyPinUpgrades(plan, id, effects), /accept-edits/);
|
||||
const result = await applyPinUpgrades(plan, id, effects, { acceptEdits: true });
|
||||
assert.equal(result.activated, false);
|
||||
assert.deepEqual(published, ["resources/Named", "root"]);
|
||||
const graph = JSON.parse(await fs.readFile(path.join(workbench, ".quixos/resource-graph.json"), "utf8"));
|
||||
assert.equal(graph.resources.length, 1);
|
||||
assert.equal(graph.resources[0].source.commit, to);
|
||||
const next = await planPinUpgrades(workbench, {
|
||||
nodes: [sources[0], { ...sources[1], source: graph.resources[0].source }],
|
||||
});
|
||||
assert.deepEqual(next.nodes.find((node) => node.kind === "workspace")!.dependencies, ["resources/Named"]);
|
||||
});
|
||||
const plan = await planPinUpgrades(workbench, { nodes: sources });
|
||||
await fs.mkdir(path.join(workbench, ".quixos"), { recursive: true });
|
||||
await fs.writeFile(
|
||||
path.join(workbench, ".quixos/resource-graph.json"),
|
||||
JSON.stringify({ resources: [{ ...sources[1], source: { resolver: "git", ...sources[1].source } }] }),
|
||||
);
|
||||
assert.deepEqual(
|
||||
plan.nodes.map((node) => node.directory),
|
||||
["resources/Named", "root"],
|
||||
);
|
||||
let fail = true;
|
||||
const published: string[] = [];
|
||||
const effects: UpgradeEffects = {
|
||||
check: async (node) => {
|
||||
if (node.kind === "workspace" && fail) throw new Error("refactor required");
|
||||
},
|
||||
snapshot: async () => to,
|
||||
publish: async (root) => {
|
||||
published.push(path.relative(workbench, root));
|
||||
},
|
||||
};
|
||||
await assert.rejects(() => applyPinUpgrades(plan, undefined, effects), /refactor required/);
|
||||
assert.deepEqual(published, ["resources/Named"]);
|
||||
assert.match(await fs.readFile(path.join(workbench, "root/quixos.lock"), "utf8"), new RegExp(to));
|
||||
const id = (await fs.readdir(path.join(workbench, ".quixos/upgrades")))
|
||||
.find((name) => name.endsWith(".json"))!
|
||||
.slice(0, -5);
|
||||
fail = false;
|
||||
await fs.appendFile(path.join(workbench, "root/workspace.qx"), "\n// explicit refactor\n");
|
||||
await assert.rejects(() => applyPinUpgrades(plan, id, effects), /accept-edits/);
|
||||
const result = await applyPinUpgrades(plan, id, effects, { acceptEdits: true });
|
||||
assert.equal(result.activated, false);
|
||||
assert.deepEqual(published, ["resources/Named", "root"]);
|
||||
const graph = JSON.parse(await fs.readFile(path.join(workbench, ".quixos/resource-graph.json"), "utf8"));
|
||||
assert.equal(graph.resources.length, 1);
|
||||
assert.equal(graph.resources[0].source.commit, to);
|
||||
const next = await planPinUpgrades(workbench, {
|
||||
nodes: [sources[0], { ...sources[1], source: graph.resources[0].source }],
|
||||
});
|
||||
assert.deepEqual(next.nodes.find((node) => node.kind === "workspace")!.dependencies, ["resources/Named"]);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user