diff --git a/CHANGELOG.md b/CHANGELOG.md index fe12e2e..d3ce64b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ ### Fixed +- Fixed managed Linux desktop/browser leases to preinstall video capture and native addon build helpers, avoiding per-scenario apt installs in browser QA runs. - Fixed managed Linux desktop bootstrap performance by using a lean Openbox/Xvfb desktop stack instead of the heavier XFCE meta package. - Fixed SSH readiness progress logs to distinguish open TCP ports, failed SSH authentication, and failed Crabbox ready checks. - Fixed auto-shell command reconstruction so arguments with spaces stay quoted when shell operators such as `&&` are present. diff --git a/docs/commands/vnc.md b/docs/commands/vnc.md index 384e8ff..b837c17 100644 --- a/docs/commands/vnc.md +++ b/docs/commands/vnc.md @@ -146,7 +146,7 @@ host's VNC or Screen Sharing prompt. | Provider / target | Managed VNC | Notes | | --- | --- | --- | -| Hetzner Linux | Yes | Requires `--desktop`; installs XFCE, Xvfb, and x11vnc. | +| Hetzner Linux | Yes | Requires `--desktop`; installs Openbox, Xvfb, x11vnc, and capture tools. | | AWS Linux | Yes | Requires `--desktop`; same Linux desktop profile. | | AWS Windows | Yes | Requires `--target windows --desktop`; installs Git for Windows and TightVNC after EC2Launch enables OpenSSH. Spot or On-Demand follows the AWS capacity config. | | AWS macOS | Yes | Requires `--target macos --desktop --market on-demand` plus `CRABBOX_AWS_MAC_HOST_ID` or `aws.macHostId`. | diff --git a/docs/features/runner-bootstrap.md b/docs/features/runner-bootstrap.md index 9bab49d..edbb509 100644 --- a/docs/features/runner-bootstrap.md +++ b/docs/features/runner-bootstrap.md @@ -26,10 +26,13 @@ Bootstrap installs: Bootstrap intentionally does not install project language runtimes such as Go, Node, pnpm, Docker, databases, or service dependencies. Those belong in GitHub Actions hydration, devcontainers, Nix, mise/asdf, or repository setup scripts. A brokered machine should not pass readiness until `crabbox-ready` succeeds over SSH. -Interactive desktop tooling is an optional lease profile, not part of the -minimal bootstrap. See [Interactive desktop and VNC](interactive-desktop-vnc.md) -for the planned boundary: Crabbox owns the desktop/VNC machine capability, while -scenario systems own browser automation and proof artifacts. +Interactive desktop and browser tooling are optional lease profiles, not part +of the minimal bootstrap. The desktop profile installs Xvfb/Openbox, x11vnc, +screenshots, and video capture tools. The browser profile installs +Chrome/Chromium plus native addon build helpers that browser-channel QA often +needs during dependency fallback installs. Crabbox owns these machine +capabilities; scenario systems still own browser automation and proof artifacts. +See [Interactive desktop and VNC](interactive-desktop-vnc.md). Tailscale is optional too. `--tailscale` on a managed Linux lease installs the Tailscale package, joins the configured tailnet, writes non-secret metadata diff --git a/docs/features/vnc-linux.md b/docs/features/vnc-linux.md index 64f9405..7e7141d 100644 --- a/docs/features/vnc-linux.md +++ b/docs/features/vnc-linux.md @@ -24,8 +24,10 @@ Managed Linux desktop leases include: - Xvfb on `:99`; - a lightweight desktop/window-manager session; - x11vnc bound to `127.0.0.1:5900`; +- screenshot and video capture tools (`scrot` and `ffmpeg`); - a generated per-lease VNC password at `/var/lib/crabbox/vnc.password`; -- optional Chrome stable or Chromium fallback when `--browser` is requested; +- optional Chrome stable or Chromium fallback, first-run suppression, and native + addon build helpers when `--browser` is requested; - readiness checks that verify desktop services when `desktop=true`. `crabbox run --desktop` injects `CRABBOX_DESKTOP=1` and `DISPLAY=:99`. diff --git a/internal/cli/bootstrap.go b/internal/cli/bootstrap.go index 12ab4fe..1a629f7 100644 --- a/internal/cli/bootstrap.go +++ b/internal/cli/bootstrap.go @@ -528,7 +528,7 @@ func cloudInitOptionalBootstrap(cfg Config) string { parts = append(parts, cloudInitTailscaleBootstrap(cfg)) } if cfg.Desktop { - parts = append(parts, ` retry apt-get install -y --no-install-recommends xvfb openbox x11vnc xauth dbus-x11 x11-xserver-utils xterm scrot xdotool wmctrl fonts-dejavu-core fonts-liberation iproute2 openssl + parts = append(parts, ` retry apt-get install -y --no-install-recommends xvfb openbox x11vnc xauth dbus-x11 x11-xserver-utils xterm scrot ffmpeg xdotool wmctrl fonts-dejavu-core fonts-liberation iproute2 openssl install -d -m 0750 -o crabbox -g crabbox /var/lib/crabbox if [ ! -s /var/lib/crabbox/vnc.password ]; then (umask 077 && openssl rand -base64 18 > /var/lib/crabbox/vnc.password) @@ -540,7 +540,7 @@ func cloudInitOptionalBootstrap(cfg Config) string { systemctl enable --now crabbox-xvfb.service crabbox-desktop.service crabbox-desktop-session.service crabbox-x11vnc.service`) } if cfg.Browser { - parts = append(parts, ` retry apt-get install -y --no-install-recommends gnupg + parts = append(parts, ` retry apt-get install -y --no-install-recommends gnupg build-essential python3 browser_path="" if [ "$(dpkg --print-architecture)" = "amd64" ]; then install -d -m 0755 /etc/apt/trusted.gpg.d diff --git a/internal/cli/bootstrap_test.go b/internal/cli/bootstrap_test.go index 7d68dfb..ba025ad 100644 --- a/internal/cli/bootstrap_test.go +++ b/internal/cli/bootstrap_test.go @@ -57,7 +57,7 @@ func TestCloudInitDesktopProfile(t *testing.T) { got := cloudInit(cfg, "ssh-ed25519 test") for _, want := range []string{ "xvfb openbox x11vnc xauth dbus-x11", - "x11-xserver-utils xterm scrot xdotool wmctrl", + "x11-xserver-utils xterm scrot ffmpeg xdotool wmctrl", "/etc/systemd/system/crabbox-xvfb.service", "/etc/systemd/system/crabbox-desktop.service", "/usr/local/bin/crabbox-desktop-session", @@ -84,6 +84,7 @@ func TestCloudInitBrowserProfile(t *testing.T) { cfg.Browser = true got := cloudInit(cfg, "ssh-ed25519 test") for _, want := range []string{ + "gnupg build-essential python3", "https://dl.google.com/linux/linux_signing_key.pub", "chmod 0644 /etc/apt/trusted.gpg.d/google.asc", "https://dl.google.com/linux/chrome/deb/", diff --git a/worker/src/bootstrap.ts b/worker/src/bootstrap.ts index 554dc08..ed4dc58 100644 --- a/worker/src/bootstrap.ts +++ b/worker/src/bootstrap.ts @@ -408,7 +408,7 @@ function optionalBootstrap(config: LeaseConfig): string { parts.push(tailscaleBootstrap(config)); } if (config.desktop) { - parts.push(` retry apt-get install -y --no-install-recommends xvfb openbox x11vnc xauth dbus-x11 x11-xserver-utils xterm scrot xdotool wmctrl fonts-dejavu-core fonts-liberation iproute2 openssl + parts.push(` retry apt-get install -y --no-install-recommends xvfb openbox x11vnc xauth dbus-x11 x11-xserver-utils xterm scrot ffmpeg xdotool wmctrl fonts-dejavu-core fonts-liberation iproute2 openssl install -d -m 0750 -o crabbox -g crabbox /var/lib/crabbox if [ ! -s /var/lib/crabbox/vnc.password ]; then (umask 077 && openssl rand -base64 18 > /var/lib/crabbox/vnc.password) @@ -420,7 +420,7 @@ function optionalBootstrap(config: LeaseConfig): string { systemctl enable --now crabbox-xvfb.service crabbox-desktop.service crabbox-desktop-session.service crabbox-x11vnc.service`); } if (config.browser) { - parts.push(` retry apt-get install -y --no-install-recommends gnupg + parts.push(` retry apt-get install -y --no-install-recommends gnupg build-essential python3 browser_path="" if [ "$(dpkg --print-architecture)" = "amd64" ]; then install -d -m 0755 /etc/apt/trusted.gpg.d diff --git a/worker/test/bootstrap.test.ts b/worker/test/bootstrap.test.ts index 75cbfd4..beca9c0 100644 --- a/worker/test/bootstrap.test.ts +++ b/worker/test/bootstrap.test.ts @@ -79,7 +79,7 @@ describe("cloud-init bootstrap", () => { expect(got).toContain("ExecStart=/usr/bin/openbox"); expect(got).toContain("systemctl is-active --quiet crabbox-desktop.service"); expect(got).toContain("systemctl is-active --quiet crabbox-desktop-session.service"); - expect(got).toContain("x11-xserver-utils xterm scrot xdotool wmctrl"); + expect(got).toContain("x11-xserver-utils xterm scrot ffmpeg xdotool wmctrl"); expect(got).toContain("xsetroot -solid '#20242b'"); expect(got).toContain("xterm -title 'Crabbox Desktop'"); expect(got).toContain("(umask 077 && openssl rand -base64 18 > /var/lib/crabbox/vnc.password)"); @@ -105,6 +105,7 @@ describe("cloud-init bootstrap", () => { it("adds browser setup only when requested", () => { const got = cloudInit({ ...config, browser: true }); + expect(got).toContain("gnupg build-essential python3"); expect(got).toContain("https://dl.google.com/linux/linux_signing_key.pub"); expect(got).toContain("chmod 0644 /etc/apt/trusted.gpg.d/google.asc"); expect(got).toContain("https://dl.google.com/linux/chrome/deb/");