feat(selfhost): add optional browserless/chromium service for visual review - #3633
Conversation
…review Adds a --profile visual-review docker-compose service (ghcr.io/browserless/chromium, internal-network-only, resource-bounded) so self-host operators can enable automated before/after PR screenshot capture without standing up their own browser-rendering sidecar. The code-level support (BROWSER_WS_ENDPOINT -> env.BROWSER injection, the puppeteer-core self-host stub) already existed; nothing was actually deployed anywhere until now. Documents BROWSERLESS_TOKEN/BROWSER_WS_ENDPOINT/etc. in .env.example, and extends scripts/smoke-selfhost.sh with an opt-in visual-review mode that boots a real browserless container and asserts /gittensory/shot returns an actual rendered PNG end to end -- verified locally (20526-byte real render of https://example.com through the full self-host chain). Entirely additive: unset/default is byte-identical to today (no container, no screenshots, no error), and docker compose config validates cleanly both with and without the profile active.
|
Superagent didn't find any vulnerabilities or security issues in this PR. |
|
Tip 🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩 ✅ Gittensory review result - approve/merge recommendedReview updated: 2026-07-05 20:17:04 UTC
✅ Suggested Action - Approve/Merge
Review summary Nits — 7 non-blocking
Review context
Contributor next steps
Signal definitions
🟩 Safe / merged · 🟦 Advisory · 🟨 Held for review · 🟥 Blocked / closed 💰 Earn for open-source contributions like this. Gittensor lets GitHub contributors earn for the work they already do — register to start earning →. Checked by Gittensory, a quiet PR intelligence layer for OSS maintainers.
|
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #3633 +/- ##
=======================================
Coverage 93.20% 93.20%
=======================================
Files 313 313
Lines 31910 31910
Branches 11677 11677
=======================================
Hits 29741 29741
Misses 1517 1517
Partials 652 652 🚀 New features to boost your workflow:
|
Closes #3608. Part of #3607.
Summary
--profile visual-reviewdocker-compose service (ghcr.io/browserless/chromium,internal-network-only, resource-bounded via env-overridable
BROWSERLESS_MEM_LIMIT/BROWSERLESS_CONCURRENT/BROWSERLESS_QUEUED/BROWSERLESS_TIMEOUT_MS) so self-host operators canenable automated before/after PR screenshot capture without standing up their own browser-rendering
sidecar.
BROWSER_WS_ENDPOINT→env.BROWSERinjection in
src/server.ts, thepuppeteer-coreself-host stub insrc/selfhost/stubs/puppeteer.ts,Docker image + docker-compose (one-command self-host) #980) — nothing was actually deployed anywhere until this PR.
BROWSERLESS_TOKEN/BROWSER_WS_ENDPOINT/etc. in.env.example(the exhaustive reference;intentionally NOT added to
.env.selfhost.example, matching how other optional profile features likeQdrant are already scoped out of that focused starter file).
scripts/smoke-selfhost.shwith an opt-inSELFHOST_SMOKE_VISUAL_REVIEW=1mode that boots areal browserless container alongside the app and asserts
/gittensory/shotreturns an actual renderedPNG, not just that the app stays healthy.
browserless/chromeimage name.A real bug caught during validation
My first draft used
${BROWSERLESS_TOKEN:?...}(hard-required).docker compose configinterpolatesevery service's env vars for the whole file regardless of which profile is active, so that would have
broken
docker compose upfor every self-host operator NOT using visual review, profile or not. Fixed toa soft
${BROWSERLESS_TOKEN:-}default before this shipped — verified both paths explicitly (see test plan).Test plan
docker compose config --quietvalidates cleanly both with and without--profile visual-review,and without
BROWSERLESS_TOKENset (confirms the fix above actually holds)--build-arg INSTALL_VISUAL_REVIEW=true; confirmedpuppeteer-coreis actually present in the resulting image
browserless/chromiumcontainer + the app,hit
/gittensory/shot?url=https://example.com, got back a genuine 20526-byte PNG — a realend-to-end render through the full self-host chain (app → self-host puppeteer stub → browserless
→ PNG bytes → served back through the app), not a mock
default path
npx tsc --noEmit -p .— nosrc/**changes in this PR, so no Codecov patch-coverage obligationgit diff --check,shellcheck scripts/smoke-selfhost.sh(clean)