From a156874532fd9be3489618112327b8441baec5ba Mon Sep 17 00:00:00 2001 From: Ngo Quoc Dat Date: Mon, 19 Oct 2026 03:35:27 +0700 Subject: [PATCH] ci(ios): boot the simulator while the app builds --- .claude/rules/ci.md | 5 ++-- .github/workflows/ios-tests.yml | 41 +++++++++++++++++++++++++-------- 2 files changed, 34 insertions(+), 12 deletions(-) diff --git a/.claude/rules/ci.md b/.claude/rules/ci.md index 9b2f3c0190..dedff7e14f 100644 --- a/.claude/rules/ci.md +++ b/.claude/rules/ci.md @@ -52,8 +52,9 @@ The required check names are in `AGENTS.md` (CI). Reading a red check: `.claude/ ## iOS and Package Tests (`ios-tests.yml`) - Triggers and concurrency as macOS Tests. `Changes (iOS)` outputs `run` (detector `ios`) and `oracle` (detector `oracle`). -- 50 minutes on `xcode-27`, simulator `iPhone 18 Pro`. The iOS platform is downloaded only when that simulator is missing. -- `TableProMobileTests` runs with `-parallel-testing-enabled NO`: a second booted simulator killed runs with "(ipc/mig) server died" on the 3-core runner. `TableProMobile/project.yml` says `parallelizable: true`, which every run overrides. +- 50 minutes on `xcode-27`, simulator `iPhone 18 Pro` on the newest iOS runtime that has it. The iOS platform is downloaded only when that simulator is missing. +- Right after setup-xcode the job boots that simulator by UDID, so its first boot overlaps the build. `xcodebuild test` names it with `-destination id=`. +- `TableProMobileTests` runs with `-parallel-testing-enabled NO` on that one simulator: a second booted simulator killed runs with "(ipc/mig) server died" on the 3-core runner. `TableProMobile/project.yml` says `parallelizable: true`, which every run overrides. - `swift test --package-path Packages/TableProCore --parallel`, then TableProOracle with `--force-resolved-versions` when its detector is not false. Each step has `if: !cancelled()`, so one red step never hides the next. - The summary step always runs. Evidence uploads only on failure. diff --git a/.github/workflows/ios-tests.yml b/.github/workflows/ios-tests.yml index 6ccbf1f1ef..15bc6f9b99 100644 --- a/.github/workflows/ios-tests.yml +++ b/.github/workflows/ios-tests.yml @@ -46,12 +46,38 @@ jobs: runs-on: xcode-27 timeout-minutes: 50 env: - DESTINATION: platform=iOS Simulator,name=iPhone 18 Pro + SIMULATOR: iPhone 18 Pro steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - uses: ./.github/actions/setup-xcode + # Booted ahead of the build so the first boot overlaps the compile instead of following it. + # The image ships the iOS 27.0 runtime; downloading the one for Xcode's newer SDK costs minutes. + # A destination by name takes the newest runtime, so the lookup does too. + - name: Boot the simulator + id: simulator + run: | + set -euo pipefail + find_simulator() { + xcrun simctl list devices available -j | jq -r --arg name "$SIMULATOR" ' + [.devices | to_entries[] | select(.key | test("\\.iOS-[0-9-]+$")) + | {version: (.key | capture("iOS-(?[0-9-]+)$").v | split("-") | map(tonumber)), + udid: (.value[] | select(.name == $name) | .udid)}] + | sort_by(.version) | last | .udid // empty' + } + udid="$(find_simulator)" + if [ -z "$udid" ]; then + sudo xcodebuild -downloadPlatform iOS + udid="$(find_simulator)" + fi + if [ -z "$udid" ]; then + echo "No $SIMULATOR simulator, even after downloading the iOS platform." >&2 + exit 1 + fi + xcrun simctl boot "$udid" + echo "udid=$udid" >> "$GITHUB_OUTPUT" + - uses: ./.github/actions/setup-xcodegen - name: Download static libraries @@ -62,24 +88,19 @@ jobs: - name: Generate the Xcode project run: scripts/generate-project.sh ios - # The image ships the iOS 27.0 runtime. Downloading the one for Xcode's newer SDK costs minutes. - - name: Make sure the simulator exists - run: | - if ! xcrun simctl list devices available | grep -F "iPhone 18 Pro (" > /dev/null; then - sudo xcodebuild -downloadPlatform iOS - fi - # One simulator, not clones: on a 3-core runner a second booted simulator killed runs with # "(ipc/mig) server died", and TableProMobileTests is one bundle that never split anyway. - name: Run the iOS tests + env: + SIMULATOR_UDID: ${{ steps.simulator.outputs.udid }} run: | set -o pipefail xcodebuild test \ -project TableProMobile/TableProMobile.xcodeproj -scheme TableProMobile \ - -destination "$DESTINATION" -only-testing:TableProMobileTests \ + -destination "platform=iOS Simulator,id=$SIMULATOR_UDID" -only-testing:TableProMobileTests \ -parallel-testing-enabled NO \ -skipPackagePluginValidation -resultBundlePath iOS.xcresult \ - CODE_SIGNING_ALLOWED=NO COMPILER_INDEX_STORE_ENABLE=NO \ + CODE_SIGNING_ALLOWED=NO COMPILER_INDEX_STORE_ENABLE=NO COMPILATION_CACHE_ENABLE_CACHING=NO \ | xcbeautify --renderer github-actions # --parallel is what makes swift test write the XCTest xunit file. Without it only the Swift