Skip to content

Commit f9903f9

Browse files
devcontainer: bake the NEAR git-storage sandbox artifacts into the image
Reproducing CI's Playwright e2e flow locally previously meant starting the sandbox as a sibling `docker run` from outside the dev environment. Works on Linux hosts but breaks on macOS — Docker Desktop / colima route container networking through a VM, so `--network host` doesn't share the host's localhost:3030 and the workaround (shared bridge network + named alias) requires rewriting helpers that hard-code localhost:3030. Sidestep all of it by running the sandbox as a regular process inside the devcontainer. The trick: a multi-stage Dockerfile that `COPY --from=ghcr.io/petersalomonsen/near-git-storage/sandbox:main`s the artifacts (near-sandbox binary, git-server wrapper, the compiled wasm contracts, the pre-populated .near state) into the dev image at build time. No docker access needed at runtime — important for GitHub Codespaces, where docker-outside-of-docker isn't available. A `near-git-sandbox` launcher on PATH chdirs into /opt/near-sandbox so git-server finds its res/*.wasm, defaults the listen address to the port the e2e helpers expect, and the rest is just node side: post-create.sh: - pulseaudio (Web Audio sink; without it the audio-worklet code never starts and the broadcast / audio-comparison specs hang) - yarn install + playwright install chromium in wasmaudioworklet/ - npm install in tools/faust2as (Faust → AS source generator used by faust2as-compilation.spec.js) and tools/claude-bridge (relay spawned by claude-bridge.spec.js) .devcontainer/README.md walks through the design and captures the macOS-docker-networking + Playwright-image-version pitfalls for the next agent reproducing the e2e flow from a different angle. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 72cb765 commit f9903f9

4 files changed

Lines changed: 145 additions & 7 deletions

File tree

.devcontainer/Dockerfile

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# Devcontainer image: a standard "base:ubuntu" devcontainer + the NEAR
2+
# git-storage sandbox artifacts copied straight from the published image.
3+
#
4+
# Using `COPY --from=…` (multi-stage) means the sandbox binaries and
5+
# compiled wasm contracts are baked into this image at build time — no
6+
# docker access needed at runtime. The container can run the sandbox as
7+
# a plain process (`near-git-sandbox` launcher below) and the e2e suite
8+
# talks to it over localhost.
9+
FROM ghcr.io/petersalomonsen/near-git-storage/sandbox:main AS sandbox
10+
11+
FROM mcr.microsoft.com/devcontainers/base:ubuntu-24.04
12+
13+
# Web Audio in headless Chromium needs an output sink, otherwise the
14+
# audio-worklet code never starts and the broadcast / audio-comparison
15+
# specs hang on the play-toggle check.
16+
RUN apt-get update \
17+
&& apt-get install -y --no-install-recommends pulseaudio \
18+
&& rm -rf /var/lib/apt/lists/*
19+
20+
# Sandbox artifacts: near-sandbox binary, the git-server wrapper, and
21+
# the compiled wasm contracts the factory boot script needs.
22+
COPY --from=sandbox /usr/local/bin/git-server /usr/local/bin/git-server
23+
COPY --from=sandbox /app /opt/near-sandbox
24+
COPY --from=sandbox /root/.near /opt/near-sandbox/.near
25+
26+
# Launcher: chdir into /opt/near-sandbox so git-server finds its res/*.wasm,
27+
# point .near at the bundled state, default the listen address to the port
28+
# the e2e helpers expect.
29+
RUN cat <<'WRAPPER' > /usr/local/bin/near-git-sandbox && chmod +x /usr/local/bin/near-git-sandbox
30+
#!/bin/bash
31+
exec env \
32+
LISTEN_ADDR="${LISTEN_ADDR:-0.0.0.0:3030}" \
33+
HOME="${HOME:-/opt/near-sandbox}" \
34+
bash -c 'cd /opt/near-sandbox && exec git-server "$@"' near-git-sandbox "$@"
35+
WRAPPER

.devcontainer/README.md

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
# Devcontainer
2+
3+
Self-contained dev environment for `wasmaudioworklet`: Node + Playwright
4+
browsers + the NEAR git-storage sandbox, all running as plain processes
5+
inside one Ubuntu container. The Playwright e2e suite can run end-to-end
6+
against the sandbox at `localhost:3030` with no `docker run …` from the
7+
test code.
8+
9+
## How it's wired
10+
11+
`Dockerfile` is a multi-stage build that bakes the sandbox artifacts
12+
into the dev image — no docker access needed at runtime, which keeps
13+
the setup working on GitHub Codespaces (no docker-outside-of-docker
14+
needed) as well as locally:
15+
16+
- `FROM ghcr.io/petersalomonsen/near-git-storage/sandbox:main AS sandbox`
17+
— pulled purely to `COPY --from=…`.
18+
- `FROM mcr.microsoft.com/devcontainers/base:ubuntu-24.04` — the actual
19+
base. Installs `pulseaudio` (Web Audio sink — without it audio-worklet
20+
code never starts and the broadcast / audio-comparison specs hang on
21+
the play-toggle check) and copies in the sandbox's `git-server` binary,
22+
`/app` (which contains `res/*.wasm`), and `.near` state.
23+
- Drops a `near-git-sandbox` launcher onto PATH that chdirs into
24+
`/opt/near-sandbox` and runs `git-server` on `localhost:3030`.
25+
26+
`post-create.sh` then just does the Node side:
27+
28+
1. Boots `pulseaudio`.
29+
2. `yarn install` + `yarn playwright install chromium` in `wasmaudioworklet/`.
30+
3. `npm install` in `tools/faust2as/` (Faust → AS source generator used
31+
by `e2e/faust2as-compilation.spec.js`) and `tools/claude-bridge/`
32+
(relay spawned by `e2e/claude-bridge.spec.js`).
33+
34+
## Running the e2e suite
35+
36+
```sh
37+
# 1. Boot the sandbox (port 3030). Picks up the wasm contracts from
38+
# /opt/near-sandbox/res/.
39+
near-git-sandbox &
40+
41+
# 2. Regenerate the Faust test sources (only needed when transpiler or
42+
# upstream Faust examples change — committed output lives under
43+
# wasmaudioworklet/faust/faust-test-sources.js so this is optional).
44+
node tools/faust2as/generate-test-sources.js
45+
46+
# 3. Run the suite. --workers=1 because the sandbox-using specs share a
47+
# single NEAR_REPO_CONTRACT; running in parallel causes pushBaseline
48+
# conflicts.
49+
cd wasmaudioworklet
50+
yarn playwright test --workers=1
51+
```
52+
53+
## Why "sandbox as a process", not a sibling docker container
54+
55+
Earlier iterations of the test setup booted the sandbox image with
56+
`docker run -p 3030:8080` from outside the dev environment. That's fine
57+
on Linux hosts but breaks on macOS:
58+
59+
- Docker Desktop / colima on macOS routes container networking through
60+
a VM, so `--network host` *doesn't* share the actual host network.
61+
A container started with `--network host` can't reach a sandbox
62+
bound on the host's `localhost:3030`.
63+
- The workaround (shared bridge network with named aliases) requires
64+
rewriting helpers that hard-code `localhost:3030`.
65+
66+
Running the sandbox **inside** the dev container sidesteps all of that:
67+
both the test runner and `git-server` live in the same Linux process
68+
space and just talk over `localhost`. CI does the same thing (the
69+
GitHub workflow `docker run`s the sandbox image alongside, but on a
70+
proper Linux host) — this devcontainer setup matches that behaviour.
71+
72+
## Matching Playwright versions
73+
74+
When using an off-the-shelf playwright Docker image to reproduce CI
75+
locally, the image's playwright version **must** match
76+
`wasmaudioworklet/package.json`'s pinned `playwright` (currently
77+
`1.59.1`). Mismatch surfaces as:
78+
79+
```
80+
Error: browserType.launch: Executable doesn't exist at
81+
/ms-playwright/chromium_headless_shell-1217/chrome-linux/headless_shell
82+
… current: mcr.microsoft.com/playwright:v1.60.0-noble
83+
… required: mcr.microsoft.com/playwright:v1.59.1-noble
84+
```
85+
86+
`./node_modules/.bin/playwright install chromium` inside the container
87+
fixes that ad-hoc, but matching the image tag is the cleaner approach.

.devcontainer/devcontainer.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
11
{
2+
// Multi-stage Dockerfile bakes in the NEAR git-storage sandbox
3+
// artifacts via `COPY --from=…` so no docker access is needed at
4+
// runtime. See ./Dockerfile + ./README.md for the design.
5+
"build": { "dockerfile": "Dockerfile" },
26
"postCreateCommand": "./.devcontainer/post-create.sh",
37
"customizations": {
48
"vscode": {
@@ -7,4 +11,4 @@
711
]
812
}
913
}
10-
}
14+
}

.devcontainer/post-create.sh

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,20 @@
11
#!/bin/bash
2-
sudo apt-get install -y pulseaudio
3-
pulseaudio -D --exit-idle-time=-1
4-
cd wasmaudioworklet
5-
yarn install
6-
yarn playwright install-deps
7-
yarn playwright install
2+
# Devcontainer one-time setup. Sandbox artifacts are already baked into
3+
# the image (see ./Dockerfile), so all we have to do here is install the
4+
# Node-side deps and Playwright browsers.
5+
#
6+
# Once this completes, you should be able to:
7+
# near-git-sandbox &
8+
# cd wasmaudioworklet && yarn playwright test --workers=1
9+
set -euo pipefail
810

11+
pulseaudio -D --exit-idle-time=-1 || true # already running on rerun
12+
13+
(cd wasmaudioworklet && yarn install --frozen-lockfile)
14+
(cd wasmaudioworklet && yarn playwright install-deps && yarn playwright install chromium)
15+
16+
# tools/faust2as drives the Faust → AssemblyScript transpiler used by both
17+
# the dev app and the e2e source-generator.
18+
(cd tools/faust2as && npm install --no-audit --no-fund)
19+
# tools/claude-bridge ships the relay that claude-bridge.spec.js spawns.
20+
(cd tools/claude-bridge && npm install --no-audit --no-fund)

0 commit comments

Comments
 (0)