サンプルリポジトリ

https://github.com/parallel-sandbox 配下の公開リポジトリが 2 つあります。どちらもローカルで docker compose up で動き、README にボックス内での動かし方がツール呼び出し単位で書いてあります。プラットフォーム自体のテストにもこの 2 つを使っています。

example-compose-app

https://github.com/parallel-sandbox/example-compose-app

ジョブをキューに入れる Node API(ポート 3000)と、それを処理する Go ワーカー(ポート 8080)を docker-compose.yml でつないだもの。e2e/ には、実際のブラウザでページを開き、ジョブを追加し、ワーカーが終えるのを待ってスクリーンショットを保存する Playwright テストがあります。

ボックス内で試せること:git clonedocker compose up --build、2 つのサービスの sandbox_wire、仮想ディスプレイ上のヘッド付きブラウザ、sandbox_shot の録画、sandbox_get でのスクリーンショット取得。

sandbox_start { "services": [{ "name": "api", "port": 3000 }, { "name": "worker", "port": 8080 }] }
sandbox_exec  { "id": "<id>", "cmd": "git clone https://github.com/parallel-sandbox/example-compose-app.git" }
sandbox_exec  { "id": "<id>", "cmd": "docker compose up -d --build --wait", "cwd": "example-compose-app", "timeoutSec": 600 }
sandbox_wire  { "id": "<id>", "service": "api", "mode": "box" }
sandbox_wire  { "id": "<id>", "service": "worker", "mode": "box" }
sandbox_exec  { "id": "<id>", "cmd": "npm ci && npx playwright install chromium", "cwd": "example-compose-app/e2e", "timeoutSec": 600 }
sandbox_shot  { "id": "<id>", "record": "start" }
sandbox_exec  { "id": "<id>", "cmd": "DISPLAY=:99 HEADED=1 BASE_URL=http://localhost:3000 npx playwright test", "cwd": "example-compose-app/e2e", "timeoutSec": 300 }
sandbox_shot  { "id": "<id>", "record": "stop" }
sandbox_get   { "id": "<id>", "path": "example-compose-app/e2e/screenshots/jobs.png" }
sandbox_stop  { "id": "<id>" }

README にはもう一つの配線モードも書いてあります。変更したサービスだけをボックスで動かし、変更していないものは externalBaseUrlmode: "external" で自分の環境に向けます。

example-web-with-logs

https://github.com/parallel-sandbox/example-web-with-logs

ブラウザログ SDK @parallelsandbox/log を付けた 1 枚の静的ページ。ボタンを押すと 3 段の関数呼び出しの奥で未捕捉のエラーを投げます。ページは esbuild でビルド(minify、ソースマップ付き)、nginx で配信、コンテナ起動時に環境変数から設定を書き出すので、プロジェクトの write key はイメージに入りません。

ボックス内で試せること:sandbox_startsecrets、ソースマップのアップロード、仮想ディスプレイ上のページ、logs_errorssrc/app.js の行に解決されたスタック付きでエラーを返すこと。

sandbox_start { "services": [{ "name": "web", "port": 8080 }], "secrets": ["PSBX_LOG_WRITE_KEY"] }
sandbox_exec  { "id": "<id>", "cmd": "git clone https://github.com/parallel-sandbox/example-web-with-logs.git" }
sandbox_exec  { "id": "<id>", "cmd": "PSBX_RELEASE=$(git rev-parse --short HEAD) PSBX_LOG_PROJECT=<project id> PSBX_LOG_ENDPOINT=https://log.parallelsandbox.com docker compose up -d --build --wait", "cwd": "example-web-with-logs", "timeoutSec": 600 }
sandbox_wire  { "id": "<id>", "service": "web", "mode": "box" }

ソースマップはボックスではなく自分のマシンからアップロードします。API キーがボックスに入らないためです。同じコミットなので release id も同じです:

git clone https://github.com/parallel-sandbox/example-web-with-logs.git && cd example-web-with-logs
npm ci && PSBX_RELEASE=$(git rev-parse --short HEAD) npm run build
PSBX_API_KEY=<api key> PSBX_LOG_PROJECT=<project id> ./scripts/upload-sourcemaps.sh

ボックスに戻って:

sandbox_exec  { "id": "<id>", "cmd": "DISPLAY=:99 chromium --no-sandbox --kiosk --window-size=1280,800 --user-data-dir=/tmp/chrome http://localhost:8080/", "background": true }
sandbox_shot  { "id": "<id>" }
sandbox_exec  { "id": "<id>", "cmd": "DISPLAY=:99 xdotool mousemove 640 300 click 1" }
logs_errors   { "project": "<project id>", "since": "10m" }
sandbox_stop  { "id": "<id>" }

テンプレートとして使う

どちらのリポジトリもごく普通の形です。ルートに docker-compose.yml、サービスごとにディレクトリと Dockerfile、テストは専用ディレクトリ。この形のリポジトリなら、ボックスでの動かし方は同じです。ParallelSandbox 固有なのは sandbox_startservices 一覧(名前とポート)と、ブラウザエラーを集めたい場合のログ SDK だけです。