Skip to content

refactor(desktop): enhance loadSetupConfig to support worktree paths#1252

Merged
Kitenite merged 2 commits intosuperset-sh:mainfrom
Ipriyankrajai:fix/config
Feb 6, 2026
Merged

refactor(desktop): enhance loadSetupConfig to support worktree paths#1252
Kitenite merged 2 commits intosuperset-sh:mainfrom
Ipriyankrajai:fix/config

Conversation

@Ipriyankrajai
Copy link
Copy Markdown
Contributor

@Ipriyankrajai Ipriyankrajai commented Feb 6, 2026

Description

Setup/teardown config (loadSetupConfig) always read .superset/config.json from the local main branch checkout (mainRepoPath), ignoring the version on the worktree's branch. When a user updates their config on remote (e.g. renaming .superset/setup.sh to scripts/setup-worktree.sh) and creates a new workspace before pulling main locally, the old config is used — causing a "no such file or directory" error for the old script path.

This PR changes loadSetupConfig to prefer reading config from the worktree path first (which has the branch-specific version of config.json), falling back to mainRepoPath when the worktree doesn't have a config (e.g. when .superset is fully gitignored).

Changes

  • setup.ts — Extracted readConfigFromPath helper; loadSetupConfig now accepts { mainRepoPath, worktreePath? } and tries worktree first
  • teardown.ts — Same refactor (teardown had the identical bug)
  • create.ts — Updated all 6 call sites to pass worktreePath; for createFromPr, moved config loading into handleExistingWorktree/handleNewWorktree where the worktree path is known
  • init.ts — Updated getSetupCommands to pass worktree path
  • setup.test.ts — Updated existing tests for new signature + added 2 new tests covering worktree preference and fallback behavior

Related Issues

Type of Change

  • Bug fix
  • New feature
  • Documentation
  • Refactor
  • Other (please describe):

Testing

  • All 6 unit tests pass in setup.test.ts (4 updated + 2 new)
    • prefers worktree config over main repo config — verifies worktree config takes priority
    • falls back to main repo when worktree has no config — verifies backward compatibility
  • Full monorepo typecheck passes (18/18 tasks)
  • Biome lint passes

Screenshots (if applicable)

N/A — backend logic change only.

Additional Notes

The root cause: .superset/config.json is tracked in git, so worktrees created from remote branches already contain the updated version. But loadSetupConfig was hardcoded to read from mainRepoPath, which reflects the local (potentially stale) main branch. The fix reads from the worktree first, which always has the config matching its branch.

Summary by CodeRabbit

  • Refactor
    • Setup configuration loading now automatically prefers worktree-specific configs and falls back to the main repo; workspace operations now load setup internally for simpler, more consistent behavior.
  • Chore / Public API
    • The setup loader now accepts an optional worktree path to enable worktree-aware resolution.
  • Tests
    • Added tests covering config resolution: main repo, worktree-preferred, and fallback scenarios.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Feb 6, 2026

📝 Walkthrough

Walkthrough

Refactors setup configuration loading to be worktree-aware: loadSetupConfig now accepts { mainRepoPath, worktreePath? } and prefers worktree config. Call sites and handlers (create/init/teardown) load setup internally instead of receiving setupConfig as a parameter; tests updated accordingly.

Changes

Cohort / File(s) Summary
Setup Utilities
apps/desktop/src/lib/trpc/routers/workspaces/utils/setup.ts, apps/desktop/src/lib/trpc/routers/workspaces/utils/teardown.ts
Changed loadSetupConfig signature to accept { mainRepoPath, worktreePath? }. Added readConfigFromPath helper and worktree-first lookup. teardown.ts now imports and uses the new loader.
Setup Tests
apps/desktop/src/lib/trpc/routers/workspaces/utils/setup.test.ts
Updated tests to call loadSetupConfig with the new options object; added scenarios for worktree preference and fallback to main repo.
Workspace Creation
apps/desktop/src/lib/trpc/routers/workspaces/procedures/create.ts
Removed setupConfig from HandleExistingWorktreeParams and HandleNewWorktreeParams and from handler signatures; handlers now call loadSetupConfig({ mainRepoPath, worktreePath }) internally. Call sites updated to stop passing setupConfig.
Workspace Init
apps/desktop/src/lib/trpc/routers/workspaces/procedures/init.ts
Calls to loadSetupConfig changed to pass { mainRepoPath, worktreePath } (uses relations.worktree?.path).

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested reviewers

  • AviPeltz

