fix(apple): fall back to the default Xcode past a dangling selection link
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
realPathswallowedEACCES. Both are fixed: that case is dropped and anEACCESnegative is added. - Pre-existing flake, not from this change:
packages/apple-runner/src/__tests__/index-recovery-wiring.test.tsfails 39 tests together in about 1 of 6 isolated runs at base32bf98def5. - Not verified on a live device: this host has a valid selection link, and making a dangling
/var/db/xcode_select_linkneeds root. The normal path (link resolves) is unchanged.