Files
stanp-opencode/src/herdr.ts
T
2026-09-17 21:32:26 +02:00

247 lines
9.2 KiB
TypeScript

import { execFile } from "node:child_process";
import { realpath } from "node:fs/promises";
import { z } from "zod";
export async function command(args: string[], cwd: string, signal: AbortSignal) {
return new Promise<string>((resolve, reject) => {
execFile(
args[0]!,
args.slice(1),
{ cwd, signal, timeout: 30_000, maxBuffer: 2_000_000 },
(error, stdout, stderr) => {
if (error) reject(new Error(`${args[0]} failed: ${stderr || error.message}`));
else resolve(stdout.trim());
},
);
});
}
const inventory = z.object({
result: z.object({
source: z.object({
repo_key: z.string(),
repo_root: z.string(),
source_checkout_path: z.string(),
source_workspace_id: z.string(),
}),
worktrees: z.array(
z.object({
path: z.string(),
is_linked_worktree: z.boolean(),
is_bare: z.boolean(),
open_workspace_id: z.string().nullish(),
}),
),
}),
});
const workspaces = z.object({
result: z.object({ workspaces: z.array(z.object({ workspace_id: z.string() })) }),
});
const created = z.object({
result: z.object({
workspace: z.object({ workspace_id: z.string() }),
worktree: z.object({
path: z.string(),
branch: z.string(),
is_linked_worktree: z.literal(true),
}),
}),
});
export const rootSchema = z.object({
directory: z.string(),
workspaceID: z.string(),
repo: z.string(),
});
export type HerdrRoot = z.infer<typeof rootSchema>;
// The command seam also lets tests use an isolated CLI without touching a live server.
export class Herdr {
constructor(private readonly run = command) {}
async root(directory: string, signal: AbortSignal): Promise<HerdrRoot | undefined> {
signal = AbortSignal.any([signal, AbortSignal.timeout(5_000)]);
const canonical = await realpath(directory);
const list = workspaces.parse(
JSON.parse(await this.run(["herdr", "workspace", "list"], canonical, signal)),
);
const matches: HerdrRoot[] = [];
for (const workspace of list.result.workspaces) {
let value: z.infer<typeof inventory>;
try {
value = inventory.parse(
JSON.parse(
await this.run(
["herdr", "worktree", "list", "--workspace", workspace.workspace_id],
canonical,
signal,
),
),
);
} catch (error) {
if (signal.aborted) throw error;
// Non-repository workspaces cannot provide a worktree inventory.
continue;
}
const source = value.result.source;
if (source.source_workspace_id !== workspace.workspace_id) continue;
if (
(await realpath(source.source_checkout_path)) !== canonical ||
(await realpath(source.repo_root)) !== canonical
)
continue;
const checkout = value.result.worktrees.find(
(item) =>
item.open_workspace_id === workspace.workspace_id &&
!item.is_linked_worktree &&
!item.is_bare,
);
if (!checkout || (await realpath(checkout.path)) !== canonical) continue;
const repo = await realpath(source.repo_key);
const gitRepo = await this.run(
["git", "rev-parse", "--path-format=absolute", "--git-common-dir"],
canonical,
signal,
);
if ((await realpath(gitRepo)) !== repo) continue;
matches.push({ directory: canonical, workspaceID: workspace.workspace_id, repo });
}
// Never guess which Herdr workspace owns a duplicated checkout.
return matches.length === 1 ? matches[0] : undefined;
}
async create(
root: HerdrRoot,
base: string,
branch: string,
signal: AbortSignal,
label?: string,
) {
const current = await this.root(root.directory, signal);
if (!current || current.workspaceID !== root.workspaceID || current.repo !== root.repo)
throw new Error("Herdr root workspace is no longer eligible");
const ref = await this.run(
["git", "rev-parse", "--symbolic-full-name", "--verify", "--end-of-options", base],
root.directory,
signal,
);
if (!ref.startsWith("refs/heads/") && !ref.startsWith("refs/remotes/"))
throw new Error(
"base must identify a locally available branch or remote-tracking branch",
);
const commit = await this.run(
["git", "rev-parse", "--verify", "--end-of-options", `${ref}^{commit}`],
root.directory,
signal,
);
await this.run(["git", "check-ref-format", "--branch", branch], root.directory, signal);
if (
await this.run(
["git", "branch", "--list", "--format=%(refname)", branch],
root.directory,
signal,
)
)
throw new Error(`Destination branch already exists: ${branch}`);
const response = created.parse(
JSON.parse(
await this.run(
[
"herdr",
"worktree",
"create",
// Pin the repository even if a user repoints the workspace after discovery.
"--cwd",
root.directory,
"--branch",
branch,
"--base",
commit,
...(label === undefined ? [] : ["--label", label]),
"--no-focus",
],
root.directory,
signal,
),
),
);
const directory = await realpath(response.result.worktree.path);
const workspaceID = response.result.workspace.workspace_id;
try {
if (
response.result.worktree.branch !== branch ||
directory === root.directory ||
workspaceID === root.workspaceID
)
throw new Error("Herdr returned an unexpected checkout");
const actual = await this.run(["git", "rev-parse", "HEAD"], directory, signal);
const actualBranch = await this.run(
["git", "symbolic-ref", "--short", "HEAD"],
directory,
signal,
);
const repo = await this.run(
["git", "rev-parse", "--path-format=absolute", "--git-common-dir"],
directory,
signal,
);
if (
actual !== commit ||
actualBranch !== branch ||
(await realpath(repo)) !== root.repo
)
throw new Error(
"Created checkout does not match the requested repository and base",
);
await this.verify(directory, workspaceID, root, branch, signal);
} catch (error) {
throw new Error(
`Retained Herdr workspace ${workspaceID}, checkout ${directory}, branch ${branch}`,
{ cause: error },
);
}
return { directory, workspaceID, branch };
}
async verify(
directory: string,
workspaceID: string,
root: HerdrRoot,
branch: string,
signal: AbortSignal,
) {
const value = inventory.parse(
JSON.parse(
await this.run(
["herdr", "worktree", "list", "--workspace", workspaceID],
directory,
signal,
),
),
);
const checkout = value.result.worktrees.find(
(item) => item.open_workspace_id === workspaceID && item.is_linked_worktree,
);
const sourceCheckout = value.result.worktrees.find(
(item) =>
item.open_workspace_id === value.result.source.source_workspace_id &&
!item.is_linked_worktree &&
!item.is_bare,
);
if (
!checkout ||
!sourceCheckout ||
value.result.source.source_workspace_id !== root.workspaceID ||
(await realpath(value.result.source.source_checkout_path)) !== root.directory ||
(await realpath(sourceCheckout.path)) !== root.directory ||
(await realpath(checkout.path)) !== directory ||
(await realpath(value.result.source.repo_key)) !== root.repo
)
throw new Error("Bound Herdr checkout is no longer available");
if (
(await this.run(["git", "symbolic-ref", "--short", "HEAD"], directory, signal)) !==
branch
)
throw new Error("Bound Herdr checkout changed branches");
}
}