Poem

🐰 Hopped from repo down to tree,

I sniffed for config close to me.
If none was found, I turned around—
Back to the main repo, safe and sound.
Now worktrees get their rightful crown.

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: enhancing loadSetupConfig to support worktree paths as a workaround for a config-reading bug.
Description check ✅ Passed The description comprehensively covers all required sections: clear problem statement, detailed changes across multiple files, type of change (bug fix), testing evidence, and additional context on root cause.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/desktop/src/lib/trpc/routers/workspaces/utils/setup.ts (1)

30-56: 🛠️ Refactor suggestion | 🟠 Major

readConfigFromPath and loadSetupConfig are duplicated in teardown.ts with inconsistent constant usage.

Both setup.ts (lines 30–71) and teardown.ts (lines 16–53) implement nearly identical readConfigFromPath and loadSetupConfig functions. The only functional difference is field validation (setup vs teardown). Additionally, teardown.ts hardcodes the path strings ".superset" and "config.json" instead of importing and using the shared constants PROJECT_SUPERSET_DIR_NAME and CONFIG_FILE_NAME from shared/constants (which setup.ts correctly uses).

Extract these functions into a shared utility module that accepts the field name to validate, or export readConfigFromPath from setup.ts and reuse it in teardown.ts. Either way, ensure teardown.ts imports and uses the shared constants rather than hardcoding values.

🤖 Fix all issues with AI agents
In `@apps/desktop/src/lib/trpc/routers/workspaces/utils/teardown.ts`:
- Around line 16-17: Replace the hardcoded ".superset" string in
readConfigFromPath with the PROJECT_SUPERSET_DIR_NAME constant: import {
PROJECT_SUPERSET_DIR_NAME } from "shared/constants" at the top of the file and
change the join call in readConfigFromPath to use PROJECT_SUPERSET_DIR_NAME
(i.e., join(basePath, PROJECT_SUPERSET_DIR_NAME, "config.json")) so it matches
setup.ts and avoids string drift.
🧹 Nitpick comments (3)
apps/desktop/src/lib/trpc/routers/workspaces/utils/setup.ts (1)

43-49: Unsafe as SetupConfig cast on parsed JSON.

JSON.parse(content) as SetupConfig trusts the file content entirely. The validation on line 45 only checks parsed.setup is an array (when present), but doesn't verify element types are strings or that unexpected fields don't cause downstream issues. Since coding guidelines recommend validating untrusted data with Zod, consider using a Zod schema here — you already depend on zod in the project.

