diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 20acc6316..81a841ec1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -132,84 +132,89 @@ jobs: cd apps/ios xcodegen generate - - name: iOS tests - run: | - set -euo pipefail - RESULT_BUNDLE_PATH="$RUNNER_TEMP/Clawdis-iOS.xcresult" - DEST_ID="$( - python3 - <<'PY' - import json - import subprocess - import sys - import uuid - - def sh(args: list[str]) -> str: - return subprocess.check_output(args, text=True).strip() + - name: iOS tests + run: | + set -euo pipefail + RESULT_BUNDLE_PATH="$RUNNER_TEMP/Clawdis-iOS.xcresult" + DEST_ID="$( + python3 - <<'PY' + import json + import subprocess + import sys + import uuid - # Prefer an already-created iPhone simulator if it exists. - devices = json.loads(sh(["xcrun", "simctl", "list", "devices", "-j"])) - candidates: list[tuple[str, str]] = [] - for runtime, devs in (devices.get("devices") or {}).items(): - for dev in devs or []: - if not dev.get("isAvailable"): - continue - name = str(dev.get("name") or "") - udid = str(dev.get("udid") or "") - if not udid or not name.startswith("iPhone"): - continue - candidates.append((name, udid)) + def sh(args: list[str]) -> str: + return subprocess.check_output(args, text=True).strip() - candidates.sort(key=lambda it: (0 if "iPhone 16" in it[0] else 1, it[0])) - if candidates: - print(candidates[0][1]) - sys.exit(0) + # Prefer an already-created iPhone simulator if it exists. + devices = json.loads(sh(["xcrun", "simctl", "list", "devices", "-j"])) + candidates: list[tuple[str, str]] = [] + for runtime, devs in (devices.get("devices") or {}).items(): + for dev in devs or []: + if not dev.get("isAvailable"): + continue + name = str(dev.get("name") or "") + udid = str(dev.get("udid") or "") + if not udid or not name.startswith("iPhone"): + continue + candidates.append((name, udid)) - # Otherwise, create one from the newest available iOS runtime. - runtimes = json.loads(sh(["xcrun", "simctl", "list", "runtimes", "-j"])).get("runtimes") or [] - ios = [rt for rt in runtimes if rt.get("platform") == "iOS" and rt.get("isAvailable")] - if not ios: - print("No available iOS runtimes found.", file=sys.stderr) - sys.exit(1) + candidates.sort(key=lambda it: (0 if "iPhone 16" in it[0] else 1, it[0])) + if candidates: + print(candidates[0][1]) + sys.exit(0) - def version_key(rt: dict) -> tuple[int, ...]: - parts = [] - for p in str(rt.get("version") or "0").split("."): - try: - parts.append(int(p)) - except ValueError: - parts.append(0) - return tuple(parts) + # Otherwise, create one from the newest available iOS runtime. + runtimes = json.loads(sh(["xcrun", "simctl", "list", "runtimes", "-j"])).get("runtimes") or [] + ios = [rt for rt in runtimes if rt.get("platform") == "iOS" and rt.get("isAvailable")] + if not ios: + print("No available iOS runtimes found.", file=sys.stderr) + sys.exit(1) - ios.sort(key=version_key, reverse=True) - runtime = ios[0] - runtime_id = str(runtime.get("identifier") or "") - if not runtime_id: - print("Missing iOS runtime identifier.", file=sys.stderr) - sys.exit(1) + def version_key(rt: dict) -> tuple[int, ...]: + parts: list[int] = [] + for p in str(rt.get("version") or "0").split("."): + try: + parts.append(int(p)) + except ValueError: + parts.append(0) + return tuple(parts) - supported = runtime.get("supportedDeviceTypes") or [] - iphones = [dt for dt in supported if dt.get("productFamily") == "iPhone"] - if not iphones: - print("No iPhone device types for iOS runtime.", file=sys.stderr) - sys.exit(1) + ios.sort(key=version_key, reverse=True) + runtime = ios[0] + runtime_id = str(runtime.get("identifier") or "") + if not runtime_id: + print("Missing iOS runtime identifier.", file=sys.stderr) + sys.exit(1) - iphones.sort(key=lambda dt: (0 if "iPhone 16" in str(dt.get("name") or "") else 1, str(dt.get("name") or ""))) - device_type_id = str(iphones[0].get("identifier") or "") - if not device_type_id: - print("Missing iPhone device type identifier.", file=sys.stderr) - sys.exit(1) + supported = runtime.get("supportedDeviceTypes") or [] + iphones = [dt for dt in supported if dt.get("productFamily") == "iPhone"] + if not iphones: + print("No iPhone device types for iOS runtime.", file=sys.stderr) + sys.exit(1) - sim_name = f"CI iPhone {uuid.uuid4().hex[:8]}" - udid = sh(["xcrun", "simctl", "create", sim_name, device_type_id, runtime_id]) - if not udid: - print("Failed to create iPhone simulator.", file=sys.stderr) - sys.exit(1) - print(udid) - PY - )" - echo "Using iOS Simulator id: $DEST_ID" - xcodebuild test \ - -project apps/ios/Clawdis.xcodeproj \ + iphones.sort( + key=lambda dt: ( + 0 if "iPhone 16" in str(dt.get("name") or "") else 1, + str(dt.get("name") or ""), + ) + ) + device_type_id = str(iphones[0].get("identifier") or "") + if not device_type_id: + print("Missing iPhone device type identifier.", file=sys.stderr) + sys.exit(1) + + sim_name = f"CI iPhone {uuid.uuid4().hex[:8]}" + udid = sh(["xcrun", "simctl", "create", sim_name, device_type_id, runtime_id]) + if not udid: + print("Failed to create iPhone simulator.", file=sys.stderr) + sys.exit(1) + print(udid) + PY + )" + echo "Using iOS Simulator id: $DEST_ID" + xcodebuild test \ + -project apps/ios/Clawdis.xcodeproj \ -scheme Clawdis \ -destination "platform=iOS Simulator,id=$DEST_ID" \ -resultBundlePath "$RESULT_BUNDLE_PATH" \