Skip to content

Conversation

@cameroncooke
Copy link
Owner

@cameroncooke cameroncooke commented Jan 6, 2026

Fix CocoaPods build path resolution by passing the project directory as the current working directory (cwd) to the xcodebuild command executor.

CocoaPods projects use relative paths in .xcfilelist files. Previously, xcodebuild was not always executed from the project's root directory, causing these relative paths to fail and leading to build errors like "Unable to load contents of file list." This change ensures xcodebuild runs in the correct context, resolving these path issues.


Open in Cursor Open in Web


Note

Ensures CocoaPods-relative paths resolve by running xcodebuild from the project directory.

  • Update executeXcodeBuildCommand to merge execOpts with cwd set to the project dir before calling the executor
  • Enhance createMockExecutor to accept full executor signature and optional onExecute callback; propagate opts
  • Add cwd handling tests in build-utils.test.ts (workspace, project, and execOpts merge)
  • Adjust device and macOS tests to expect opts: { cwd: '/path/to' } instead of timeout and validate command generation

Written by Cursor Bugbot for commit 80f29cc. This will update automatically on new commits. Configure here.

Co-authored-by: web <web@cameroncooke.com>
@cursor
Copy link

cursor bot commented Jan 6, 2026

Cursor Agent can help with this pull request. Just @cursor in comments and I'll start working on changes in this branch.
Learn more about Cursor Agents

@pkg-pr-new
Copy link

pkg-pr-new bot commented Jan 6, 2026

Open in StackBlitz

npm i https://pkg.pr.new/cameroncooke/XcodeBuildMCP/xcodebuildmcp@167

commit: 80f29cc

@cameroncooke cameroncooke marked this pull request as ready for review January 6, 2026 22:39
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 6, 2026

Walkthrough

This pull request refactors the executor interface and related test infrastructure to support passing working directory (cwd) information to build commands. Test files across the device and macOS build tools are updated to replace timeout parameters with an opts object containing an optional cwd field. The mock executor in test utilities is enhanced with an onExecute callback mechanism to capture and verify executor invocations. Production code in build-utils.ts is modified to pass the derived project directory as cwd when executing xcodebuild. New test cases are added to verify correct cwd handling for both workspace and project build scenarios, including preservation of existing environment variables.

Pre-merge checks

✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Cocoapods build path fix' directly and clearly summarizes the main change: fixing CocoaPods build path resolution by passing the project directory as the working directory to xcodebuild.
Description check ✅ Passed The description comprehensively explains the problem, the solution, and the impact, directly relating to all changes in the pull request.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

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
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: 0

Caution

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

⚠️ Outside diff range comments (3)
src/mcp/tools/macos/__tests__/build_run_macos.test.ts (3)

137-152: Inconsistent test migration: timeout vs opts.

Line 151 expects timeout: undefined, but the first executor call (line 134) correctly uses opts: { cwd: '/path/to' }. The second executor call (for build settings) doesn't receive a cwd parameter in the production code, so the expectation should either be opts: undefined or the field should be omitted entirely for clarity.

🔎 Suggested fix
 expect(executorCalls[1]).toEqual({
   command: [
     'xcodebuild',
     '-showBuildSettings',
     '-project',
     '/path/to/project.xcodeproj',
     '-scheme',
     'MyApp',
     '-configuration',
     'Debug',
   ],
   description: 'Get Build Settings for Launch',
   logOutput: true,
-  timeout: undefined,
+  opts: undefined,
 });

233-248: Inconsistent test migration: timeout vs opts.

Line 247 has the same issue as line 151 - it expects timeout: undefined whilst the executor signature has been updated to use opts. For consistency with the migrated tests, this should be updated to match the new parameter structure.

🔎 Suggested fix
 expect(executorCalls[1]).toEqual({
   command: [
     'xcodebuild',
     '-showBuildSettings',
     '-workspace',
     '/path/to/workspace.xcworkspace',
     '-scheme',
     'MyApp',
     '-configuration',
     'Debug',
   ],
   description: 'Get Build Settings for Launch',
   logOutput: true,
-  timeout: undefined,
+  opts: undefined,
 });

294-320: Incomplete test migration to opts parameter.

These test cases still use timeout?: number in their mock executor signatures (lines 301, 357, 418) whilst other tests in this file have been migrated to use opts?: { cwd?: string }. For consistency and to align with the broader changes in this PR, these should be updated to use the new opts parameter structure.

