01ca965c7f
Coordinate registered resource edits bottom-up into retained exact remote sources. Use one Nix-owned source graph for provisional checking, template publication, explicit baseline upgrades and host activation; retain independent runtime pins. Add scoped contract inspection, historical recovery, derived worklists, crash-safe locks, named dependency adoption and plain-QX structural editing. Repair TODO ownership and template instantiation, and document the supported agent workflow. Validated with protocol and command suites, real jj/Nix convergence and cache checks, TS/React installed-command acceptance, and fresh TODO first-edit acceptance. No live deployment or public publication performed. Props projection generation and a one-command rich feature generator remain explicitly outside this delivery.
112 lines
4.4 KiB
TypeScript
112 lines
4.4 KiB
TypeScript
import childProcess from "node:child_process";
|
|
import crypto from "node:crypto";
|
|
import { mkdir, mkdtemp, readFile, realpath, rename, rm, stat } from "node:fs/promises";
|
|
import path from "node:path";
|
|
import { promisify } from "node:util";
|
|
import type { CapabilityRepositoryResolver } from "./assembly.js";
|
|
|
|
const execFile = promisify(childProcess.execFile);
|
|
|
|
const sourceKey = (kind: string, repository: string, commit: string) =>
|
|
`${kind}\0${repository}\0${commit.toLowerCase()}`;
|
|
|
|
const checkoutName = (kind: string, repository: string, commit: string) => {
|
|
const digest = crypto
|
|
.createHash("sha256")
|
|
.update(sourceKey(kind, repository, commit))
|
|
.digest("hex")
|
|
.slice(0, 24);
|
|
return `${kind}-${digest}`;
|
|
};
|
|
|
|
export const createGitCapabilityResolver = async (options: {
|
|
checkoutRoot: string;
|
|
snapshotMap?: string;
|
|
snapshotOnly?: boolean;
|
|
}): Promise<CapabilityRepositoryResolver> => {
|
|
await mkdir(options.checkoutRoot, { recursive: true });
|
|
const checkoutRoot = await realpath(options.checkoutRoot);
|
|
const snapshots = new Map<string, string>();
|
|
if (options.snapshotMap) {
|
|
const snapshotMapPath = await realpath(options.snapshotMap);
|
|
const document = JSON.parse(await readFile(snapshotMapPath, "utf8")) as {
|
|
resources?: Array<{
|
|
kind: string;
|
|
repository: string;
|
|
commit: string;
|
|
directory: string;
|
|
}>;
|
|
};
|
|
for (const entry of document.resources ?? []) {
|
|
if (entry.kind !== "interface" && entry.kind !== "package") {
|
|
throw new Error(`Snapshot map has unsupported resource kind ${entry.kind}`);
|
|
}
|
|
const key = sourceKey(entry.kind, entry.repository, entry.commit);
|
|
if (snapshots.has(key)) throw new Error(`Snapshot map repeats ${key}`);
|
|
const directory = path.resolve(path.dirname(snapshotMapPath), entry.directory);
|
|
snapshots.set(key, await realpath(directory));
|
|
}
|
|
}
|
|
|
|
const checkouts = new Map<string, Promise<{ directory: string }>>();
|
|
return async (source, kind) => {
|
|
const key = sourceKey(kind, source.repository, source.commit);
|
|
const snapshot = snapshots.get(key);
|
|
if (snapshot) return { directory: snapshot };
|
|
if (options.snapshotOnly) throw new Error(`No offline snapshot for ${kind} ${source.repository}@${source.commit}`);
|
|
const existing = checkouts.get(key);
|
|
if (existing) return await existing;
|
|
const pending = (async () => {
|
|
const directory = path.join(
|
|
checkoutRoot,
|
|
checkoutName(kind, source.repository, source.commit),
|
|
);
|
|
const verify = async (checkout: string) => {
|
|
const { stdout } = await execFile("git", ["-C", checkout, "rev-parse", "HEAD"]);
|
|
if (stdout.trim().toLowerCase() !== source.commit.toLowerCase()) {
|
|
throw new Error(`Locked commit mismatch for ${source.repository}: wanted ${source.commit}, fetched ${stdout.trim()}`);
|
|
}
|
|
const { stdout: changes } = await execFile("git", ["-C", checkout, "status", "--porcelain", "--untracked-files=all"]);
|
|
if (changes.trim()) throw new Error(`Dependency checkout was modified: ${checkout}`);
|
|
};
|
|
// Only complete, checked clones become visible under the deterministic name.
|
|
// Concurrent resolvers may fetch independently, but cannot observe a partial clone.
|
|
if (await stat(directory).then(() => true, (error: NodeJS.ErrnoException) => {
|
|
if (error.code === "ENOENT") return false;
|
|
throw error;
|
|
})) {
|
|
await verify(directory);
|
|
return { directory };
|
|
}
|
|
const staging = await mkdtemp(path.join(checkoutRoot, ".fetch-"));
|
|
const checkout = path.join(staging, "checkout");
|
|
try {
|
|
await execFile("git", [
|
|
"-c",
|
|
"advice.detachedHead=false",
|
|
"clone",
|
|
"--depth",
|
|
"1",
|
|
"--single-branch",
|
|
"--branch",
|
|
`quixos-reachability/${source.commit.toLowerCase()}`,
|
|
source.repository,
|
|
checkout,
|
|
]);
|
|
await verify(checkout);
|
|
try { await rename(checkout, directory); }
|
|
catch (error) {
|
|
if (!["EEXIST", "ENOTEMPTY"].includes((error as NodeJS.ErrnoException).code ?? "")) throw error;
|
|
await verify(directory);
|
|
}
|
|
} finally {
|
|
await rm(staging, { recursive: true, force: true });
|
|
}
|
|
return { directory };
|
|
})();
|
|
checkouts.set(key, pending);
|
|
try { return await pending; }
|
|
catch (error) { checkouts.delete(key); throw error; }
|
|
};
|
|
};
|