diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 00000000..0b3b7b76 --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,35 @@ +# Devcontainer image: a standard "base:ubuntu" devcontainer + the NEAR +# git-storage sandbox artifacts copied straight from the published image. +# +# Using `COPY --from=…` (multi-stage) means the sandbox binaries and +# compiled wasm contracts are baked into this image at build time — no +# docker access needed at runtime. The container can run the sandbox as +# a plain process (`near-git-sandbox` launcher below) and the e2e suite +# talks to it over localhost. +FROM ghcr.io/petersalomonsen/near-git-storage/sandbox:main AS sandbox + +FROM mcr.microsoft.com/devcontainers/base:ubuntu-24.04 + +# Web Audio in headless Chromium needs an output sink, otherwise the +# audio-worklet code never starts and the broadcast / audio-comparison +# specs hang on the play-toggle check. +RUN apt-get update \ + && apt-get install -y --no-install-recommends pulseaudio \ + && rm -rf /var/lib/apt/lists/* + +# Sandbox artifacts: near-sandbox binary, the git-server wrapper, and +# the compiled wasm contracts the factory boot script needs. +COPY --from=sandbox /usr/local/bin/git-server /usr/local/bin/git-server +COPY --from=sandbox /app /opt/near-sandbox +COPY --from=sandbox /root/.near /opt/near-sandbox/.near + +# Launcher: chdir into /opt/near-sandbox so git-server finds its res/*.wasm, +# point .near at the bundled state, default the listen address to the port +# the e2e helpers expect. +RUN cat <<'WRAPPER' > /usr/local/bin/near-git-sandbox && chmod +x /usr/local/bin/near-git-sandbox +#!/bin/bash +exec env \ + LISTEN_ADDR="${LISTEN_ADDR:-0.0.0.0:3030}" \ + HOME="${HOME:-/opt/near-sandbox}" \ + bash -c 'cd /opt/near-sandbox && exec git-server "$@"' near-git-sandbox "$@" +WRAPPER diff --git a/.devcontainer/README.md b/.devcontainer/README.md new file mode 100644 index 00000000..be9078c3 --- /dev/null +++ b/.devcontainer/README.md @@ -0,0 +1,87 @@ +# Devcontainer + +Self-contained dev environment for `wasmaudioworklet`: Node + Playwright +browsers + the NEAR git-storage sandbox, all running as plain processes +inside one Ubuntu container. The Playwright e2e suite can run end-to-end +against the sandbox at `localhost:3030` with no `docker run …` from the +test code. + +## How it's wired + +`Dockerfile` is a multi-stage build that bakes the sandbox artifacts +into the dev image — no docker access needed at runtime, which keeps +the setup working on GitHub Codespaces (no docker-outside-of-docker +needed) as well as locally: + +- `FROM ghcr.io/petersalomonsen/near-git-storage/sandbox:main AS sandbox` + — pulled purely to `COPY --from=…`. +- `FROM mcr.microsoft.com/devcontainers/base:ubuntu-24.04` — the actual + base. Installs `pulseaudio` (Web Audio sink — without it audio-worklet + code never starts and the broadcast / audio-comparison specs hang on + the play-toggle check) and copies in the sandbox's `git-server` binary, + `/app` (which contains `res/*.wasm`), and `.near` state. +- Drops a `near-git-sandbox` launcher onto PATH that chdirs into + `/opt/near-sandbox` and runs `git-server` on `localhost:3030`. + +`post-create.sh` then just does the Node side: + +1. Boots `pulseaudio`. +2. `yarn install` + `yarn playwright install chromium` in `wasmaudioworklet/`. +3. `npm install` in `tools/faust2as/` (Faust → AS source generator used + by `e2e/faust2as-compilation.spec.js`) and `tools/claude-bridge/` + (relay spawned by `e2e/claude-bridge.spec.js`). + +## Running the e2e suite + +```sh +# 1. Boot the sandbox (port 3030). Picks up the wasm contracts from +# /opt/near-sandbox/res/. +near-git-sandbox & + +# 2. Regenerate the Faust test sources (only needed when transpiler or +# upstream Faust examples change — committed output lives under +# wasmaudioworklet/faust/faust-test-sources.js so this is optional). +node tools/faust2as/generate-test-sources.js + +# 3. Run the suite. --workers=1 because the sandbox-using specs share a +# single NEAR_REPO_CONTRACT; running in parallel causes pushBaseline +# conflicts. +cd wasmaudioworklet +yarn playwright test --workers=1 +``` + +## Why "sandbox as a process", not a sibling docker container + +Earlier iterations of the test setup booted the sandbox image with +`docker run -p 3030:8080` from outside the dev environment. That's fine +on Linux hosts but breaks on macOS: + +- Docker Desktop / colima on macOS routes container networking through + a VM, so `--network host` *doesn't* share the actual host network. + A container started with `--network host` can't reach a sandbox + bound on the host's `localhost:3030`. +- The workaround (shared bridge network with named aliases) requires + rewriting helpers that hard-code `localhost:3030`. + +Running the sandbox **inside** the dev container sidesteps all of that: +both the test runner and `git-server` live in the same Linux process +space and just talk over `localhost`. CI does the same thing (the +GitHub workflow `docker run`s the sandbox image alongside, but on a +proper Linux host) — this devcontainer setup matches that behaviour. + +## Matching Playwright versions + +When using an off-the-shelf playwright Docker image to reproduce CI +locally, the image's playwright version **must** match +`wasmaudioworklet/package.json`'s pinned `playwright` (currently +`1.59.1`). Mismatch surfaces as: + +``` +Error: browserType.launch: Executable doesn't exist at + /ms-playwright/chromium_headless_shell-1217/chrome-linux/headless_shell +… current: mcr.microsoft.com/playwright:v1.60.0-noble +… required: mcr.microsoft.com/playwright:v1.59.1-noble +``` + +`./node_modules/.bin/playwright install chromium` inside the container +fixes that ad-hoc, but matching the image tag is the cleaner approach. diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 6f8950c3..ff4ecdbf 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -1,4 +1,8 @@ { + // Multi-stage Dockerfile bakes in the NEAR git-storage sandbox + // artifacts via `COPY --from=…` so no docker access is needed at + // runtime. See ./Dockerfile + ./README.md for the design. + "build": { "dockerfile": "Dockerfile" }, "postCreateCommand": "./.devcontainer/post-create.sh", "customizations": { "vscode": { @@ -7,4 +11,4 @@ ] } } -} \ No newline at end of file +} diff --git a/.devcontainer/post-create.sh b/.devcontainer/post-create.sh index 0ca3a21c..05fd927f 100755 --- a/.devcontainer/post-create.sh +++ b/.devcontainer/post-create.sh @@ -1,8 +1,20 @@ #!/bin/bash -sudo apt-get install -y pulseaudio -pulseaudio -D --exit-idle-time=-1 -cd wasmaudioworklet -yarn install -yarn playwright install-deps -yarn playwright install +# Devcontainer one-time setup. Sandbox artifacts are already baked into +# the image (see ./Dockerfile), so all we have to do here is install the +# Node-side deps and Playwright browsers. +# +# Once this completes, you should be able to: +# near-git-sandbox & +# cd wasmaudioworklet && yarn playwright test --workers=1 +set -euo pipefail +pulseaudio -D --exit-idle-time=-1 || true # already running on rerun + +(cd wasmaudioworklet && yarn install --frozen-lockfile) +(cd wasmaudioworklet && yarn playwright install-deps && yarn playwright install chromium) + +# tools/faust2as drives the Faust → AssemblyScript transpiler used by both +# the dev app and the e2e source-generator. +(cd tools/faust2as && npm install --no-audit --no-fund) +# tools/claude-bridge ships the relay that claude-bridge.spec.js spawns. +(cd tools/claude-bridge && npm install --no-audit --no-fund)