🔎 Example fix for one test case (lines 294-320)
 const mockExecutor = (
   command: string[],
   description: string,
   logOutput: boolean,
-  timeout?: number,
+  opts?: { cwd?: string },
 ) => {
   callCount++;
   if (callCount === 1) {
     // First call for build succeeds
     return Promise.resolve({
       success: true,
       output: 'BUILD SUCCEEDED',
       error: '',
     });
   } else if (callCount === 2) {
     // Second call for build settings fails
     return Promise.resolve({
       success: false,
       output: '',
       error: 'error: Failed to get settings',
     });
   }
   return Promise.resolve({ success: true, output: '', error: '' });
 };

Apply similar changes to the other test cases at lines 350-383 and 413-438.

Also applies to: 350-383, 413-438

🧹 Nitpick comments (2)
src/utils/build-utils.ts (1)

102-109: Consider validating projectDir before use.

The projectDir is derived using path.dirname() but isn't validated before being passed as cwd. If workspacePath or projectPath are at the root level or malformed, projectDir could be an empty string or unexpected value, potentially causing the executor to fail silently or use an unintended directory.

🔎 Suggested validation
 let projectDir = '';
 if (params.workspacePath) {
   projectDir = path.dirname(params.workspacePath);
   command.push('-workspace', params.workspacePath);
 } else if (params.projectPath) {
   projectDir = path.dirname(params.projectPath);
   command.push('-project', params.projectPath);
 }
+
+if (!projectDir || projectDir === '/') {
+  log('warning', `Derived project directory is invalid: '${projectDir}'. Using current directory.`);
+  projectDir = '.';
+}
src/utils/__tests__/build-utils.test.ts (1)

264-347: Excellent test coverage for cwd handling, with a minor type safety suggestion.

The three test cases comprehensively verify:

  1. Workspace builds receive the correct cwd
  2. Project builds receive the correct cwd
  3. Existing execOpts.env is preserved when merging with cwd

The use of the onExecute callback for observation aligns with the DI testing philosophy from learnings.

💡 Optional: Improve type safety

Consider replacing any with a proper type for capturedOptions:

-let capturedOptions: any;
+let capturedOptions: { cwd?: string; env?: Record<string, string> } | undefined;

This would provide better type checking whilst maintaining test clarity.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6b23a50 and 80f29cc.

📒 Files selected for processing (5)
  • src/mcp/tools/device/__tests__/build_device.test.ts
  • src/mcp/tools/macos/__tests__/build_run_macos.test.ts
  • src/test-utils/mock-executors.ts
  • src/utils/__tests__/build-utils.test.ts
  • src/utils/build-utils.ts
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Use TypeScript file extensions (.ts) for all relative imports to ensure compatibility with native TypeScript runtimes
Never use .js extensions for internal file imports; always use .ts extensions
Use .js extensions only for external package imports (e.g., @modelcontextprotocol/sdk/server/mcp.js)

Files:

  • src/utils/__tests__/build-utils.test.ts
  • src/utils/build-utils.ts
  • src/mcp/tools/device/__tests__/build_device.test.ts
  • src/mcp/tools/macos/__tests__/build_run_macos.test.ts
  • src/test-utils/mock-executors.ts
**/*.test.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.test.{ts,tsx}: Enforce strict Dependency Injection (DI) testing philosophy - completely ban the use of Vitest mocking utilities (vi.mock(), vi.fn(), vi.spyOn(), etc.)
Use injectable executors (CommandExecutor and FileSystemExecutor) for all external interactions instead of mocking
Import core logic functions from tool files and pass mock executors to simulate different test outcomes

Files:

  • src/utils/__tests__/build-utils.test.ts
  • src/mcp/tools/device/__tests__/build_device.test.ts
  • src/mcp/tools/macos/__tests__/build_run_macos.test.ts
src/**/*.ts

📄 CodeRabbit inference engine (AGENTS.md)

When Claude Code is detected, automatically consolidate multiple content blocks into a single text response separated by --- dividers to work around MCP specification violation

Files:

  • src/utils/__tests__/build-utils.test.ts
  • src/utils/build-utils.ts
  • src/mcp/tools/device/__tests__/build_device.test.ts
  • src/mcp/tools/macos/__tests__/build_run_macos.test.ts
  • src/test-utils/mock-executors.ts
