Skip to content

Run the browser tests on a Modal GPU in CI - #97

Open
roprgm wants to merge 5 commits into
simpler-gpu-testsfrom
modal-ci
Open

roprgm wants to merge 5 commits into
simpler-gpu-testsfrom
modal-ci

Conversation

@roprgm

@roprgm roprgm commented Oct 15, 2026 •

Copy link
Copy Markdown
Owner

Change

Browser tests run on a Modal L4 GPU for every pull request. bun run test:modal [file] runs the same suite from any machine without a GPU. Browser tests also share one editor page per worker instead of reloading it for each test.

  • tests/modal.ts: starts a GPU sandbox, uploads the working tree, runs bun run test:browser --workers=8, streams the output, and brings back test-results/ on failure.
  • Image cache: dependencies are an image layer keyed on the contents of package.json and bun.lock. A cached sandbox is ready in about 2 s.
  • Chromium build: Modal hands WebGPU only to Chromium's headless shell, so the sandbox sets PLAYWRIGHT_CHANNEL. Local and macOS runs still use chromium.
  • CI: a browser job runs next to check, signed in with the MODAL_TOKEN_ID and MODAL_TOKEN_SECRET secrets.
  • Shared page: opening an image starts a new document, which the editor mounts afresh. openImage and openPhoto reuse the loaded editor.
    • Tests that change the page itself set isolated: routes, init scripts, a fake clock, or touch.
    • A failure drops the shared page.
    • On Modal a reload costs about 2 s, against 0.3 s for opening another image.
  • choose: puts the pointer back on the select, so no tooltip left where the list was takes the test's next Escape.

Stacked on #96, whose GPU-agnostic tests pass on NVIDIA.

Verification

Before After
CI browser job, warm cache 1 min 40 s 1 min 08 s – 1 min 23 s
Tests on Modal, 8 workers 60–83 s 54–66 s
Devbox, all workers 46–53 s 33–44 s
Devbox, 1 worker 132 s 102 s
  • Passing runs: 53 passed and 1 skipped (HEIC has no HEVC decoder) in every run: three local parallel runs, one single-worker run where every test shares one page, two Modal runs, and three CI runs.
  • Workers: 12 or 16 workers on Modal are no faster and time out under GPU contention.
  • Other checks: bun run check, bun run build, and bun run test pass.

🤖 Generated with Claude Code

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@vercel

vercel Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
openlight Ready Ready Preview Oct 15, 2026 7:33am UTC

Request Review

@coderabbitai

coderabbitai Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration
  • Configuration used: defaults
  • Review profile: CHILL
  • Plan: Essentials
  • Run ID: a29e4904-6c3c-4d08-b259-43ebc410a49e

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Comment @coderabbitai help to get the list of available commands.

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 2 potential issues.

Devin Review

Comment thread .github/workflows/ci.yml
Comment on lines +41 to +45
- name: Browser tests on a Modal GPU
run: bun run test:modal
env:
MODAL_TOKEN_ID: ${{ secrets.MODAL_TOKEN_ID }}
MODAL_TOKEN_SECRET: ${{ secrets.MODAL_TOKEN_SECRET }}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 Fork pull requests fail browser CI

For fork pull requests, test:modal starts without Modal credentials. GitHub withholds repository secrets from fork pull_request jobs, so the browser job fails before running tests.

Learn more

The workflow starts the browser job for every pull request, including forks. GitHub withholds repository secrets from fork pull_request jobs, leaving both Modal token variables empty. Modal authentication then fails before it can create the GPU sandbox, and those pull requests get a failing CI check.

Example: A contributor opens a pull request from their fork. The check job can run, but the browser job receives empty MODAL_TOKEN_ID and MODAL_TOKEN_SECRET and cannot start the suite.

Recommended fix: Gate the Modal job on credentials being available or on an in-repository pull request, and decide how fork pull requests receive browser coverage without exposing credentials to untrusted code. Keep ordinary build and unit checks available to forks.

Devin Review


Was this helpful? React with 👍 or 👎 to provide feedback.

Comment thread tests/modal.ts
// Eight workers keep the GPU busy; more contend for it and time out.
cpu: 8,
memoryMiB: 16384,
timeoutMs: 20 * 60_000,

@devin-ai-integration devin-ai-integration Bot Oct 15, 2026 •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Timed-out CI leaves GPU running

When the browser job reaches its 15-minute timeout, GitHub stops test:modal before its cleanup runs. The sandbox's timeoutMs is 20 minutes, leaving the GPU active after CI ends.

Learn more

The browser job has a 15-minute timeout at browser, but the sandbox is configured to live for 20 minutes. If provisioning or a browser test hangs until GitHub's timeout, the runner kills the process before the finally block can call sandbox.terminate(). Modal can keep the L4 sandbox active until its own timeout.

Example: A run hangs at minute 14. GitHub stops it at minute 15; the remote GPU sandbox can continue running until minute 20 even though no test result can be returned.

Recommended fix: Set the sandbox timeout below the job timeout with enough headroom for artifact collection and cleanup, so Modal ends the GPU session even if the runner is killed.

Devin Review


Was this helpful? React with 👍 or 👎 to provide feedback.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Each worker loads the editor once; a test opens its image as a new document,
which the editor mounts afresh, instead of reloading the page. Tests that
change the page itself, with routes, init scripts, a fake clock, or touch,
keep a page of their own, and a failure drops the shared page.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

This branch was successfully deployed

1 active deployment
Preview — bc155d87 Deployed Oct 15, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant