diff --git a/.github/workflows/e2e-tests.yml b/.github/workflows/e2e-tests.yml index 7b0076bf..4c29d7f4 100644 --- a/.github/workflows/e2e-tests.yml +++ b/.github/workflows/e2e-tests.yml @@ -146,10 +146,10 @@ jobs: node-version: '24.10.0' cache: 'pnpm' - - name: Setup Xcode 26.4.1 + - name: Setup Xcode 26.6 uses: maxim-lobanov/setup-xcode@ed7a3b1fda3918c0306d1b724322adc0b8cc0a90 # v1.7.0 with: - xcode-version: '26.4.1' + xcode-version: '26.6' - name: Install Watchman run: brew install watchman diff --git a/.github/workflows/ios-simulator-startup-timings.yml b/.github/workflows/ios-simulator-startup-timings.yml new file mode 100644 index 00000000..a41f767a --- /dev/null +++ b/.github/workflows/ios-simulator-startup-timings.yml @@ -0,0 +1,170 @@ +name: Simulator boot benchmark + +on: + workflow_dispatch: + +permissions: + contents: read + +jobs: + simulator-boot: + strategy: + fail-fast: false + matrix: + runtime: + - iOS-26-2 + - iOS-26-4 + - iOS-26-5 + + runs-on: macos-26 + timeout-minutes: 20 + + steps: + - name: Select Xcode 26.6 + shell: bash + run: | + set -euo pipefail + + sudo xcode-select \ + --switch /Applications/Xcode_26.6.app/Contents/Developer + + xcodebuild -version + + echo "SIMULATOR_CACHE_KEY=core-simulator-device-ios-26-5-xcode-26-6-${ImageOS}-${ImageVersion}" >> "$GITHUB_ENV" + + - name: Restore iOS 26.5 simulator cache + id: cache-ios-26-5-restore + if: matrix.runtime == 'iOS-26-5' + uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 + with: + path: ~/Library/Developer/CoreSimulator/Devices/FD9EC0F9-63AA-4C2F-B0F5-224AAE68866E + key: ${{ env.SIMULATOR_CACHE_KEY }} + + - name: Inspect available simulators + shell: bash + run: | + set -euo pipefail + + xcrun simctl list runtimes + xcrun simctl list devices available + + - name: Measure existing simulator boot + shell: bash + env: + RUNTIME_SUFFIX: ${{ matrix.runtime }} + DEVICE_NAME: iPhone 17 Pro + run: | + set -euo pipefail + + RUNTIME_ID="com.apple.CoreSimulator.SimRuntime.${RUNTIME_SUFFIX}" + BOOT_LOG="$RUNNER_TEMP/bootstatus-${RUNTIME_SUFFIX}.log" + + echo "Looking for:" + echo " Runtime: $RUNTIME_ID" + echo " Device: $DEVICE_NAME" + + # Verify that the requested runtime is installed. + if ! xcrun simctl list runtimes -j | + jq -e --arg runtime "$RUNTIME_ID" ' + .runtimes[] + | select( + .identifier == $runtime + and .isAvailable == true + ) + ' >/dev/null; then + echo "::error::Runtime $RUNTIME_ID is not available" + exit 1 + fi + + # Select an existing available device only. + UDID="$( + xcrun simctl list devices available -j | + jq -er \ + --arg runtime "$RUNTIME_ID" \ + --arg name "$DEVICE_NAME" ' + .devices[$runtime] + | map( + select( + .name == $name + and .isAvailable == true + ) + ) + | .[0].udid + ' + )" || { + echo "::error::No existing $DEVICE_NAME found for $RUNTIME_ID" + echo "Available devices:" + xcrun simctl list devices available + exit 1 + } + + echo "Selected existing simulator:" + echo " $DEVICE_NAME" + echo " $RUNTIME_ID" + echo " $UDID" + + # Do not erase, recreate, or delete the selected device. + xcrun simctl shutdown "$UDID" 2>/dev/null || true + + START_SECONDS="$(date +%s)" + + echo "=== Boot request ===" + time xcrun simctl boot "$UDID" + + echo "=== Boot completion ===" + set +e + xcrun simctl bootstatus "$UDID" -b 2>&1 | + tee "$BOOT_LOG" + BOOT_STATUS="${PIPESTATUS[0]}" + set -e + + END_SECONDS="$(date +%s)" + ELAPSED_SECONDS="$((END_SECONDS - START_SECONDS))" + + MIGRATION_SAMPLES="$( + grep -c "Waiting on Data Migration" "$BOOT_LOG" || true + )" + + { + echo "### Existing simulator boot result" + echo + echo "- Runtime: \`$RUNTIME_ID\`" + echo "- Device: \`$DEVICE_NAME\`" + echo "- UDID: \`$UDID\`" + echo "- Duration: **${ELAPSED_SECONDS}s**" + echo "- Migration samples: **${MIGRATION_SAMPLES}**" + echo "- Runner image: \`${ImageOS:-unknown}\`" + echo "- Image version: \`${ImageVersion:-unknown}\`" + } >> "$GITHUB_STEP_SUMMARY" + + if [[ "$BOOT_STATUS" -ne 0 ]]; then + echo "::error::Simulator failed to finish booting" + exit "$BOOT_STATUS" + fi + + - name: Shut down iOS 26.5 simulator before caching + id: shutdown-ios-26-5 + if: always() && matrix.runtime == 'iOS-26-5' + shell: bash + run: | + set -euo pipefail + + UDID="FD9EC0F9-63AA-4C2F-B0F5-224AAE68866E" + xcrun simctl shutdown "$UDID" || true + + for _ in {1..30}; do + if xcrun simctl list devices | grep -F "$UDID" | grep -q '(Shutdown)'; then + exit 0 + fi + sleep 1 + done + + echo "::error::Simulator $UDID did not shut down before caching" + exit 1 + + - name: Save iOS 26.5 simulator cache + if: always() && matrix.runtime == 'iOS-26-5' && steps.shutdown-ios-26-5.outcome == 'success' && steps.cache-ios-26-5-restore.outputs.cache-hit != 'true' + uses: actions/cache/save@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 + with: + path: ~/Library/Developer/CoreSimulator/Devices/FD9EC0F9-63AA-4C2F-B0F5-224AAE68866E + key: ${{ steps.cache-ios-26-5-restore.outputs.cache-primary-key }}