Commit Graph

2 Commits

Author SHA1 Message Date
Filip131311 5a3f896652 fix(screen-recording): pick an ffmpeg that can actually encode, not just one that exists
Resolution trusted the first `command -v ffmpeg` hit unconditionally and only
consulted the fallback list when ffmpeg was ABSENT. A conda-forge build
(`--disable-gpl`, so no libx264) ahead of Homebrew on PATH therefore won every
time, and every recording died with `Unrecognized option 'preset'` — `-preset`
being a libx264-private option — while a working build sat in the fallback list,
unreachable.

Each candidate is now asked whether it can encode, and the first that says yes
wins.

The probe has to read ffmpeg's OUTPUT: `-h encoder=libx264` exits 0 whether or
not the encoder exists, so the obvious implementation — try/catch around
execFile — would look like a fix and change nothing. It matches the success
header rather than the failure sentence, because the header comes from ffmpeg's
help formatter and has been stable for a decade while the failure text is prose
any release may reword; keying on the failure string would silently re-break
recording, and in the direction that breaks setups which currently work.
`-hide_banner` is load-bearing rather than cosmetic: without it the build banner
goes to stderr carrying the literal `--enable-libx264`.

Everything inconclusive — a timeout, a kill, no output at all — is treated as
usable, so the probe can only ever demote a build that positively said it lacks
libx264. On any host where recording works today, ffmpeg still gets to speak for
itself.

Two things the failure path was getting wrong:

- "ffmpeg was not found on PATH. Install it" was shown to someone with three
  ffmpegs installed, which is what sent the reporter looking in the wrong place.
  That case now has its own message and its own failure code, since the code is
  rendered to the user verbatim. An EACCES binary counts as found, not missing,
  for the same reason.
- There was no way to override the choice. ARGENT_FFMPEG adds one, and the probe
  is deliberately ADVISORY for it: the probe's one new failure mode is a false
  negative on a build whose help output we don't recognise, and an escape hatch
  subject to the filter it exists to escape would rescue nobody. A pinned binary
  is never silently swapped for a different one either — it fails loudly instead.

`resolveBinary` is deleted rather than extended. Its generic half duplicated
`commandOnPath`, worse: the hand-rolled `/bin/sh -c command -v` could never match
on Windows, where Android recording is reachable. Going through `commandOnPath`
also means the binary we validate is the binary we spawn, instead of resolving
PATH twice and proving nothing.

Not cached on purpose — the tool-server has no idle shutdown by default, so a
cached "no usable ffmpeg" would outlive the user acting on our own error message.
One ~33ms probe per recording start, against a path that already waits ~800ms.

`resolveFfmpeg` keeps its name and stays reachable from ./watermark so the
existing test mock seam still disarms it.

Verified end to end on a host in the reported state (conda's ffmpeg first on
PATH): recording now produces a valid 87KB h264 mp4 at native resolution where it
previously failed instantly.

Fixes #621
2026-08-03 09:50:35 +02:00
Ignacy Łątka 7400179b5b Windows support: Android + Chromium host control plane (#421)
## 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-server` binary, 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**
- New shared `commandOnPath()` helper — `where` on Windows, `command -v`
via `/bin/sh` on POSIX. `android-binary`, `check-deps`, and `vega-cli`
previously hardcoded `/bin/sh`, which never matches on Windows.
- `android-binary`: append `.exe` to `adb`/`emulator` in the
`$ANDROID_HOME` fallback; probe `%LOCALAPPDATA%\Android\Sdk` (Android
Studio's Windows default).
- simulator-server resolver + dispatcher + `bundle-tools` +
`download-simulator-server.sh`: ship and resolve `simulator-server.exe`
on `win32` (new `simulatorServerBinaryName()`), add `win32` to the
bundled host keys, add the `simulator-server-argent-windows.exe`
download target with PE arch validation.
- `stop-metro`: resolve listening PIDs via `netstat -ano` on Windows
(`lsof` is POSIX-only and threw ENOENT).
- `adb.ts`: AVD root resolution used `$HOME` + `startsWith("/")`
(rejects `C:\…`); now `os.homedir()` + `path.isAbsolute()`.
- **`boot-device`**: the emulator was spawned `detached` with
`stdio:"ignore"`; on Windows a detached emulator with NUL stdout/stderr
never reaches `sys.boot_completed` (verified on `windows-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 `.exe` Android resolution, win32 `.exe`
simulator-server resolution, and a win32 AVD drive-path case; dep-gate
tests now mock at the `commandOnPath` boundary so they're
platform-agnostic.

**CI** — `windows-e2e.yml` (`windows-chromium` on `windows-latest`):
runs the resolver/dep unit tests natively on Windows, confirms `adb.exe`
resolution 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 in `scripts/ci/windows-chromium-e2e.mjs`
(also runnable locally / in Docker). The `simulator-server.exe` build +
the full **Android-emulator E2E on Windows** live in radon CI (the
binary's source is the private radon repo).

## Verification (all green)
- **Chromium control plane on real Windows** (`windows-latest`): `✓
discovered chromium-cdp-9222 → ✓ screenshot → ✓ describe → ✓ gesture-tap
→ ✓ DOM mutation observed`. Resolver/dep unit tests run natively;
`adb.exe` resolves via the SDK root (it's *not* on PATH on the runner —
the gap the resolver closes).
- **Android emulator on real Windows** (radon#134, `windows-latest` /
WHPX): the real Argent path — `boot-device` → `{ booted: true }` →
screenshot via `simulator-server.exe` → gesture-tap — all green. (The
boot-device fix above is what makes the detached emulator reach
`boot_completed`.)
- **Local clean-room E2E**: the Chromium flow runs green in a headless
Linux Docker container (fresh `npm ci`, no host pollution).
- **No regressions**: full tool-server suite (1624) +
native-devtools-ios (16); ESLint/Prettier/typecheck clean; **Wayland
E2E** (Linux AVD boot) and **Vega VVD E2E** still pass (they exercise
the changed `adb.ts` / `boot-device.ts` / `vega-cli.ts` paths).

## Notes
Android-emulator E2E needs WHPX nested virtualization — confirmed
available on the current hosted `windows-latest` image (Server 2025);
the emulator boots hardware-accelerated in ~3 min.

---------

Co-authored-by: Ignacy Łątka <claude-hubert.gancarczyk@swmansion.com>
Co-authored-by: Ignacy Łątka <latekvo@users.noreply.github.com>
2026-07-22 13:53:32 +02:00