Consolidate Reader source ownership and pinned application instances
Add exact recursive source checkout, independent Git/jj developer workbenches and source-only backup/recovery commands, with Workspace instance binding outside source. Qualify two empty Reader Graphs, packaged source commands, Agent VM compilation, exact Gitea read grants and reconciled Reader settings/annotation stability. Publish checked Reader Package/root revisions to canonical review branches and retention tags, retain all 57 repositories independently in private versioned storage, verify downloaded-backup recovery and advance the example pin. Add bounded Central pin acquisition and selection-specific read-only identities, qualified against all 57 canonical repositories without write authority. Replace regex fork dependency discovery with compiler-parsed lock imports and rewrite proposals; preserve source identities and unrelated files, passing instance binding through packed and ordinary host checks. Hosted run-pin versus authoring preparation and maintained duplicate removal remain pending. Live source selections, Template defaults and default source branches are unchanged.
This commit is contained in:
@@ -0,0 +1,93 @@
|
||||
import { execFile as callback } from "node:child_process";
|
||||
import { promisify } from "node:util";
|
||||
import { randomUUID } from "node:crypto";
|
||||
import { mkdir, mkdtemp, lstat, rename, rm, writeFile } from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
import { materializeWorkspaceSources, workspaceSourcePin, type WorkspaceSourcePin } from "./source-closure.js";
|
||||
|
||||
const exec = promisify(callback);
|
||||
|
||||
/** Developer workflow, not an ordinary Workspace Agent's implicit permission
|
||||
* to publish against canonical repositories. Creates independent Git/jj copies,
|
||||
* with instance binding outside committed source. No Graph is started here.
|
||||
*/
|
||||
export async function prepareDevelopmentSources(options: {
|
||||
source: WorkspaceSourcePin;
|
||||
directory: string;
|
||||
cacheDirectory: string;
|
||||
locations?: Record<string, string>;
|
||||
}) {
|
||||
const source = workspaceSourcePin(options.source);
|
||||
const destination = path.resolve(options.directory);
|
||||
const cache = path.resolve(options.cacheDirectory);
|
||||
if (cache === destination || cache.startsWith(destination + path.sep))
|
||||
throw Error("The retained source cache must live outside the disposable workbench");
|
||||
if (
|
||||
await lstat(destination).catch((error: NodeJS.ErrnoException) => {
|
||||
if (error.code !== "ENOENT") throw error;
|
||||
return null;
|
||||
})
|
||||
)
|
||||
throw Error("Choose a new development workbench directory; existing checkouts are never overwritten");
|
||||
const closure = await materializeWorkspaceSources({
|
||||
source,
|
||||
directory: options.cacheDirectory,
|
||||
locations: options.locations,
|
||||
});
|
||||
const origins = new Set<string>();
|
||||
for (const entry of closure.resources) {
|
||||
const identity = `${entry.kind}:${entry.repository}`;
|
||||
if (origins.has(identity)) throw Error("Multiple revisions of one repository require explicit editable selection");
|
||||
origins.add(identity);
|
||||
}
|
||||
const parent = path.dirname(destination);
|
||||
await mkdir(parent, { recursive: true });
|
||||
const staging = await mkdtemp(path.join(parent, ".development-sources-"));
|
||||
const workspaceId = randomUUID();
|
||||
try {
|
||||
const resources = [];
|
||||
for (const entry of closure.resources) {
|
||||
const relative = entry.kind === "workspace" ? "root" : "resources/" + entry.directory;
|
||||
const checkout = path.join(staging, relative);
|
||||
await mkdir(path.dirname(checkout), { recursive: true });
|
||||
await exec("git", [
|
||||
"clone",
|
||||
"--no-hardlinks",
|
||||
"--",
|
||||
path.join(path.resolve(options.cacheDirectory), entry.directory),
|
||||
checkout,
|
||||
]);
|
||||
await exec("git", ["remote", "set-url", "origin", entry.repository], { cwd: checkout });
|
||||
await exec("git", ["checkout", "--detach", entry.commit], { cwd: checkout });
|
||||
await exec("jj", ["git", "init", "--colocate"], {
|
||||
cwd: checkout,
|
||||
env: { ...process.env, QUIXOS_JJ_NO_CHECKPOINT: "1", QUIXOS_SUBTREE_PUBLISH: "0" },
|
||||
});
|
||||
await exec("jj", ["describe", "-m", "Develop canonical application source"], {
|
||||
cwd: checkout,
|
||||
env: { ...process.env, QUIXOS_JJ_NO_CHECKPOINT: "1", QUIXOS_SUBTREE_PUBLISH: "0" },
|
||||
});
|
||||
if (entry.kind !== "workspace")
|
||||
resources.push({
|
||||
kind: entry.kind,
|
||||
directory: relative,
|
||||
source: { resolver: "git", repository: entry.repository, commit: entry.commit },
|
||||
});
|
||||
}
|
||||
await mkdir(path.join(staging, ".quixos"), { mode: 0o700 });
|
||||
await writeFile(
|
||||
path.join(staging, ".quixos/resource-graph.json"),
|
||||
JSON.stringify({ formatVersion: 1, workspaceId, resources }, null, 2) + "\n",
|
||||
{ mode: 0o600 },
|
||||
);
|
||||
await writeFile(
|
||||
path.join(staging, ".quixos/development-source.json"),
|
||||
JSON.stringify({ source, workspaceId }, null, 2) + "\n",
|
||||
{ mode: 0o600 },
|
||||
);
|
||||
await rename(staging, destination);
|
||||
return { directory: destination, source, workspaceId, repositories: closure.resources.length };
|
||||
} finally {
|
||||
await rm(staging, { recursive: true, force: true });
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user