SHA256
fix(ci): bound stalled Apple probes and record verified native APK
Mac Node bootstrap and checkout now pass, but Apple-tool preflight stalled after Xcode version output for twenty minutes. Add per-command deadlines and diagnostics without changing licenses/host settings; fourteen preflight/probe regressions pass. Record successful Android build/upload, downloaded APK signature/version and emulator install/launch, and unchanged missing-KVM matrix failures. No Apple app success is claimed.
This commit is contained in:
1 parent
40a7962307
commit
92dbd0e06d
7 files changed
+120
-18
No files matched your search
@@ -0,0 +1,37 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Bounded Apple toolchain probes; never install components or change the host."""
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
|
||||
def probe(command, timeout=60):
|
||||
label = " ".join(command)
|
||||
print(f"Runner prerequisite: checking {label} (timeout {timeout}s)", flush=True)
|
||||
try:
|
||||
result = subprocess.run(command, capture_output=True, text=True, timeout=timeout, check=False)
|
||||
except subprocess.TimeoutExpired as error:
|
||||
raise RuntimeError(
|
||||
f"{label} did not return within {timeout}s. Check selected Xcode, first-launch components "
|
||||
"and CoreSimulator in the runner user's interactive login session; run this command there. "
|
||||
"CI has not accepted licenses or changed host settings."
|
||||
) from error
|
||||
except OSError as error:
|
||||
raise RuntimeError(f"Cannot start {label}: {error}") from error
|
||||
if result.returncode:
|
||||
raise RuntimeError(f"{label} exited {result.returncode}: {(result.stderr or result.stdout).strip()}")
|
||||
return result.stdout
|
||||
|
||||
|
||||
def main():
|
||||
try:
|
||||
print(probe(["xcodebuild", "-version"]).strip(), flush=True)
|
||||
probe(["xcrun", "--find", "swift"])
|
||||
probe(["xcrun", "simctl", "list", "devices", "available"])
|
||||
except RuntimeError as error:
|
||||
print(f"Runner prerequisite: {error}", file=sys.stderr)
|
||||
return 1
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
@@ -17,9 +17,7 @@ case "${1:-}" in
|
||||
ios)
|
||||
[[ "$(uname -s)" == Darwin && "$(uname -m)" == arm64 ]] || fail 'macos-arm64 must execute natively on the Apple Silicon host'
|
||||
command -v xcodebuild >/dev/null || fail 'install full Xcode and select it with xcode-select'
|
||||
xcodebuild -version
|
||||
xcrun --find swift >/dev/null
|
||||
xcrun simctl list devices available >/dev/null
|
||||
python3 "${BASH_SOURCE[0]%/*}/check-apple-tools.py"
|
||||
;;
|
||||
*) fail 'usage: bash scripts/check-ci-runner.sh android|emulator|ios' ;;
|
||||
esac
|
||||
@@ -1,24 +1,32 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Test runner prerequisite decisions with fake tools, not actual Apple SDKs."""
|
||||
import os
|
||||
import importlib.util
|
||||
from pathlib import Path
|
||||
import shlex
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
import unittest
|
||||
|
||||
SCRIPT = Path(__file__).with_name("check-ci-runner.sh")
|
||||
SPEC = importlib.util.spec_from_file_location("apple_tools", SCRIPT.with_name("check-apple-tools.py"))
|
||||
APPLE_TOOLS = importlib.util.module_from_spec(SPEC)
|
||||
SPEC.loader.exec_module(APPLE_TOOLS)
|
||||
|
||||
|
||||
class RunnerPrerequisiteTests(unittest.TestCase):
|
||||
def run_check(self, mode, system="Linux", architecture="x86_64", missing=None, broken_xcode=False):
|
||||
with tempfile.TemporaryDirectory(prefix="yuvomi-runner-test-") as temporary:
|
||||
def run_check(self, mode, system="Linux", architecture="x86_64", missing=None, broken_xcode=False, broken_xcrun=False):
|
||||
base = "/tmp/opencode" if Path("/tmp/opencode").exists() else None
|
||||
with tempfile.TemporaryDirectory(prefix="yuvomi-runner-test-", dir=base) as temporary:
|
||||
directory = Path(temporary)
|
||||
for name in ["git", "curl", "unzip", "python3", "xcodebuild", "xcrun"]:
|
||||
if name == missing:
|
||||
continue
|
||||
path = directory / name
|
||||
status = 1 if name == "xcodebuild" and broken_xcode else 0
|
||||
path.write_text(f"#!/bin/sh\nexit {status}\n")
|
||||
status = 1 if (name == "xcodebuild" and broken_xcode or name == "xcrun" and broken_xcrun) else 0
|
||||
path.write_text(f"#!/bin/sh\nexec {shlex.quote(sys.executable)} \"$@\"\n" if name == "python3"
|
||||
else f"#!/bin/sh\nexit {status}\n")
|
||||
path.chmod(0o700)
|
||||
uname = directory / "uname"
|
||||
uname.write_text('#!/bin/sh\ncase "$1" in -s) echo "$TEST_SYSTEM";; -m) echo "$TEST_ARCH";; esac\n')
|
||||
@@ -62,6 +70,24 @@ class RunnerPrerequisiteTests(unittest.TestCase):
|
||||
def test_unknown_mode_fails(self):
|
||||
self.assertNotEqual(self.run_check("other").returncode, 0)
|
||||
|
||||
def test_missing_xcrun_is_actionable(self):
|
||||
result = self.run_check("ios", system="Darwin", architecture="arm64", missing="xcrun")
|
||||
self.assertNotEqual(result.returncode, 0)
|
||||
self.assertIn("Cannot start xcrun", result.stderr)
|
||||
|
||||
def test_broken_xcrun_is_not_a_pass(self):
|
||||
result = self.run_check("ios", system="Darwin", architecture="arm64", broken_xcrun=True)
|
||||
self.assertNotEqual(result.returncode, 0)
|
||||
self.assertIn("xcrun --find swift exited 1", result.stderr)
|
||||
|
||||
def test_unresponsive_apple_probe_times_out_with_guidance(self):
|
||||
with self.assertRaisesRegex(RuntimeError, "interactive login session"):
|
||||
APPLE_TOOLS.probe([sys.executable, "-c", "import time; time.sleep(30)"], timeout=0.05)
|
||||
|
||||
def test_apple_probe_preserves_tool_failure_diagnostic(self):
|
||||
with self.assertRaisesRegex(RuntimeError, "fixture tool failure"):
|
||||
APPLE_TOOLS.probe([sys.executable, "-c", "import sys; sys.exit('fixture tool failure')"])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in new issue
Block a user