diff --git a/AGENTS.md b/AGENTS.md index c154c64399..c6d5949c51 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -103,7 +103,7 @@ When a change is scoped to a single subtree, update the scoped `AGENTS.md` inste ## Remote cloud environment -For developing in ephemeral cloud VMs (Cursor Cloud, Codex, GitHub Codespaces, etc.), see [`docs/remote-cloud-environment.md`](docs/remote-cloud-environment.md) — covers runtime requirements, generated-file gotchas, and dev-mode setup. +For developing in ephemeral cloud VMs (Cursor Cloud, Codex, GitHub Codespaces, etc.), see [`docs/remote-cloud-environment.md`](docs/remote-cloud-environment.md) — covers runtime requirements, generated-file gotchas, dev-mode setup, key commands, and Firecracker-specific caveats. --- diff --git a/docs/remote-cloud-environment.md b/docs/remote-cloud-environment.md index ae5b069f5e..6adf27f473 100644 --- a/docs/remote-cloud-environment.md +++ b/docs/remote-cloud-environment.md @@ -33,3 +33,13 @@ All documented in the root `Makefile`: - `make lint` — golangci-lint + ESLint - `make typecheck` — TypeScript type-checking across all apps - `make fmt` — format all code (run before lint to avoid false positives) +- `make test-e2e` — Playwright E2E tests (requires `make build-backend && make build-web` first). Uses `e2e/playwright.config.ts`. 1000+ specs. + +## Firecracker / cloud-VM caveats + +These apply to Cursor Cloud, Codex, and similar Firecracker-backed sandboxes: + +- **golangci-lint PATH**: `go install` places the binary in `~/go/bin/`. Ensure `PATH` includes it (the update script appends to `~/.bashrc`, but in-session shells may need `export PATH="$PATH:$HOME/go/bin"`). +- **First page load**: `make dev` compiles pages on first visit via Turbopack — expect ~25 s for the initial load. +- **CLI test flake**: `src/ports.test.ts > isPortInUse > returns false within the timeout when the host black-holes packets` fails because `192.0.2.1` behaves differently inside Firecracker. This is a known environment-specific flake, not a code issue. +- **Playwright browser installation**: `playwright install chromium` hangs during zip extraction (Node.js io_uring incompatibility with the Firecracker kernel). Workaround: `scripts/install-playwright-browsers.sh` runs the install with a timeout, then falls back to extracting the already-downloaded zips with `unzip`. The update script calls this automatically. System deps can be installed via `playwright install-deps chromium`. diff --git a/scripts/install-playwright-browsers.sh b/scripts/install-playwright-browsers.sh new file mode 100755 index 0000000000..d8ba823864 --- /dev/null +++ b/scripts/install-playwright-browsers.sh @@ -0,0 +1,64 @@ +#!/usr/bin/env bash +# install-playwright-browsers.sh — install Playwright chromium browser +# with a workaround for Firecracker VMs where Node.js io_uring-based +# zip extraction hangs indefinitely. +# +# Strategy: run `playwright install chromium` with a timeout. If it +# completes, great. If it hangs (extraction stuck), kill it and +# manually extract the already-downloaded zips with `unzip`. + +set -euo pipefail + +PW_CACHE="${PLAYWRIGHT_BROWSERS_PATH:-$HOME/.cache/ms-playwright}" +TIMEOUT_SECS=90 + +cd "$(dirname "$0")/../apps" + +# Check if browsers are already fully installed +if pnpm --filter @kandev/web exec playwright install chromium --dry-run 2>&1 | grep -q "already installed"; then + echo "[playwright] browsers already installed" + exit 0 +fi + +# Install system dependencies (idempotent, needs sudo) +pnpm --filter @kandev/web exec playwright install-deps chromium 2>/dev/null || true + +# Attempt normal install with timeout +rm -rf "$PW_CACHE/__dirlock" +if timeout "$TIMEOUT_SECS" pnpm --filter @kandev/web exec playwright install chromium 2>&1; then + echo "[playwright] install completed normally" + exit 0 +fi + +echo "[playwright] install timed out — falling back to manual extraction" + +# Kill any leftover oopDownload processes +pkill -f oopDownloadBrowserMain 2>/dev/null || true +sleep 1 + +# Manually extract any downloaded zips that weren't fully extracted +for comp_dir in "$PW_CACHE"/chromium-* "$PW_CACHE"/chromium_headless_shell-* "$PW_CACHE"/ffmpeg-*; do + [ -d "$comp_dir" ] || continue + [ -f "$comp_dir/INSTALLATION_COMPLETE" ] && continue + + comp_name=$(basename "$comp_dir" | sed 's/-[0-9]*$//') + zipfile=$(find /tmp -name "playwright-download-${comp_name}-*" -type f 2>/dev/null | sort | tail -1) + + if [ -z "$zipfile" ]; then + echo "[playwright] no zip found for $comp_name — skipping" + continue + fi + + if ! unzip -t "$zipfile" >/dev/null 2>&1; then + echo "[playwright] zip corrupt for $comp_name — skipping" + continue + fi + + echo "[playwright] extracting $comp_name from $zipfile" + rm -rf "$comp_dir" + mkdir -p "$comp_dir" + (cd "$comp_dir" && unzip -q "$zipfile") + echo '{}' > "$comp_dir/INSTALLATION_COMPLETE" +done + +echo "[playwright] manual extraction complete"