Skip to content

fix(app): pin an iOS build destination and validate prerequisites - #11793

Merged
kodjima33 merged 2 commits into
BasedHardware:mainfrom
formed2forge:fix/setup-sh-device-pinning
Aug 18, 2026
Merged

fix(app): pin an iOS build destination and validate prerequisites#11793
kodjima33 merged 2 commits into
BasedHardware:mainfrom
formed2forge:fix/setup-sh-device-pinning

Conversation

@formed2forge

@formed2forge formed2forge commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

Summary

Two related gaps in app/setup.sh, both hit before any app code matters:

  1. run_build_ios() never pinned a target device. With no -d, Flutter picked a destination itself. On a machine with no iOS simulator runtime installed and only a wirelessly-paired phone visible, it silently built for macOS desktop instead — after a full pod install --repo-update and build_runner pass — and failed with an unrelated "No macOS desktop project configured" error.
  2. setup.sh prints a prerequisite list but validates almost none of it. Xcode v16.4, CocoaPods v1.16.2, Flutter v3.44.5 are documented in the script's own header, but the whole script has exactly one command -v check (for fastlane). A missing or outdated tool surfaces as a confusing downstream failure instead of a named error — unlike the dev harness's own Cannot start; missing prerequisites: pattern, which names each gap with a remedy.

