Keep new worktrees clean when setup changes are uncommitted (#3311)

* fix(worktrees): preserve selected base config

New worktrees were overwriting the selected ref's paseo.json with source-checkout bytes, which could create an accidental revert in an otherwise fresh workspace. Keep committed base config authoritative and warn when locally saved setup changes still need to be committed.

* fix(worktrees): seed config exclusively

* fix(projects): refresh config status on focus
This commit is contained in:
Mohamed Boudra
2026-08-13 12:02:15 +02:00
committed by GitHub
parent 9a7301d9a0
commit c190fed27c
23 changed files with 460 additions and 55 deletions
@@ -1,4 +1,5 @@
import { chmod, readFile, writeFile } from "node:fs/promises";
import { execFileSync } from "node:child_process";
import path from "node:path";
import { expect, type Page } from "@playwright/test";
import type { WebSocketRoute } from "@playwright/test";
@@ -177,6 +178,19 @@ export async function expectSaveButtonDisabled(page: Page): Promise<void> {
await expect(page.getByTestId("save-button")).toBeDisabled();
}
export async function expectUncommittedSetupWarning(page: Page): Promise<void> {
const warning = page.getByRole("alert").filter({ hasText: "Commit paseo.json changes" });
await expect(warning).toContainText("Commit paseo.json changes");
await expect(warning).toContainText(
"New worktrees use the setup script from the base branch you select.",
);
}
export async function expectNoUncommittedSetupWarning(page: Page): Promise<void> {
const warning = page.getByRole("alert").filter({ hasText: "Commit paseo.json changes" });
await expect(warning).toHaveCount(0);
}
// --- Form-state assertions ---
export async function expectProjectSettingsFormVisible(page: Page): Promise<void> {
@@ -249,6 +263,11 @@ export async function restorePaseoConfig(
await writeFile(path.join(repoPath, "paseo.json"), JSON.stringify(config, null, 2) + "\n");
}
export function commitPaseoConfig(repoPath: string): void {
execFileSync("git", ["add", "paseo.json"], { cwd: repoPath });
execFileSync("git", ["commit", "-m", "Update project config"], { cwd: repoPath });
}
// The daemon writes atomically via a temp file + rename, so blocking writes requires
// removing write permission from the *directory*, not just the file.
export async function blockPaseoConfigWrites(repoPath: string): Promise<void> {