Skip to content

Commit 95fa5ef

Browse files
committed
šŸ› fix(setup): show debug-gated workflows when existing config enables debug
Pass existing project config into workflow option evaluation so that debug-gated workflows (e.g. doctor) appear in the setup wizard when the user's config already has debug: true.
1 parent 9044daf commit 95fa5ef

2 files changed

Lines changed: 101 additions & 2 deletions

File tree

ā€Žsrc/cli/commands/__tests__/setup.test.tsā€Ž

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,13 +124,106 @@ describe('setup command', () => {
124124
};
125125

126126
expect(parsed.enabledWorkflows?.length).toBeGreaterThan(0);
127+
expect(parsed.enabledWorkflows).not.toContain('doctor');
127128
expect(parsed.debug).toBe(false);
128129
expect(parsed.sentryDisabled).toBe(false);
129130
expect(parsed.sessionDefaults?.workspacePath).toBe('App.xcworkspace');
130131
expect(parsed.sessionDefaults?.scheme).toBe('App');
131132
expect(parsed.sessionDefaults?.simulatorId).toBe('SIM-1');
132133
});
133134

135+
it('shows debug-gated workflows when existing config enables debug', async () => {
136+
let storedConfig = 'schemaVersion: 1\ndebug: true\n';
137+
let offeredWorkflowIds: string[] = [];
138+
139+
const fs = createMockFileSystemExecutor({
140+
existsSync: (targetPath) => targetPath === configPath && storedConfig.length > 0,
141+
stat: async () => ({ isDirectory: () => true, mtimeMs: 0 }),
142+
readdir: async (targetPath) => {
143+
if (targetPath === cwd) {
144+
return [
145+
{
146+
name: 'App.xcworkspace',
147+
isDirectory: () => true,
148+
isSymbolicLink: () => false,
149+
},
150+
];
151+
}
152+
153+
return [];
154+
},
155+
readFile: async (targetPath) => {
156+
if (targetPath !== configPath) {
157+
throw new Error(`Unexpected read path: ${targetPath}`);
158+
}
159+
return storedConfig;
160+
},
161+
writeFile: async (targetPath, content) => {
162+
if (targetPath !== configPath) {
163+
throw new Error(`Unexpected write path: ${targetPath}`);
164+
}
165+
storedConfig = content;
166+
},
167+
});
168+
169+
const executor: CommandExecutor = async (command) => {
170+
if (command.includes('--json')) {
171+
return createMockCommandResponse({
172+
success: true,
173+
output: JSON.stringify({
174+
devices: {
175+
'iOS 17.0': [
176+
{
177+
name: 'iPhone 15',
178+
udid: 'SIM-1',
179+
state: 'Shutdown',
180+
isAvailable: true,
181+
},
182+
],
183+
},
184+
}),
185+
});
186+
}
187+
188+
if (command[0] === 'xcrun') {
189+
return createMockCommandResponse({
190+
success: true,
191+
output: `== Devices ==\n-- iOS 17.0 --\n iPhone 15 (SIM-1) (Shutdown)`,
192+
});
193+
}
194+
195+
return createMockCommandResponse({
196+
success: true,
197+
output: `Information about workspace "App":\n Schemes:\n App`,
198+
});
199+
};
200+
201+
const prompter: Prompter = {
202+
selectOne: async <T>(opts: { options: Array<{ value: T }> }) => opts.options[0].value,
203+
selectMany: async <T>(opts: { options: Array<{ value: T }> }) => {
204+
offeredWorkflowIds = opts.options.map((option) => String(option.value));
205+
return opts.options.map((option) => option.value);
206+
},
207+
confirm: async (opts: { defaultValue: boolean }) => opts.defaultValue,
208+
};
209+
210+
await runSetupWizard({
211+
cwd,
212+
fs,
213+
executor,
214+
prompter,
215+
quietOutput: true,
216+
});
217+
218+
const parsed = parseYaml(storedConfig) as {
219+
debug?: boolean;
220+
enabledWorkflows?: string[];
221+
};
222+
223+
expect(parsed.debug).toBe(true);
224+
expect(offeredWorkflowIds).toContain('doctor');
225+
});
226+
134227
it('fails fast when Xcode command line tools are unavailable', async () => {
135228
const failingExecutor: CommandExecutor = async (command) => {
136229
if (command[0] === 'xcodebuild') {

ā€Žsrc/cli/commands/setup.tsā€Ž

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -118,14 +118,18 @@ function normalizeExistingDefaults(config?: ProjectConfig): {
118118
};
119119
}
120120

121-
function getWorkflowOptions(debug: boolean): WorkflowManifestEntry[] {
121+
function getWorkflowOptions(
122+
debug: boolean,
123+
existingConfig?: ProjectConfig,
124+
): WorkflowManifestEntry[] {
122125
const manifest = loadManifest();
123126
const config = getConfig();
124127

125128
const predicateContext = {
126129
runtime: 'mcp' as const,
127130
config: {
128131
...config,
132+
...existingConfig,
129133
debug,
130134
},
131135
runningUnderXcode: false,
@@ -197,11 +201,12 @@ function getChangedFields(
197201

198202
async function selectWorkflowIds(opts: {
199203
debug: boolean;
204+
existingConfig?: ProjectConfig;
200205
existingEnabledWorkflows: string[];
201206
prompter: Prompter;
202207
quietOutput: boolean;
203208
}): Promise<string[]> {
204-
const workflows = getWorkflowOptions(opts.debug);
209+
const workflows = getWorkflowOptions(opts.debug, opts.existingConfig);
205210
if (workflows.length === 0) {
206211
return [];
207212
}
@@ -427,6 +432,7 @@ async function collectSetupSelection(
427432

428433
const enabledWorkflows = await selectWorkflowIds({
429434
debug,
435+
existingConfig,
430436
existingEnabledWorkflows: existingConfig?.enabledWorkflows ?? [],
431437
prompter: deps.prompter,
432438
quietOutput: deps.quietOutput,

0 commit comments

Comments
Ā (0)