♻️ Suggested approach
+import { z } from "zod";
+
+const setupConfigSchema = z.object({
+	setup: z.array(z.string()).optional(),
+	teardown: z.array(z.string()).optional(),
+}).passthrough();
+
 function readConfigFromPath(basePath: string): SetupConfig | null {
 	const configPath = join(basePath, PROJECT_SUPERSET_DIR_NAME, CONFIG_FILE_NAME);
 	if (!existsSync(configPath)) return null;
 	try {
 		const content = readFileSync(configPath, "utf-8");
-		const parsed = JSON.parse(content) as SetupConfig;
-		if (parsed.setup && !Array.isArray(parsed.setup)) {
-			throw new Error("'setup' field must be an array of strings");
-		}
-		return parsed;
+		const parsed = JSON.parse(content);
+		return setupConfigSchema.parse(parsed) as SetupConfig;
 	} catch (error) {

This would also eliminate the duplicated validation in teardown.ts if you share the helper.

apps/desktop/src/lib/trpc/routers/workspaces/utils/setup.test.ts (1)

61-98: Good coverage for the new worktree-aware behavior.

The two new tests cover the primary happy paths well. One edge case worth considering: when the worktree has an invalid config (e.g., malformed JSON), the current implementation silently falls back to mainRepoPath. A test for this scenario would document and lock in that behavior.

apps/desktop/src/lib/trpc/routers/workspaces/utils/teardown.ts (1)

55-58: Pre-existing: runTeardown uses 3 positional parameters.

Coding guidelines prefer object parameters for functions with 2+ params. This is pre-existing and out of scope for this PR, but worth noting for a future pass.

Comment on lines +16 to +17
function readConfigFromPath(basePath: string): SetupConfig | null {
const configPath = join(basePath, ".superset", "config.json");
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Hardcoded ".superset" instead of using PROJECT_SUPERSET_DIR_NAME constant.

setup.ts uses the PROJECT_SUPERSET_DIR_NAME constant from shared/constants, but this copy hardcodes ".superset". Use the constant for consistency and to avoid drift.

♻️ Proposed fix
+import { PROJECT_SUPERSET_DIR_NAME, CONFIG_FILE_NAME } from "shared/constants";
+
 function readConfigFromPath(basePath: string): SetupConfig | null {
-	const configPath = join(basePath, ".superset", "config.json");
+	const configPath = join(basePath, PROJECT_SUPERSET_DIR_NAME, CONFIG_FILE_NAME);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
function readConfigFromPath(basePath: string): SetupConfig | null {
const configPath = join(basePath, ".superset", "config.json");
import { PROJECT_SUPERSET_DIR_NAME, CONFIG_FILE_NAME } from "shared/constants";
function readConfigFromPath(basePath: string): SetupConfig | null {
const configPath = join(basePath, PROJECT_SUPERSET_DIR_NAME, CONFIG_FILE_NAME);
🤖 Prompt for AI Agents
In `@apps/desktop/src/lib/trpc/routers/workspaces/utils/teardown.ts` around lines
16 - 17, Replace the hardcoded ".superset" string in readConfigFromPath with the
PROJECT_SUPERSET_DIR_NAME constant: import { PROJECT_SUPERSET_DIR_NAME } from
"shared/constants" at the top of the file and change the join call in
readConfigFromPath to use PROJECT_SUPERSET_DIR_NAME (i.e., join(basePath,
PROJECT_SUPERSET_DIR_NAME, "config.json")) so it matches setup.ts and avoids
string drift.

…ort from setup

Removes the duplicated readConfigFromPath/loadSetupConfig from teardown.ts
in favor of importing the shared version from setup.ts. Also adds teardown
field validation to the shared reader and fixes the hardcoded ".superset"
string (now uses PROJECT_SUPERSET_DIR_NAME constant).
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@apps/desktop/src/lib/trpc/routers/workspaces/utils/setup.ts`:
- Around line 69-74: loadSetupConfig currently treats a malformed worktree
config the same as a missing file because readConfigFromPath returns null on
both cases; change readConfigFromPath to throw an error for parse/validation
failures (but still return null when file is not found), then update
loadSetupConfig to call readConfigFromPath(worktreePath) inside a try/catch: if
it returns a config return it, if it throws rethrow or surface the error (do not
silently fall back to mainRepoPath), and only call
readConfigFromPath(mainRepoPath) when the worktree read returned null
(file-not-found) so broken worktree configs are not ignored; reference
readConfigFromPath and loadSetupConfig and the worktreePath/mainRepoPath
fallback logic when making the change.
🧹 Nitpick comments (2)
apps/desktop/src/lib/trpc/routers/workspaces/utils/setup.ts (2)

43-51: Validation doesn't verify array elements are strings.

The error messages claim "array of strings" but only Array.isArray is checked. A config like { "setup": [123, null] } would pass validation. Consider validating element types too, or using a Zod schema for the config shape.

♻️ Suggested improvement
-		if (parsed.setup && !Array.isArray(parsed.setup)) {
-			throw new Error("'setup' field must be an array of strings");
-		}
-
-		if (parsed.teardown && !Array.isArray(parsed.teardown)) {
-			throw new Error("'teardown' field must be an array of strings");
-		}
+		if (
+			parsed.setup &&
+			(!Array.isArray(parsed.setup) ||
+				!parsed.setup.every((s) => typeof s === "string"))
+		) {
+			throw new Error("'setup' field must be an array of strings");
+		}
+
+		if (
+			parsed.teardown &&
+			(!Array.isArray(parsed.teardown) ||
+				!parsed.teardown.every((s) => typeof s === "string"))
+		) {
+			throw new Error("'teardown' field must be an array of strings");
+		}

54-59: Console log missing [domain/operation] prefix.

Per coding guidelines, use prefixed console logging: e.g., [workspace/setup]. The same applies to the existing log in copySupersetConfigToWorktree (line 24), though that's outside this diff's scope.

♻️ Suggested fix
 		console.error(
-			`Failed to read setup config at ${configPath}: ${error instanceof Error ? error.message : String(error)}`,
+			`[workspace/setup] Failed to read setup config at ${configPath}: ${error instanceof Error ? error.message : String(error)}`,
 		);

As per coding guidelines, "Use prefixed console logging with consistent context pattern: [domain/operation] message for entry/exit of significant operations, external API calls, and error conditions".

Comment on lines +69 to +74
if (worktreePath) {
const config = readConfigFromPath(worktreePath);
if (config) return config;
}

return readConfigFromPath(mainRepoPath);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Silent fallback when worktree config is malformed could mask errors.

If the worktree config file exists but contains invalid JSON or fails validation, readConfigFromPath returns null, and loadSetupConfig silently falls back to mainRepoPath. This means a broken worktree config is quietly ignored in favor of the main config — the exact scenario this PR aims to prevent.

Consider distinguishing "file not found" (legitimate fallback) from "file exists but is invalid" (should not fall back). One approach: have readConfigFromPath throw on parse/validation errors instead of returning null, and only return null for the missing-file case. Then loadSetupConfig can catch and decide.

♻️ Sketch of the approach
 function readConfigFromPath(basePath: string): SetupConfig | null {
 	const configPath = join(basePath, PROJECT_SUPERSET_DIR_NAME, CONFIG_FILE_NAME);
 
 	if (!existsSync(configPath)) {
 		return null;
 	}
 
-	try {
-		const content = readFileSync(configPath, "utf-8");
-		const parsed = JSON.parse(content) as SetupConfig;
-		// ... validation ...
-		return parsed;
-	} catch (error) {
-		console.error(...);
-		return null;
-	}
+	const content = readFileSync(configPath, "utf-8");
+	const parsed = JSON.parse(content) as SetupConfig;
+	// ... validation ...
+	return parsed;
 }
 
 export function loadSetupConfig({ mainRepoPath, worktreePath }: { ... }): SetupConfig | null {
 	if (worktreePath) {
-		const config = readConfigFromPath(worktreePath);
-		if (config) return config;
+		try {
+			const config = readConfigFromPath(worktreePath);
+			if (config) return config;
+		} catch (error) {
+			console.error(`[workspace/setup] Invalid config in worktree at ${worktreePath}: ${error instanceof Error ? error.message : String(error)}`);
+			return null; // Don't fall back to main — worktree config is authoritative but broken
+		}
 	}
 
-	return readConfigFromPath(mainRepoPath);
+	try {
+		return readConfigFromPath(mainRepoPath);
+	} catch (error) {
+		console.error(`[workspace/setup] Invalid config in main repo at ${mainRepoPath}: ${error instanceof Error ? error.message : String(error)}`);
+		return null;
+	}
 }
🤖 Prompt for AI Agents
In `@apps/desktop/src/lib/trpc/routers/workspaces/utils/setup.ts` around lines 69
- 74, loadSetupConfig currently treats a malformed worktree config the same as a
missing file because readConfigFromPath returns null on both cases; change
readConfigFromPath to throw an error for parse/validation failures (but still
return null when file is not found), then update loadSetupConfig to call
readConfigFromPath(worktreePath) inside a try/catch: if it returns a config
return it, if it throws rethrow or surface the error (do not silently fall back
to mainRepoPath), and only call readConfigFromPath(mainRepoPath) when the
worktree read returned null (file-not-found) so broken worktree configs are not
ignored; reference readConfigFromPath and loadSetupConfig and the
worktreePath/mainRepoPath fallback logic when making the change.

@Kitenite Kitenite merged commit cdda5bb into superset-sh:main Feb 6, 2026
2 of 5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants