Use saved Project Settings for new worktree setup (#3233)

* fix(worktrees): honor saved setup configuration

Project Settings writes paseo.json in the source checkout, but existing committed copies in new worktrees previously won silently. Copy the saved project config before lifecycle setup so the command shown to users is the command that runs.

* fix(worktrees): replace config symlinks safely

A selected ref can place a symlink at paseo.json, so copying over that path may overwrite a file outside the worktree. Materialize the saved source config with atomic rename semantics to replace the path entry without following it.
This commit is contained in:
Mohamed Boudra
2026-08-11 21:20:30 +02:00
committed by GitHub
parent b9d7d4b18e
commit 2f54c0e309
4 changed files with 64 additions and 19 deletions
+9 -9
View File
@@ -1,7 +1,7 @@
import { execFile } from "child_process";
import { promisify } from "util";
import { existsSync, mkdirSync, realpathSync, rmSync, statSync } from "fs";
import { copyFile, rm, stat } from "fs/promises";
import { readFile, rm, stat } from "fs/promises";
import { join, basename, dirname, isAbsolute, resolve, sep } from "path";
import net from "node:net";
import { createHash } from "node:crypto";
@@ -33,6 +33,7 @@ import {
import { runGitCommand } from "./run-git-command.js";
import { spawnProcess } from "./spawn.js";
import { resolvePaseoHome } from "../server/paseo-home.js";
import { writeFileAtomic } from "../server/atomic-file.js";
import { createExternalProcessEnv } from "../server/paseo-env.js";
import { parseGitRevParsePath, resolveGitRevParsePath } from "./git-rev-parse-path.js";
import { validateBranchSlug } from "@getpaseo/protocol/branch-slug";
@@ -793,21 +794,20 @@ export async function runWorktreeTeardownCommands(options: {
return results;
}
export async function seedPaseoConfigFile(options: {
export async function copySourcePaseoConfigFile(options: {
sourceCwd: string;
targetCwd: string;
}): Promise<void> {
const sourceConfigPath = join(options.sourceCwd, "paseo.json");
const targetConfigPath = join(options.targetCwd, "paseo.json");
let sourceConfig: Buffer;
try {
await stat(targetConfigPath);
return;
sourceConfig = await readFile(sourceConfigPath);
} catch (error) {
if ((error as NodeJS.ErrnoException).code !== "ENOENT") throw error;
if ((error as NodeJS.ErrnoException).code === "ENOENT") return;
throw error;
}
await copyFile(sourceConfigPath, targetConfigPath).catch((error) => {
if ((error as NodeJS.ErrnoException).code !== "ENOENT") throw error;
});
await writeFileAtomic(targetConfigPath, sourceConfig);
}
/**
@@ -1257,7 +1257,7 @@ export const createWorktree = async ({
: {}),
});
await seedPaseoConfigFile({ sourceCwd: cwd, targetCwd: worktreePath });
await copySourcePaseoConfigFile({ sourceCwd: cwd, targetCwd: worktreePath });
if (runSetup) {
await runWorktreeSetupCommands({