Skip to content

Commit a720f17

Browse files
committed
feat(session): auto-scope DerivedData per workspace/project path
When derivedDataPath is not explicitly set but workspacePath or projectPath is known, the session store computes a workspace-scoped subdirectory under DerivedData using a name+hash scheme (e.g. MyApp-4ee6552f04c9). This prevents concurrent sessions building different clones of the same project from colliding on a shared build directory. Closes #340
1 parent c40789b commit a720f17

File tree

3 files changed

+52
-2
lines changed

3 files changed

+52
-2
lines changed

src/mcp/tools/session-management/__tests__/session_clear_defaults.test.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,8 @@ describe('session-clear-defaults tool', () => {
4444
const current = sessionStore.getAll();
4545
expect(current.scheme).toBeUndefined();
4646
expect(current.deviceId).toBeUndefined();
47-
expect(current.derivedDataPath).toBeUndefined();
47+
// derivedDataPath is computed from projectPath when not explicitly set
48+
expect(current.derivedDataPath).toContain('proj-');
4849
expect(current.projectPath).toBe('/path/to/proj.xcodeproj');
4950
expect(current.simulatorName).toBe('iPhone 17');
5051
expect(current.useLatestOS).toBe(true);

src/utils/__tests__/session-store.test.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,4 +121,38 @@ describe('SessionStore', () => {
121121
const stored = sessionStore.getAll();
122122
expect(stored.env).toEqual({ API_KEY: 'secret' });
123123
});
124+
125+
it('computes a workspace-scoped derivedDataPath when workspacePath is set', () => {
126+
sessionStore.setDefaults({ workspacePath: '/Users/dev/clone-1/MyApp.xcworkspace' });
127+
128+
const defaults = sessionStore.getAll();
129+
expect(defaults.derivedDataPath).toMatch(/MyApp-[a-f0-9]{12}$/);
130+
});
131+
132+
it('computes a project-scoped derivedDataPath when projectPath is set', () => {
133+
sessionStore.setDefaults({ projectPath: '/Users/dev/clone-2/MyApp.xcodeproj' });
134+
135+
const defaults = sessionStore.getAll();
136+
expect(defaults.derivedDataPath).toMatch(/MyApp-[a-f0-9]{12}$/);
137+
});
138+
139+
it('does not override an explicitly set derivedDataPath', () => {
140+
sessionStore.setDefaults({
141+
workspacePath: '/Users/dev/clone-1/MyApp.xcworkspace',
142+
derivedDataPath: '/custom/path',
143+
});
144+
145+
expect(sessionStore.getAll().derivedDataPath).toBe('/custom/path');
146+
});
147+
148+
it('produces different hashes for different workspace paths', () => {
149+
sessionStore.setDefaults({ workspacePath: '/clone-1/MyApp.xcworkspace' });
150+
const path1 = sessionStore.getAll().derivedDataPath;
151+
152+
sessionStore.clearAll();
153+
sessionStore.setDefaults({ workspacePath: '/clone-2/MyApp.xcworkspace' });
154+
const path2 = sessionStore.getAll().derivedDataPath;
155+
156+
expect(path1).not.toBe(path2);
157+
});
124158
});

src/utils/session-store.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
import * as crypto from 'node:crypto';
2+
import * as path from 'node:path';
3+
import { DERIVED_DATA_DIR } from './log-paths.ts';
14
import { log } from './logger.ts';
25

36
export type SessionDefaults = {
@@ -133,7 +136,19 @@ class SessionStore {
133136

134137
getAllForProfile(profile: string | null): SessionDefaults {
135138
const defaults = profile === null ? this.globalDefaults : (this.profiles[profile] ?? {});
136-
return this.cloneDefaults(defaults);
139+
const result = this.cloneDefaults(defaults);
140+
141+
if (!result.derivedDataPath) {
142+
const anchor = result.workspacePath ?? result.projectPath;
143+
if (anchor) {
144+
const resolved = path.resolve(anchor);
145+
const hash = crypto.createHash('sha256').update(resolved).digest('hex').slice(0, 12);
146+
const name = path.basename(resolved, path.extname(resolved));
147+
result.derivedDataPath = path.join(DERIVED_DATA_DIR, `${name}-${hash}`);
148+
}
149+
}
150+
151+
return result;
137152
}
138153

139154
listProfiles(): string[] {

0 commit comments

Comments
 (0)