What changed

  • select_ios_device() enumerates iOS-platform destinations from flutter devices --machine (via jq): returns the one candidate directly, prompts when there are several (failing fast without a TTY — same reasoning as detect_apple_team_id's existing prompt), and fails with a named error instead of a silent fallback when there are none.
  • check_ios_prerequisites() validates Flutter/Xcode/CocoaPods/jq against the versions the script already documents, listing every gap at once with a remedy, before any of the slow setup steps run.
  • run_build_ios() now calls both before doing any real work, and passes the resolved -d <device_id> through to flutter run.

Testing

  • New shell tests: app/test/shell/ios_device_selection_test.sh (single device returns directly; zero devices fails named; multiple devices with no TTY fails fast, not hangs; empty device list fails named) and app/test/shell/ios_prerequisites_test.sh (_version_at_least comparisons; each of the four prerequisites individually missing/outdated with the correct remedy; all four missing at once).
  • Verified live, not just in the new tests — ran the real bash setup.sh ios three ways:
    • Non-interactively with two real devices connected (a physical iPhone + a booted simulator) → correctly failed fast with the multi-device prompt rather than hang.
    • Through a real pty, feeding the interactive prompt "2" → correctly proceeded through pod install/build_runner and reached Launching lib/main.dart on iPhone 17 Pro in debug mode... — the exact device chosen.
    • With a stubbed toolchain reporting only a macOS destination → correctly failed with the named "No iOS device or simulator found" error instead of the original silent-fallback bug.

Failure-Class: none

Fixes #11775.

Review in cubic

Two related gaps in setup.sh, both hit before any app code matters:

1. run_build_ios() never passed -d to `flutter run`, so Flutter picked
   a destination itself. On a machine with no iOS simulator runtime
   installed and only a wirelessly-paired phone visible, it silently
   built for macOS desktop instead — after a full pod install
   --repo-update and build_runner pass — and failed with an unrelated
   "No macOS desktop project configured" error (BasedHardware#11775).

2. setup.sh prints a prerequisite list (Xcode v16.4, CocoaPods
   v1.16.2, Flutter v3.44.5) but validated almost none of it — one
   `command -v` check in the whole script. A missing or outdated tool
   surfaced as a confusing downstream failure instead of a named error,
   unlike the dev harness's own `Cannot start; missing prerequisites:`
   pattern, which names each gap with a remedy.

select_ios_device() enumerates iOS-platform destinations from `flutter
devices --machine`, returns the one candidate directly, prompts when
there are several (failing fast without a TTY, same reasoning as
detect_apple_team_id's prompt), and fails with a named error instead
of a silent fallback when there are none. check_ios_prerequisites()
validates Flutter/Xcode/CocoaPods/jq against the versions the script
already documents, listing every gap at once with its remedy.

Verified live, not just in the new shell tests: ran the real `bash
setup.sh ios` three ways — non-interactively with two real devices
connected (correctly failed fast rather than hang), through a real pty
feeding the interactive prompt (correctly built and reached `Launching
lib/main.dart on iPhone 17 Pro`, the device actually chosen), and with
a stubbed toolchain reporting only macOS as a destination (correctly
failed with the named "no iOS device or simulator found" error instead
of the original silent-fallback bug).

Failure-Class: none

Fixes BasedHardware#11775.
@Git-on-my-level

Copy link
Copy Markdown
Collaborator

Thanks @formed2forge — reviewed head 2847da1. Well-scoped fix with real regression tests; I verified the key claims and ran the new suites.

Verified against main and the head:

  • run_build_ios() on main really passes no -d to flutter run, so Flutter's silent destination pick (macOS desktop when no simulator runtime is installed) is a real failure mode, not a hypothetical.
  • The enforced floors in check_ios_prerequisites() (Flutter 3.44.5, Xcode 16.4, CocoaPods 1.16.2) match setup.sh's own header exactly — the check enforces documented policy rather than inventing new minimums.
  • _version_at_least()'s sort -V reliance: macOS's BSD-lineage /usr/bin/sort documents -V, --version-sort, and any Mac that can run the required Xcode 16.4 has it — the comment's portability claim holds.
  • The Cannot start; missing prerequisites: precedent cited in the comment exists in scripts/dev-harness/dev_harness/cli.py.
  • Ran both new suites hermetically against the head's setup.sh: app/test/shell/ios_device_selection_test.sh 4/4 (single device, zero devices, multi-device no-TTY fail-fast, empty machine list) and app/test/shell/ios_prerequisites_test.sh 10/10 (version comparisons, all-met, each named failure, all-four-missing-at-once). The function-extraction + stubbed-PATH harness design is genuinely behavioral — nice.

Non-blocking notes:

  • check_ios_prerequisites(): if xcodebuild exists but is broken (license unaccepted, missing components), xcodebuild -version returns nothing and the [[ -n "$xcode_version" ]] guard skips the check — a broken install still passes the gate and fails downstream. A named "xcodebuild present but not usable" message would close that gap.
  • select_ios_device()'s comment cites detect_apple_team_id's prompt as precedent, but that function doesn't exist in setup.sh on main (it only appears as a stub in app/scripts/generate_ios_custom_config_bundle_id_test.sh). The no-TTY fail-fast itself is right; the reference is just stale.
  • jq is now a hard prerequisite for every iOS build. Reasonable given the named remedy, but it's one new install step for contributors with previously-working builds; and for brew-installed CocoaPods the remedy sudo gem install cocoapods should be brew upgrade cocoapods.
  • Neither suite is wired into CI (mobile-app-checks.yml runs flutter test only). That matches the existing app/scripts/*_test.sh convention, so not blocking — a small ubuntu runner (bash+jq) would make these stick.

What I can't verify from here is the macOS behavior itself. Before this becomes the default setup.sh ios path, one on-Mac pass of the prereq-failure messages and the multi-device prompt would confirm the UX end-to-end.


by AI on behalf of David — needs one on-Mac validation run of the new setup.sh ios flow and a maintainer call on making jq a hard iOS prerequisite; please @Git-on-my-level if David is needed.

@Git-on-my-level Git-on-my-level added positive-signal Good PR — positive signal, not a formal approval ios labels Aug 18, 2026
Addresses review feedback on BasedHardware#11793:

- check_ios_prerequisites() silently passed when xcodebuild is on PATH
  but unusable (license not accepted, components missing) — xcodebuild
  -version then prints nothing matching, the version guard short-circuits,
  and a broken Xcode install sailed through the exact gate meant to
  catch it. Named explicitly now, with a remedy.
- select_ios_device()'s comment cited detect_apple_team_id as
  precedent; that function doesn't exist in setup.sh on main (it's from
  the separate, unmerged BasedHardware#7641). Removed the stale reference.
- The CocoaPods outdated-version remedy always said `sudo gem install
  cocoapods`, wrong for a Homebrew-installed CocoaPods (gem-installing
  over a brew-managed one doesn't actually update what's on PATH). Now
  names both.

New test confirms the broken-Xcode gap was real: fails (rc=0, silent
pass) against the pre-fix check_ios_prerequisites(), passes against the
fix.

Failure-Class: none
@formed2forge

Copy link
Copy Markdown
Contributor Author

Thanks for the thorough review, @Git-on-my-level — addressed all three notes in a follow-up commit (500e9762):

  1. Broken/unlicensed Xcode silently passing — fixed. check_ios_prerequisites() now explicitly names "xcodebuild is on PATH but not usable" when the version line can't be parsed, instead of treating that as "nothing to check." New test confirms this was a real gap: it fails (rc=0, silent pass) against the pre-fix check, passes against the fix.
  2. Stale detect_apple_team_id reference — removed. You're right that it doesn't exist on main; the comment now just states the reasoning directly.
  3. CocoaPods update remedy — now names both brew upgrade cocoapods and sudo gem install cocoapods, since gem-installing over a Homebrew-managed CocoaPods doesn't actually update what's on PATH.

On the two open questions:

  • jq as a hard prerequisite: agreed this is a real, if small, cost — happy to defer to a maintainer call on whether that's acceptable, per your note.
  • On-Mac validation of the actual UX: this was tested live, not just in the shell test suites — three real runs of bash setup.sh ios / flutter run, covering the multi-device-no-TTY fail-fast path (hit unplanned, with a real wirelessly-reconnected phone), the interactive prompt through an actual pty (selected a device by number, watched it build and reach Launching lib/main.dart on <chosen device>), and the original bug's exact repro (stubbed toolchain reporting only macOS, confirmed the new named error fires instead of the old silent fallback). Details are in the PR description.

@kodjima33 kodjima33 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

setup.sh only: pins an iOS build destination instead of silently falling back to macOS, and validates the prerequisites the script already documents. Shell tests + live verification. Fixes #11775.

@kodjima33
kodjima33 merged commit 1baa3a7 into BasedHardware:main Aug 18, 2026
25 checks passed
@cursor
cursor Bot deleted the fix/setup-sh-device-pinning branch September 2, 2026 02:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ios positive-signal Good PR — positive signal, not a formal approval

Projects

None yet

Development

Successfully merging this pull request may close these issues.

setup.sh ios never pins a device and validates none of the prerequisites it prints

3 participants