fix(workspace): respect explicit workspaceMode:false, improve exclusion test
Address PR #1739 review feedback: - P1 (greptile): When workspaceMode is explicitly false in config.json, skip the auto-detection fallback so toggling workspace mode off via the dashboard has a lasting effect (was being re-enabled on next registration). - CodeRabbit: node_modules exclusion test now includes a real sibling sub-repo to prove the exclusion is the gate, not just absence of detection. - Add test for workspaceMode:false config.json guard.
This commit is contained in:
@@ -154,8 +154,45 @@ describe("ensureGitRepositoryForProjectPath", () => {
|
||||
await git(fakePkg, ["add", "index.js"]);
|
||||
await git(fakePkg, ["commit", "-m", "init"]);
|
||||
|
||||
// Also create a real sibling sub-repo to prove it IS detected while node_modules is excluded
|
||||
const realRepo = join(projectPath, "my-app");
|
||||
mkdirSync(realRepo, { recursive: true });
|
||||
await git(realRepo, ["init", "-b", "main"]);
|
||||
await git(realRepo, ["config", "user.email", "test@test.com"]);
|
||||
await git(realRepo, ["config", "user.name", "Test"]);
|
||||
writeFileSync(join(realRepo, "README.md"), "# my-app\n");
|
||||
await git(realRepo, ["add", "README.md"]);
|
||||
await git(realRepo, ["commit", "-m", "init"]);
|
||||
|
||||
const detected = await detectWorkspaceRepos(projectPath);
|
||||
|
||||
expect(detected).toEqual([]);
|
||||
// node_modules is excluded; my-app is detected
|
||||
expect(detected).toEqual(["my-app"]);
|
||||
});
|
||||
|
||||
it("skips auto-detection when workspaceMode is explicitly false in config.json", async () => {
|
||||
const projectPath = tempDir("fusion-git-workspace-disabled-");
|
||||
// Create a real git sub-repo so detectWorkspaceRepos would find it
|
||||
const subRepo = join(projectPath, "repo-a");
|
||||
mkdirSync(subRepo, { recursive: true });
|
||||
await git(subRepo, ["init", "-b", "main"]);
|
||||
await git(subRepo, ["config", "user.email", "test@test.com"]);
|
||||
await git(subRepo, ["config", "user.name", "Test"]);
|
||||
writeFileSync(join(subRepo, "README.md"), "# repo-a\n");
|
||||
await git(subRepo, ["add", "README.md"]);
|
||||
await git(subRepo, ["commit", "-m", "init"]);
|
||||
|
||||
// Write config.json with workspaceMode: false (user disabled it via dashboard)
|
||||
mkdirSync(join(projectPath, ".fusion"), { recursive: true });
|
||||
writeFileSync(
|
||||
join(projectPath, ".fusion", "config.json"),
|
||||
JSON.stringify({ settings: { workspaceMode: false } }),
|
||||
);
|
||||
|
||||
const outcome = await ensureGitRepositoryForProjectPath(projectPath);
|
||||
|
||||
// Should proceed to git init, not workspace detection
|
||||
expect(outcome).toBe("initialized");
|
||||
expect(existsSync(join(projectPath, ".git"))).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -68,16 +68,24 @@ export async function ensureGitRepositoryForProjectPath(
|
||||
CLI interactive setup writes workspace.json explicitly, but dashboard POST /api/projects
|
||||
and `fn project add` do not — without this fallback they would create a stray .git at the
|
||||
workspace root because loadWorkspaceConfig returned null.
|
||||
|
||||
FNXC:Workspace 2026-06-24-17:00:
|
||||
If the user has explicitly disabled workspace mode (workspaceMode: false in config.json),
|
||||
skip auto-detection and proceed to git init. Without this guard, toggling workspace mode off
|
||||
via the dashboard would have no lasting effect — the fallback would re-detect sub-repos and
|
||||
re-create workspace.json on the next registration call.
|
||||
*/
|
||||
const detectedRepos = await detectWorkspaceRepos(projectPath, runner, timeout);
|
||||
if (detectedRepos.length > 0) {
|
||||
try {
|
||||
await saveWorkspaceConfig(projectPath, { repos: detectedRepos });
|
||||
} catch {
|
||||
// Best-effort: persist for the fast path on future calls, but don't fail
|
||||
// the current registration if the write fails (permissions, disk full, etc.).
|
||||
if (!(await isWorkspaceModeExplicitlyDisabled(projectPath))) {
|
||||
const detectedRepos = await detectWorkspaceRepos(projectPath, runner, timeout);
|
||||
if (detectedRepos.length > 0) {
|
||||
try {
|
||||
await saveWorkspaceConfig(projectPath, { repos: detectedRepos });
|
||||
} catch {
|
||||
// Best-effort: persist for the fast path on future calls, but don't fail
|
||||
// the current registration if the write fails (permissions, disk full, etc.).
|
||||
}
|
||||
return "existing";
|
||||
}
|
||||
return "existing";
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -195,6 +203,23 @@ export interface WorkspaceConfig {
|
||||
|
||||
const WORKSPACE_CONFIG_FILENAME = "workspace.json";
|
||||
|
||||
/**
|
||||
* Reads .fusion/config.json and returns true when `workspaceMode` is explicitly
|
||||
* set to `false`. This guards the auto-detection fallback so a user who has
|
||||
* intentionally disabled workspace mode doesn't get it silently re-enabled.
|
||||
*/
|
||||
async function isWorkspaceModeExplicitlyDisabled(projectPath: string): Promise<boolean> {
|
||||
try {
|
||||
const { readFile } = await import("node:fs/promises");
|
||||
const { join } = await import("node:path");
|
||||
const raw = await readFile(join(projectPath, ".fusion", "config.json"), "utf-8");
|
||||
const config = JSON.parse(raw) as { settings?: { workspaceMode?: boolean } };
|
||||
return config.settings?.workspaceMode === false;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
FNXC:Workspace 2026-06-22-00:00:
|
||||
Workspace repo entries are later joined onto the workspace root to resolve worktrees, so an
|
||||
|
||||
Reference in New Issue
Block a user