mirror of
https://github.com/alkimake/paperclip.git
synced 2026-06-15 18:30:39 +09:00
Avoid sibling worktree port collisions
Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
parent
e91da556ee
commit
555f026c24
4 changed files with 445 additions and 5 deletions
|
|
@ -465,6 +465,62 @@ async function findAvailablePort(preferredPort: number, reserved = new Set<numbe
|
|||
return port;
|
||||
}
|
||||
|
||||
function resolveRepoManagedWorktreesRoot(cwd: string): string | null {
|
||||
const normalized = path.resolve(cwd);
|
||||
const marker = `${path.sep}.paperclip${path.sep}worktrees${path.sep}`;
|
||||
const index = normalized.indexOf(marker);
|
||||
if (index === -1) return null;
|
||||
const repoRoot = normalized.slice(0, index);
|
||||
return path.resolve(repoRoot, ".paperclip", "worktrees");
|
||||
}
|
||||
|
||||
function collectClaimedWorktreePorts(homeDir: string, currentInstanceId: string, cwd: string): {
|
||||
serverPorts: Set<number>;
|
||||
databasePorts: Set<number>;
|
||||
} {
|
||||
const serverPorts = new Set<number>();
|
||||
const databasePorts = new Set<number>();
|
||||
const configPaths = new Set<string>();
|
||||
const instancesDir = path.resolve(homeDir, "instances");
|
||||
if (existsSync(instancesDir)) {
|
||||
for (const entry of readdirSync(instancesDir, { withFileTypes: true })) {
|
||||
if (!entry.isDirectory() || entry.name === currentInstanceId) continue;
|
||||
|
||||
const configPath = path.resolve(instancesDir, entry.name, "config.json");
|
||||
if (existsSync(configPath)) {
|
||||
configPaths.add(configPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const repoManagedWorktreesRoot = resolveRepoManagedWorktreesRoot(cwd);
|
||||
if (repoManagedWorktreesRoot && existsSync(repoManagedWorktreesRoot)) {
|
||||
for (const entry of readdirSync(repoManagedWorktreesRoot, { withFileTypes: true })) {
|
||||
if (!entry.isDirectory()) continue;
|
||||
const configPath = path.resolve(repoManagedWorktreesRoot, entry.name, ".paperclip", "config.json");
|
||||
if (existsSync(configPath)) {
|
||||
configPaths.add(configPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const configPath of configPaths) {
|
||||
try {
|
||||
const config = readConfig(configPath);
|
||||
if (config?.server.port) {
|
||||
serverPorts.add(config.server.port);
|
||||
}
|
||||
if (config?.database.mode === "embedded-postgres") {
|
||||
databasePorts.add(config.database.embeddedPostgresPort);
|
||||
}
|
||||
} catch {
|
||||
// Ignore malformed sibling configs.
|
||||
}
|
||||
}
|
||||
|
||||
return { serverPorts, databasePorts };
|
||||
}
|
||||
|
||||
function detectGitBranchName(cwd: string): string | null {
|
||||
try {
|
||||
const value = execFileSync("git", ["branch", "--show-current"], {
|
||||
|
|
@ -886,10 +942,14 @@ async function runWorktreeInit(opts: WorktreeInitOptions): Promise<void> {
|
|||
rmSync(paths.instanceRoot, { recursive: true, force: true });
|
||||
}
|
||||
|
||||
const claimedPorts = collectClaimedWorktreePorts(paths.homeDir, paths.instanceId, paths.cwd);
|
||||
const preferredServerPort = opts.serverPort ?? ((sourceConfig?.server.port ?? 3100) + 1);
|
||||
const serverPort = await findAvailablePort(preferredServerPort);
|
||||
const serverPort = await findAvailablePort(preferredServerPort, claimedPorts.serverPorts);
|
||||
const preferredDbPort = opts.dbPort ?? ((sourceConfig?.database.embeddedPostgresPort ?? 54329) + 1);
|
||||
const databasePort = await findAvailablePort(preferredDbPort, new Set([serverPort]));
|
||||
const databasePort = await findAvailablePort(
|
||||
preferredDbPort,
|
||||
new Set([...claimedPorts.databasePorts, serverPort]),
|
||||
);
|
||||
const targetConfig = buildWorktreeConfig({
|
||||
sourceConfig,
|
||||
paths,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue