fix(apple): fall back to the default Xcode past a dangling selection link

Merged
TH
15h ago
+68-25

Summary

Fixes #4145. In 0.21.34+, iOS input fails with apple_toolchain_unreadable when /var/db/xcode_select_link points at a deleted Xcode, even though xcode-select -p and xcrun resolve /Applications/Xcode.app.

How it regressed: #3995 stopped running xcrun to build the runner cache key and reads Xcode's files instead. To do that it copied Apple's developer-dir resolution: DEVELOPER_DIR, then the selection link, then the default Xcode. One rule did not match Apple: a dangling link was treated as a hard failure. Apple's tools skip it and use the default Xcode. A test (a selection link whose Xcode is gone fails …) locked in that assumption, so nothing caught it.

Fix: if the link's target is missing (ENOENT), the link is treated as absent, just like a missing link, and resolution uses the default Xcode. Any other unreadable link, such as EACCES on the link or its target, still fails. That keeps the #3995 guarantee: an unreadable selection never silently stands in for another Xcode.

Prevention:

  • The resolver doc now says the order must match xcode-select -p, because the cache key must name the Xcode that builds the runner.
  • The incorrect test is replaced with one that pins Apple's behavior: a dangling link falls back to the default Xcode, for both the cache key and the failure-report version.
  • New tests cover the default Xcode with no link, and the closest negative: a link target that cannot be read fails and does not fall back.
  • The test fixture now stubs the host's default Xcode path as well as the selection link. Cases no longer depend on whatever Xcode the machine has installed.

Validation

  • pnpm check:affected --run --head 031c78fc13: all runnable checks passed, exactCommit=true (pre-squash head, same tree as the pushed commit).
  • The new fallback test fails on the old code (COMMAND_FAILED, dangling selection link) and passes with the fix.
  • Independent review: it found the first draft's "no default Xcode" case was host-dependent and that realPath swallowed EACCES. Both are fixed: that case is dropped and an EACCES negative is added.
  • Pre-existing flake, not from this change: packages/apple-runner/src/__tests__/index-recovery-wiring.test.ts fails 39 tests together in about 1 of 6 isolated runs at base 32bf98def5.
  • Not verified on a live device: this host has a valid selection link, and making a dangling /var/db/xcode_select_link needs root. The normal path (link resolves) is unchanged.