🧠 Learnings (4)
📓 Common learnings
Learnt from: CR
Repo: cameroncooke/XcodeBuildMCP PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-05T22:47:54.969Z
Learning: Applies to **/*.test.{ts,tsx} : Use injectable executors (CommandExecutor and FileSystemExecutor) for all external interactions instead of mocking
Learnt from: CR
Repo: cameroncooke/XcodeBuildMCP PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-05T22:47:54.969Z
Learning: Applies to **/*.test.{ts,tsx} : Import core logic functions from tool files and pass mock executors to simulate different test outcomes
📚 Learning: 2026-01-05T22:47:54.969Z
Learnt from: CR
Repo: cameroncooke/XcodeBuildMCP PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-05T22:47:54.969Z
Learning: Applies to **/*.test.{ts,tsx} : Import core logic functions from tool files and pass mock executors to simulate different test outcomes

Applied to files:

  • src/utils/__tests__/build-utils.test.ts
  • src/mcp/tools/device/__tests__/build_device.test.ts
  • src/mcp/tools/macos/__tests__/build_run_macos.test.ts
  • src/test-utils/mock-executors.ts
📚 Learning: 2026-01-05T22:47:54.969Z
Learnt from: CR
Repo: cameroncooke/XcodeBuildMCP PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-05T22:47:54.969Z
Learning: Applies to **/*.test.{ts,tsx} : Use injectable executors (CommandExecutor and FileSystemExecutor) for all external interactions instead of mocking

Applied to files:

  • src/utils/__tests__/build-utils.test.ts
  • src/utils/build-utils.ts
  • src/mcp/tools/device/__tests__/build_device.test.ts
  • src/mcp/tools/macos/__tests__/build_run_macos.test.ts
  • src/test-utils/mock-executors.ts
📚 Learning: 2026-01-05T22:47:54.969Z
Learnt from: CR
Repo: cameroncooke/XcodeBuildMCP PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-05T22:47:54.969Z
Learning: Applies to src/tools/**/*.ts : Organize tools into directories by their functionality and use automatic loading via plugin-based MCP architecture

Applied to files:

  • src/mcp/tools/device/__tests__/build_device.test.ts
🧬 Code graph analysis (1)
src/utils/__tests__/build-utils.test.ts (2)
src/test-utils/mock-executors.ts (1)
  • createMockExecutor (27-89)
src/utils/build-utils.ts (1)
  • executeXcodeBuildCommand (45-373)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Cursor Bugbot
🔇 Additional comments (5)
src/test-utils/mock-executors.ts (1)

36-88: LGTM! Clean test observability enhancement.

The onExecute callback addition enables tests to capture and verify executor invocations without violating the DI testing philosophy. The implementation is backward compatible and properly propagates the new opts parameter (including cwd and env) to test observers.

Based on learnings, this aligns with the injectable executor pattern for test infrastructure.

src/utils/build-utils.ts (1)

230-234: LGTM! Core fix correctly implements the PR objective.

The code properly passes the derived projectDir as cwd to the executor, ensuring CocoaPods relative paths resolve correctly. The spread operator correctly preserves any existing execOpts whilst setting the working directory.

src/mcp/tools/device/__tests__/build_device.test.ts (2)

130-180: LGTM! Comprehensive test coverage for workspace cwd handling.

The test correctly verifies that the executor receives cwd: '/path/to' when building with a workspace at /path/to/MyProject.xcworkspace. The stub executor signature properly captures the new opts parameter.


182-232: LGTM! Comprehensive test coverage for project cwd handling.

The test correctly verifies that the executor receives cwd: '/path/to' when building with a project at /path/to/MyProject.xcodeproj. Consistent with the workspace test pattern.

src/mcp/tools/macos/__tests__/build_run_macos.test.ts (1)

76-171: LGTM! Consistent test updates for cwd handling.

These test cases correctly verify that the first executor call (for the build) receives opts: { cwd: '/path/to' }, ensuring the working directory is properly set for CocoaPods relative path resolution. The test coverage includes both project and workspace scenarios.

Also applies to: 173-267

description: 'macOS Build',
logOutput: true,
timeout: undefined,
opts: { cwd: '/path/to' },
Copy link

Choose a reason for hiding this comment

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

Test expectations use timeout but stub tracks opts

Medium Severity

The mock executor stub was refactored to track opts instead of timeout (line 88 pushes { command, description, logOutput, opts }), but the test expectations for executorCalls[1] at lines 151 and 247 still assert timeout: undefined. This mismatch causes the toEqual assertion to fail because the actual object has an opts property while the expected object has a timeout property. These should be changed to opts: undefined to match the stub's tracking structure.

Additional Locations (1)

Fix in Cursor Fix in Web

@cameroncooke cameroncooke merged commit 8333c1a into main Jan 7, 2026
8 checks passed
@cameroncooke cameroncooke deleted the cursor/cocoapods-build-path-fix-437e branch January 7, 2026 21:44
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.

3 participants