mirror of
https://github.com/alkimake/paperclip.git
synced 2026-06-15 10:30:37 +09:00
Add worktree reseed command
This commit is contained in:
parent
7dd3661467
commit
34589ad457
3 changed files with 247 additions and 2 deletions
|
|
@ -80,6 +80,7 @@ import {
|
|||
|
||||
type WorktreeInitOptions = {
|
||||
name?: string;
|
||||
color?: string;
|
||||
instance?: string;
|
||||
home?: string;
|
||||
fromConfig?: string;
|
||||
|
|
@ -97,6 +98,16 @@ type WorktreeMakeOptions = WorktreeInitOptions & {
|
|||
startPoint?: string;
|
||||
};
|
||||
|
||||
type WorktreeReseedOptions = {
|
||||
fromConfig?: string;
|
||||
fromDataDir?: string;
|
||||
fromInstance?: string;
|
||||
home?: string;
|
||||
seedMode?: string;
|
||||
yes?: boolean;
|
||||
seed?: boolean;
|
||||
};
|
||||
|
||||
type WorktreeEnvOptions = {
|
||||
config?: string;
|
||||
json?: boolean;
|
||||
|
|
@ -942,8 +953,8 @@ async function runWorktreeInit(opts: WorktreeInitOptions): Promise<void> {
|
|||
instanceId,
|
||||
});
|
||||
const branding = {
|
||||
name: worktreeName,
|
||||
color: generateWorktreeColor(),
|
||||
name: opts.name ?? worktreeName,
|
||||
color: opts.color ?? generateWorktreeColor(),
|
||||
};
|
||||
const sourceConfigPath = resolveSourceConfigPath(opts);
|
||||
const sourceConfig = existsSync(sourceConfigPath) ? readConfig(sourceConfigPath) : null;
|
||||
|
|
@ -1051,6 +1062,104 @@ export async function worktreeInitCommand(opts: WorktreeInitOptions): Promise<vo
|
|||
await runWorktreeInit(opts);
|
||||
}
|
||||
|
||||
function hasExplicitSourceSelection(opts: {
|
||||
fromConfig?: string;
|
||||
fromDataDir?: string;
|
||||
fromInstance?: string;
|
||||
sourceConfigPathOverride?: string;
|
||||
}): boolean {
|
||||
return Boolean(
|
||||
nonEmpty(opts.fromConfig)
|
||||
|| nonEmpty(opts.fromDataDir)
|
||||
|| nonEmpty(opts.fromInstance)
|
||||
|| nonEmpty(opts.sourceConfigPathOverride),
|
||||
);
|
||||
}
|
||||
|
||||
function resolveCurrentWorktreeReseedState(opts: { home?: string } = {}) {
|
||||
const currentConfigPath = resolveConfigPath();
|
||||
if (!existsSync(currentConfigPath)) {
|
||||
throw new Error(
|
||||
"Current directory does not have a Paperclip worktree config. Run `paperclipai worktree init` here first.",
|
||||
);
|
||||
}
|
||||
const currentConfig = readConfig(currentConfigPath);
|
||||
if (!currentConfig) {
|
||||
throw new Error(`Could not read current worktree config at ${currentConfigPath}.`);
|
||||
}
|
||||
if (currentConfig.database.mode !== "embedded-postgres") {
|
||||
throw new Error("Worktree reseed only supports embedded-postgres worktree instances.");
|
||||
}
|
||||
|
||||
const currentEnvEntries = readPaperclipEnvEntries(resolvePaperclipEnvFile(currentConfigPath));
|
||||
const instanceRoot = path.dirname(currentConfig.database.embeddedPostgresDataDir);
|
||||
const derivedHomeDir = path.dirname(path.dirname(instanceRoot));
|
||||
|
||||
return {
|
||||
currentConfigPath: path.resolve(currentConfigPath),
|
||||
instanceId:
|
||||
nonEmpty(currentEnvEntries.PAPERCLIP_INSTANCE_ID)
|
||||
?? nonEmpty(path.basename(instanceRoot))
|
||||
?? sanitizeWorktreeInstanceId(path.basename(process.cwd())),
|
||||
homeDir: path.resolve(expandHomePrefix(opts.home ?? currentEnvEntries.PAPERCLIP_HOME ?? derivedHomeDir)),
|
||||
serverPort: currentConfig.server.port,
|
||||
dbPort: currentConfig.database.embeddedPostgresPort,
|
||||
worktreeName: nonEmpty(currentEnvEntries.PAPERCLIP_WORKTREE_NAME) ?? undefined,
|
||||
worktreeColor: nonEmpty(currentEnvEntries.PAPERCLIP_WORKTREE_COLOR) ?? undefined,
|
||||
};
|
||||
}
|
||||
|
||||
export async function worktreeReseedCommand(opts: WorktreeReseedOptions): Promise<void> {
|
||||
printPaperclipCliBanner();
|
||||
p.intro(pc.bgCyan(pc.black(" paperclipai worktree reseed ")));
|
||||
|
||||
if (!hasExplicitSourceSelection(opts)) {
|
||||
throw new Error(
|
||||
"Reseed requires an explicit source. Pass --from-config or --from-instance (optionally with --from-data-dir).",
|
||||
);
|
||||
}
|
||||
|
||||
const target = resolveCurrentWorktreeReseedState({ home: opts.home });
|
||||
const sourceConfigPath = resolveSourceConfigPath(opts);
|
||||
if (path.resolve(sourceConfigPath) === target.currentConfigPath) {
|
||||
throw new Error(
|
||||
"Source and target Paperclip configs are the same. Pass a different source instance/config when reseeding.",
|
||||
);
|
||||
}
|
||||
|
||||
const seedMode = opts.seedMode ?? "minimal";
|
||||
if (!isWorktreeSeedMode(seedMode)) {
|
||||
throw new Error(`Unsupported seed mode "${seedMode}". Expected one of: minimal, full.`);
|
||||
}
|
||||
|
||||
const confirmed = opts.yes
|
||||
? true
|
||||
: await p.confirm({
|
||||
message: `Reseed the current worktree instance (${target.instanceId}) from ${sourceConfigPath}? This overwrites only the current worktree Paperclip instance data.`,
|
||||
initialValue: false,
|
||||
});
|
||||
if (p.isCancel(confirmed) || !confirmed) {
|
||||
p.log.warn("Reseed cancelled.");
|
||||
return;
|
||||
}
|
||||
|
||||
await runWorktreeInit({
|
||||
name: target.worktreeName,
|
||||
color: target.worktreeColor,
|
||||
instance: target.instanceId,
|
||||
home: target.homeDir,
|
||||
fromConfig: opts.fromConfig,
|
||||
fromDataDir: opts.fromDataDir,
|
||||
fromInstance: opts.fromInstance,
|
||||
sourceConfigPathOverride: sourceConfigPath,
|
||||
serverPort: target.serverPort,
|
||||
dbPort: target.dbPort,
|
||||
seed: opts.seed ?? true,
|
||||
seedMode,
|
||||
force: true,
|
||||
});
|
||||
}
|
||||
|
||||
export async function worktreeMakeCommand(nameArg: string, opts: WorktreeMakeOptions): Promise<void> {
|
||||
printPaperclipCliBanner();
|
||||
p.intro(pc.bgCyan(pc.black(" paperclipai worktree:make ")));
|
||||
|
|
@ -2632,6 +2741,17 @@ export function registerWorktreeCommands(program: Command): void {
|
|||
.option("--json", "Print JSON instead of shell exports")
|
||||
.action(worktreeEnvCommand);
|
||||
|
||||
worktree
|
||||
.command("reseed")
|
||||
.description("Replace the current worktree instance with a fresh seed while preserving this worktree's ports and instance id")
|
||||
.option("--from-config <path>", "Source config.json to seed from")
|
||||
.option("--from-data-dir <path>", "Source PAPERCLIP_HOME used when deriving the source config")
|
||||
.option("--from-instance <id>", "Source instance id when deriving the source config")
|
||||
.option("--home <path>", `Home root for worktree instances (env: PAPERCLIP_WORKTREES_DIR, default: ${DEFAULT_WORKTREE_HOME})`)
|
||||
.option("--seed-mode <mode>", "Seed profile: minimal or full (default: minimal)", "minimal")
|
||||
.option("--yes", "Skip the destructive confirmation prompt", false)
|
||||
.action(worktreeReseedCommand);
|
||||
|
||||
program
|
||||
.command("worktree:list")
|
||||
.description("List git worktrees visible from this repo and whether they look like Paperclip worktrees")
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue