Windows support: Android + Chromium host control plane#421
Open
latekvo wants to merge 11 commits into
Open
Conversation
Argent's host-side control plane (Android + Chromium) had a handful of POSIX-only assumptions that crashed or silently no-op'd on Windows. iOS stays macOS-only (Apple restriction), so this targets the Android + Chromium paths. - Add a shared commandOnPath() helper using `where` on Windows and `command -v` via /bin/sh on POSIX; route android-binary, check-deps, and vega-cli through it (they previously hardcoded /bin/sh, which never matches on Windows). - android-binary: append `.exe` to adb/emulator in the $ANDROID_HOME fallback and probe %LOCALAPPDATA%\\Android\\Sdk (Studio's Windows default). - simulator-server resolver + dispatcher + bundle-tools + the download script: ship and resolve simulator-server.exe on win32 (new simulatorServerBinaryName()); add win32 to the bundled host keys. - stop-metro: resolve listening PIDs via `netstat -ano` on Windows (`lsof` is POSIX-only and threw ENOENT there). Tests: cross-platform unit coverage for commandOnPath (where/command -v branches), the win32 .exe android resolution, and win32 .exe simulator-server path; update the dep-gate tests to mock at the commandOnPath boundary so they're platform-agnostic.
…er.exe) Two jobs on windows-latest, the substrate that makes 'Argent on Windows' verifiable without local virtualization: - windows-chromium: builds the workspace, runs the cross-platform resolver unit tests natively on Windows, confirms adb.exe resolution, then drives a real headless Chrome over CDP through the tool-server (discover → screenshot → describe → tap → observe the DOM mutation the tap causes). The flow is in scripts/ci/windows-chromium-e2e.mjs (also runnable locally on macOS/Linux) against scripts/ci/e2e-chromium-page.html. - windows-simulator-server: builds the argent simulator-server.exe from radon source (cargo build --features argent,swdec,swenc — the same set the radon release ships), installs it into bin/win32, confirms the resolver finds it, and smoke-runs it to prove the PE executes natively. Android-emulator E2E needs WHPX/HAXM (unavailable on hosted Windows runners), so it's out of scope here; the Android host tooling (adb.exe + the .exe that talks to it) is covered.
…don CI - native-devtools-ios resolver tests: name the simulator-server fixture per-platform (simulator-server.exe on win32) so the two host-platform tests pass on a Windows runner, where the resolver correctly requires the .exe. Unchanged on macOS/Linux. - windows-e2e.yml: drop the windows-simulator-server job. The .exe is built from the private radon repo, which the argent runner can't clone without a cross-repo token; its Windows build + smoke now lives in radon's simulator-server CI (build_argent_windows). Keeps the windows-chromium job — the host control-plane E2E — as the proof here.
windows-latest sets ANDROID_HOME but doesn't put platform-tools on PATH, so a bare `adb version` fails — which is exactly the gap the resolver closes. Run the adb.exe the resolver returns instead.
On Windows runners, Start-Process background processes don't reliably survive into a later step (the tool-server was up in its own step but gone by the next), so launch Chrome + the tool-server and run the E2E within a single step where both stay in scope.
Review follow-ups:
- adb.ts avdRootCandidates/resolveAvdPath used process.env.HOME (unset on
Windows) and a startsWith('/') gate that rejects C:\ / UNC paths, so
resolveAvdPath always returned null on Windows and the default_boot
snapshot pre-check silently fell back to cold boot. Use os.homedir() and
path.isAbsolute(); add a win32-guarded test (POSIX behaviour unchanged,
verified by the existing cases + full suite).
- download-simulator-server.sh: drop the loose '|PE32+ executable' arch-check
fallback that would pass a mislabeled non-x86-64 PE; keep the x86-64 match.
Not changed: artifacts.ts tar (Windows 10 1803+/Server 2019+ ship tar.exe).
…og file On Windows, boot-device spawned the emulator detached with stdio:"ignore"; the guest never reached sys.boot_completed (verified on windows-latest: the identical flag set boots fine when given real stdout/stderr handles, but hangs with NUL handles). Redirect the detached emulator's output to a throwaway temp log on win32 so it has valid write handles. POSIX keeps "ignore" (works there, and detaching outlives a tool-server restart).
…ggers Extract the win32 `netstat -ano` -> listening-PID parsing out of `listeningPids` into an exported pure `parseNetstatListeningPids` so the Windows-only row matching (TCP/LISTENING filter, IPv4+IPv6 `:<port>` suffix match with the colon guard against `:18081`, dedup) can be unit-tested on any host. Runtime behavior is unchanged and the POSIX lsof branch is untouched. Add a unit test feeding realistic `netstat -ano` output (banner + header, IPv4 and IPv6 LISTENING rows, ESTABLISHED rows, a UDP row, and a neighbouring `:18081` row) and asserting the deduped PID set. Add the Windows-relevant sources this PR changed (`adb.ts`, `boot-device.ts`) to the windows-e2e `paths:` trigger so future edits re-run the job, and reconcile the workflow header comment: WHPX is available on windows-latest (Server 2025) and the emulator boots there (matching boot-device.ts's "verified on windows-latest"); the full Android E2E lives in radon CI only because simulator-server.exe can't be built in this repo.
The netstat parser keyed off the literal State column value "LISTENING", but Windows localizes that column (German "ABHÖREN", French "À L'ÉCOUTE"), so on a non-English host the parser matched nothing and stop-metro silently no-opped while Metro kept running. Identify a listener by its wildcard foreign endpoint (0.0.0.0:0 / [::]:0 / *:*) instead — that is locale-independent, and an ESTABLISHED connection (the tool-server's own CDP socket, which must not be killed) always has a real remote endpoint there. Also read the PID from the trailing column so a multi-word localized State can't shift it. Add a localized (German/French) regression test.
…m dep check Merging main surfaced launch-restart-tvos.test.ts, whose execFile mock returns empty stdout on success. That satisfied the old inline `command -v` probe (which only checked for no-throw), but this branch routes dependency probes through `commandOnPath`, which treats empty stdout as "not on PATH" — so the xcrun preflight threw DependencyMissingError on the CI runner. Echo a path from the `command -v` / `where` probe (as the real tools do) so the dep resolves.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Makes Argent run on Windows. iOS Simulator is macOS-only (Apple), so on Windows that means the Android + Chromium host control plane. The blocker was twofold: (1) no Windows
simulator-serverbinary, and (2) POSIX-only assumptions in the tool-server.Companion radon PR builds the binary and runs the Android emulator E2E on Windows: software-mansion/radon#134.
Changes
Windows-aware tool-server / resolver / bundling
commandOnPath()helper —whereon Windows,command -vvia/bin/shon POSIX.android-binary,check-deps, andvega-clipreviously hardcoded/bin/sh, which never matches on Windows.android-binary: append.exetoadb/emulatorin the$ANDROID_HOMEfallback; probe%LOCALAPPDATA%\Android\Sdk(Android Studio's Windows default).bundle-tools+download-simulator-server.sh: ship and resolvesimulator-server.exeonwin32(newsimulatorServerBinaryName()), addwin32to the bundled host keys, add thesimulator-server-argent-windows.exedownload target with PE arch validation.stop-metro: resolve listening PIDs vianetstat -anoon Windows (lsofis POSIX-only and threw ENOENT).adb.ts: AVD root resolution used$HOME+startsWith("/")(rejectsC:\…); nowos.homedir()+path.isAbsolute().boot-device: the emulator was spawneddetachedwithstdio:"ignore"; on Windows a detached emulator with NUL stdout/stderr never reachessys.boot_completed(verified onwindows-latest: the identical flag set boots fine with real handles, hangs with NUL). Redirect the detached emulator's output to a temp log file on win32. POSIX keeps"ignore".Tests — cross-platform unit coverage for
commandOnPath(both branches), win32.exeAndroid resolution, win32.exesimulator-server resolution, and a win32 AVD drive-path case; dep-gate tests now mock at thecommandOnPathboundary so they're platform-agnostic.CI —
windows-e2e.yml(windows-chromiumonwindows-latest): runs the resolver/dep unit tests natively on Windows, confirmsadb.exeresolution via the SDK root, then drives a real headless Chrome over CDP through the tool-server (discover → screenshot → describe → tap → observe the DOM mutation). Flow inscripts/ci/windows-chromium-e2e.mjs(also runnable locally / in Docker). Thesimulator-server.exebuild + the full Android-emulator E2E on Windows live in radon CI (the binary's source is the private radon repo).Verification (all green)
windows-latest):✓ discovered chromium-cdp-9222 → ✓ screenshot → ✓ describe → ✓ gesture-tap → ✓ DOM mutation observed. Resolver/dep unit tests run natively;adb.exeresolves via the SDK root (it's not on PATH on the runner — the gap the resolver closes).windows-latest/ WHPX): the real Argent path —boot-device→{ booted: true }→ screenshot viasimulator-server.exe→ gesture-tap — all green. (The boot-device fix above is what makes the detached emulator reachboot_completed.)npm ci, no host pollution).adb.ts/boot-device.ts/vega-cli.tspaths).Notes
Android-emulator E2E needs WHPX nested virtualization — confirmed available on the current hosted
windows-latestimage (Server 2025); the emulator boots hardware-accelerated in ~3 min.