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
+11
-1
@@ -46,7 +46,17 @@ Before the first JavaScript action, an inline shell bootstrap installs verified
|
|||||||
native Node 22.22.1 into a versioned runner tool cache and exports its bin directory
|
native Node 22.22.1 into a versioned runner tool cache and exports its bin directory
|
||||||
through `GITHUB_PATH`. It needs no Homebrew/root access and remains available to
|
through `GITHUB_PATH`. It needs no Homebrew/root access and remains available to
|
||||||
action post-cleanup. This addresses run #4's `Cannot find: node in PATH` failure;
|
action post-cleanup. This addresses run #4's `Cannot find: node in PATH` failure;
|
||||||
the modified workflow itself still needs a real Mac run.
|
native bootstrap and checkout were verified in Mac job 1062 (run 661/#6).
|
||||||
|
That job subsequently stalled in Apple-tool preflight after printing Xcode 27.1.
|
||||||
|
The preflight now prints each tool probe and bounds it to 60 seconds using Python
|
||||||
|
(`scripts/check-apple-tools.py`), so the next run identifies the unresponsive
|
||||||
|
command rather than hanging silently. This does not validate the iOS app.
|
||||||
|
|
||||||
|
If an Apple probe stalls, run the named command in the runner user's interactive
|
||||||
|
login session, check the selected full Xcode, finish its first-launch components
|
||||||
|
and confirm CoreSimulator/runtimes work for that user. CI does not accept licenses,
|
||||||
|
change `xcode-select`, install runtimes or kill host simulator services. The runner
|
||||||
|
administrator must repair those prerequisites before native Apple validation.
|
||||||
|
|
||||||
XcodeGen's pinned, checksum-verified binary includes native arm64 and x86_64
|
XcodeGen's pinned, checksum-verified binary includes native arm64 and x86_64
|
||||||
slices; Rosetta/Homebrew installation is not required by the workflow. Build,
|
slices; Rosetta/Homebrew installation is not required by the workflow. Build,
|
||||||
|
|||||||
@@ -42,7 +42,14 @@ collection before uninstall; the PNG was opened and confirmed nonblank. Five new
|
|||||||
output-regression tests guard missing, invalid, ambiguous and stale screenshots.
|
output-regression tests guard missing, invalid, ambiguous and stale screenshots.
|
||||||
See `validation.md` for logs and limitations. No Apple SDK result is implied.
|
See `validation.md` for logs and limitations. No Apple SDK result is implied.
|
||||||
|
|
||||||
The next Mac/Ubuntu workflow execution still needs inspection after pushing.
|
Android build job 1059 in global run 660 (#5) succeeded; the actual native debug
|
||||||
|
artifact was downloaded, checked and installed/launched on `emulator-5554`.
|
||||||
|
Jobs 1060/1061 still fail KVM preflight. Mac job 1062 in run 661 (#6) passed native
|
||||||
|
Node bootstrap and checkout, printed Xcode 27.1, then stalled in Apple-tool
|
||||||
|
preflight for at least 20 minutes. The new `check-apple-tools.py` prints each
|
||||||
|
command and imposes a 60-second deadline; fourteen runner/probe regressions pass.
|
||||||
|
Inspect its next Mac run for the exact stalled command and fix the runner user
|
||||||
|
session/Xcode/CoreSimulator prerequisites without weakening the check.
|
||||||
Rebuild/collect from a clean checkout; do not present cached wrapper artifacts as
|
Rebuild/collect from a clean checkout; do not present cached wrapper artifacts as
|
||||||
the native preview. Keep the signing stash parked.
|
the native preview. Keep the signing stash parked.
|
||||||
|
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ Human security review and additional device/role/error-path coverage remain open
|
|||||||
|
|
||||||
10 JVM tests, 6 native API-35 UI tests, the 7-test real-server suite (including
|
10 JVM tests, 6 native API-35 UI tests, the 7-test real-server suite (including
|
||||||
those same 6), both Android lint variants and package builds pass. Swift has 16
|
those same 6), both Android lint variants and package builds pass. Swift has 16
|
||||||
portable passes plus strict formatting/parsing. 23 script regressions, repository
|
portable passes plus strict formatting/parsing. 27 script regressions, repository
|
||||||
checks, workflow lint and the 266-coordinate OSV scan pass. See
|
checks, workflow lint and the 266-coordinate OSV scan pass. See
|
||||||
[validation](validation.md) for evidence, failures corrected and exact limitations.
|
[validation](validation.md) for evidence, failures corrected and exact limitations.
|
||||||
|
|
||||||
@@ -62,10 +62,13 @@ state. Only the development-signed debug APK is installable as provided.
|
|||||||
|
|
||||||
## 10. Gitea CI
|
## 10. Gitea CI
|
||||||
|
|
||||||
The Android workflow offers `android-native-debug-apk`; the Mac workflow bootstraps
|
Android build job 1059 succeeded and uploaded `android-native-debug-apk`. The actual
|
||||||
verified ARM64 Node before checkout. Actual artifact publication and Apple build
|
artifact was downloaded, signature/version checked, installed and launched on the
|
||||||
results require remote-run inspection. Remote Ubuntu emulator jobs need KVM access;
|
authorized local emulator. [Download the verified artifact](https://git.nesterovic.cc/nessi/Yuvomi-App/actions/runs/660/artifacts/android-native-debug-apk).
|
||||||
local emulator success is not a substitute for a passing remote matrix.
|
Mac Node bootstrap and checkout now succeed, but its Apple-tool preflight stalled;
|
||||||
|
named/bounded probes have been added for diagnosis. The remote emulator matrix
|
||||||
|
still fails missing-KVM preflight, not native tests. No green whole-pipeline or
|
||||||
|
Apple application validation is claimed. See [validation](validation.md).
|
||||||
|
|
||||||
## 11. Signing and publication
|
## 11. Signing and publication
|
||||||
|
|
||||||
|
|||||||
+25
-4
@@ -21,7 +21,7 @@ not included in the native suites or counted toward native completion.
|
|||||||
| Android packages | Debug APK and unsigned release APK/AAB built successfully |
|
| Android packages | Debug APK and unsigned release APK/AAB built successfully |
|
||||||
| Swift portable suite | 16 passed; actual Foundation transport/contracts and URL/metadata policy, not Apple UI/Keychain execution |
|
| Swift portable suite | 16 passed; actual Foundation transport/contracts and URL/metadata policy, not Apple UI/Keychain execution |
|
||||||
| Swift formatting/parsing | Strict format lint and parsing passed, including Apple-only sources |
|
| Swift formatting/parsing | Strict format lint and parsing passed, including Apple-only sources |
|
||||||
| Script regressions | 10 runner-preflight, 8 JUnit-evidence and 5 screenshot-output tests passed |
|
| Script regressions | 14 runner-preflight/Apple-probe, 8 JUnit-evidence and 5 screenshot-output tests passed |
|
||||||
| Repository/workflows | Source invariants, Python compilation, diff whitespace and actionlint for all 3 Gitea workflows passed |
|
| Repository/workflows | Source invariants, Python compilation, diff whitespace and actionlint for all 3 Gitea workflows passed |
|
||||||
| OSV | No listed advisories for 266 locked/direct Maven coordinates and plugins; not a security audit |
|
| OSV | No listed advisories for 266 locked/direct Maven coordinates and plugins; not a security audit |
|
||||||
|
|
||||||
@@ -51,10 +51,31 @@ Evidence paths in the local external build directory:
|
|||||||
`/home/neste/yuvomi-native-integration-final.log`,
|
`/home/neste/yuvomi-native-integration-final.log`,
|
||||||
`/home/neste/yuvomi-native-swift.log`.
|
`/home/neste/yuvomi-native-swift.log`.
|
||||||
|
|
||||||
|
## Observed Gitea results for source `40a7962`
|
||||||
|
|
||||||
|
- Android run #5 (global run 660), build job 1059: **success**, including packages,
|
||||||
|
JVM/lint, formatting/repository/script checks, OSV and both artifact uploads.
|
||||||
|
- The actual `android-native-debug-apk` ZIP was downloaded. It contains exactly
|
||||||
|
`app-debug.apk`, version `0.2.0-native-preview`/code 2; APK v2 signature verified,
|
||||||
|
no fixture certificate/private-key files, installed and launched on the local
|
||||||
|
authorized API-35 emulator. SHA-256 of the downloaded APK:
|
||||||
|
`48d58ba5663b2e4fe563064efc567732e8ea35588cb610660f60406e82ccf610`.
|
||||||
|
Its ephemeral CI debug certificate differs from the local development key, so
|
||||||
|
its checksum intentionally differs from the locally collected APK.
|
||||||
|
- [Download the verified native debug artifact](https://git.nesterovic.cc/nessi/Yuvomi-App/actions/runs/660/artifacts/android-native-debug-apk)
|
||||||
|
(repository access required; normal 14-day artifact retention applies).
|
||||||
|
- Remote jobs 1060/1061 (API 35/37.0): **failed preflight** because `/dev/kvm` was
|
||||||
|
not exposed/writable. They executed no emulator tests. The matrix is not green.
|
||||||
|
- iOS run #6 (global 661), job 1062: verified native Node bootstrap and checkout
|
||||||
|
**succeeded**. Xcode 27.1/build 27A9275 printed, but the subsequent Apple-tool
|
||||||
|
preflight did not return during a 20-minute observation. No SwiftUI compile or
|
||||||
|
simulator/XCTest/Keychain/device success is claimed. Bounded, named tool probes
|
||||||
|
now fail with actionable diagnostics instead of silently hanging for the job's
|
||||||
|
full hour; their actual next Mac result still needs inspection.
|
||||||
|
|
||||||
No Apple SDK application build, simulator UI test or physical-device result is
|
No Apple SDK application build, simulator UI test or physical-device result is
|
||||||
claimed from these Linux runs. The modified Mac bootstrap and remote APK upload
|
claimed from the Linux runs or the stalled Mac job. Runner prerequisites were
|
||||||
still require inspection of the next actual Gitea run. Prior remote emulator jobs
|
not weakened or counted as passing, and no host license/settings were changed.
|
||||||
failed KVM preflight; the prerequisite was not weakened or counted as passing.
|
|
||||||
|
|
||||||
## Commands
|
## Commands
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
ios)
|
||||||
[[ "$(uname -s)" == Darwin && "$(uname -m)" == arm64 ]] || fail 'macos-arm64 must execute natively on the Apple Silicon host'
|
[[ "$(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'
|
command -v xcodebuild >/dev/null || fail 'install full Xcode and select it with xcode-select'
|
||||||
xcodebuild -version
|
python3 "${BASH_SOURCE[0]%/*}/check-apple-tools.py"
|
||||||
xcrun --find swift >/dev/null
|
|
||||||
xcrun simctl list devices available >/dev/null
|
|
||||||
;;
|
;;
|
||||||
*) fail 'usage: bash scripts/check-ci-runner.sh android|emulator|ios' ;;
|
*) fail 'usage: bash scripts/check-ci-runner.sh android|emulator|ios' ;;
|
||||||
esac
|
esac
|
||||||
@@ -1,24 +1,32 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
"""Test runner prerequisite decisions with fake tools, not actual Apple SDKs."""
|
"""Test runner prerequisite decisions with fake tools, not actual Apple SDKs."""
|
||||||
import os
|
import os
|
||||||
|
import importlib.util
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
import shlex
|
||||||
import subprocess
|
import subprocess
|
||||||
|
import sys
|
||||||
import tempfile
|
import tempfile
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
SCRIPT = Path(__file__).with_name("check-ci-runner.sh")
|
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):
|
class RunnerPrerequisiteTests(unittest.TestCase):
|
||||||
def run_check(self, mode, system="Linux", architecture="x86_64", missing=None, broken_xcode=False):
|
def run_check(self, mode, system="Linux", architecture="x86_64", missing=None, broken_xcode=False, broken_xcrun=False):
|
||||||
with tempfile.TemporaryDirectory(prefix="yuvomi-runner-test-") as temporary:
|
base = "/tmp/opencode" if Path("/tmp/opencode").exists() else None
|
||||||
|
with tempfile.TemporaryDirectory(prefix="yuvomi-runner-test-", dir=base) as temporary:
|
||||||
directory = Path(temporary)
|
directory = Path(temporary)
|
||||||
for name in ["git", "curl", "unzip", "python3", "xcodebuild", "xcrun"]:
|
for name in ["git", "curl", "unzip", "python3", "xcodebuild", "xcrun"]:
|
||||||
if name == missing:
|
if name == missing:
|
||||||
continue
|
continue
|
||||||
path = directory / name
|
path = directory / name
|
||||||
status = 1 if name == "xcodebuild" and broken_xcode else 0
|
status = 1 if (name == "xcodebuild" and broken_xcode or name == "xcrun" and broken_xcrun) else 0
|
||||||
path.write_text(f"#!/bin/sh\nexit {status}\n")
|
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)
|
path.chmod(0o700)
|
||||||
uname = directory / "uname"
|
uname = directory / "uname"
|
||||||
uname.write_text('#!/bin/sh\ncase "$1" in -s) echo "$TEST_SYSTEM";; -m) echo "$TEST_ARCH";; esac\n')
|
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):
|
def test_unknown_mode_fails(self):
|
||||||
self.assertNotEqual(self.run_check("other").returncode, 0)
|
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__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
Reference in new issue
Block a user