Skip to content

Don't fail when no Xcode is selected - #495

Open
efim0v wants to merge 1 commit into
XcodesOrg:mainfrom
efim0v:fix/210-no-selected-xcode
Open

efim0v wants to merge 1 commit into
XcodesOrg:mainfrom
efim0v:fix/210-no-selected-xcode

Conversation

@efim0v

@efim0v efim0v commented Oct 13, 2026

Copy link
Copy Markdown

Summary

Fixes #210.

On a Mac with no developer directory selected (no Xcode and no Command Line Tools, which is what a new Mac looks like), xcode-select -p exits with status 2. xcodes list, update, installed and select (with or without -p) all run it, so they abort with that error. xcodes install --select installs Xcode and then fails at the select step for the same reason.

With this change, a failing xcode-select -p means "no selected Xcode":

  • list, update and installed print the list without a selected marker
  • select -p prints the existing No selected Xcode message
  • select selects the requested Xcode, or shows the interactive list when no version or path is given

Nothing changes when xcode-select -p succeeds.

Root cause

printAvailableXcodes, printInstalledXcodes and selectXcodeAsync start with try await Current.shell.xcodeSelectPrintPath(). XcodesProcess.run throws ProcessExecutionError for a non-zero exit status, and nothing caught it. The selected path is only used to mark the selected Xcode (and, in select, to detect "already selected"), but its absence failed the whole command.

The fix

  • Environment.swift: new Shell.selectedXcodePath(), which calls xcodeSelectPrintPath and returns nil when it throws ProcessExecutionError. Other errors still propagate. It follows the same pattern as authenticateSudoerIfNecessaryAsync, so tests keep mocking xcodeSelectPrintPath.
  • XcodeInstaller.swift: printAvailableXcodes and printInstalledXcodes pass the optional path to XcodeListPresentationService, which already treats nil as "nothing selected".
  • XcodeSelect.swift: selectXcodeAsync uses the helper and passes "" to XcodeSelectionService when nothing is selected. XcodeSelectionService.request compares the trimmed request with the trimmed selected path, so with nothing selected, xcodes select without arguments would come back as .alreadySelectedPath and print Xcode at path is already selected. That case is now only honoured when a path is actually selected. Otherwise it is handled like .selectPath, which falls back to the interactive list as it does today. Another option would be an optional selected path in XcodesKit's XcodeSelectionService.request. I kept the change in this repo so it works with the current XcodesKit 1.1.x.

Testing

  • Added four tests to XcodesKitTests that stub xcodeSelectPrintPath to throw ProcessExecutionError with status 2, which is what XcodesProcess.run throws for that exit:

    • test_PrintAvailableXcodes_WithNoSelectedXcode_PrintsXcodes
    • test_Installed_WithNoSelectedXcode
    • test_SelectPrint_WithNoSelectedXcode
    • test_SelectInteractively_WithNoSelectedXcode

    On main all four fail with the thrown ProcessExecutionError. With this change they pass. Before I added the .alreadySelectedPath handling, the interactive test failed with Xcode at path is already selected.

  • Ran swift test locally with Swift 6.4 (Xcode 27.0): 90 tests, 85 pass. The 5 failures are test_InstallLogging_*, and they fail the same way on main (86 tests, same 5 failures). My Mac's region setting formats the download percentage as 1 % with a non-breaking space, while the fixtures expect 1%. Those tests don't go through the code changed here, and CI on main is green.

  • The new code builds without warnings (swift build --build-tests -Xswiftc -warnings-as-errors).

  • Ran the built xcodes select -p and xcodes installed on a Mac that has Xcode selected. The output is the same as before, and the selected Xcode is still marked.

  • I didn't test on a Mac with no developer directory. That needs a machine without /Applications/Xcode.app, without the Command Line Tools and without an xcode-select -s path. DEVELOPER_DIR doesn't simulate it, because xcode-select -p just prints whatever it is set to.

`xcode-select -p` exits with status 2 when no developer directory is
selected, e.g. on a new Mac without Xcode or the Command Line Tools.
`list`, `update`, `installed` and `select` (also used by
`install --select`) all ran it and aborted on that error.

Treat that failure as "no selected Xcode" instead: the lists are
printed without a selected marker, `select -p` prints "No selected
Xcode", and `select` goes on to select the requested Xcode.
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.

Don't error when using xcode-select to find current selected version.

1 participant