Skip to content

feat(selfhost): add optional browserless/chromium service for visual review - #3633

Merged
loopover-orb[bot] merged 1 commit into
mainfrom
feat/selfhost-browserless-visual-review
Jul 5, 2026
Merged

feat(selfhost): add optional browserless/chromium service for visual review#3633
loopover-orb[bot] merged 1 commit into
mainfrom
feat/selfhost-browserless-visual-review

Conversation

@JSONbored

Copy link
Copy Markdown
Owner

Closes #3608. Part of #3607.

Summary

  • Adds a --profile visual-review docker-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 can
    enable automated before/after PR screenshot capture without standing up their own browser-rendering
    sidecar.
  • The code-level support already existed and was already merged (BROWSER_WS_ENDPOINTenv.BROWSER
    injection in src/server.ts, the puppeteer-core self-host stub in src/selfhost/stubs/puppeteer.ts,
    Docker image + docker-compose (one-command self-host) #980) — nothing was actually deployed anywhere until this PR.
  • Documents 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 like
    Qdrant are already scoped out of that focused starter file).
  • Extends scripts/smoke-selfhost.sh with an opt-in SELFHOST_SMOKE_VISUAL_REVIEW=1 mode that boots a
    real browserless container alongside the app and asserts /gittensory/shot returns an actual rendered
    PNG, not just that the app stays healthy.
  • Fixed a stale Dockerfile comment referencing the deprecated browserless/chrome image name.

A real bug caught during validation

My first draft used ${BROWSERLESS_TOKEN:?...} (hard-required). docker compose config interpolates
every service's env vars for the whole file regardless of which profile is active, so that would have
broken docker compose up for every self-host operator NOT using visual review, profile or not. Fixed to
a soft ${BROWSERLESS_TOKEN:-} default before this shipped — verified both paths explicitly (see test plan).

Test plan

  • docker compose config --quiet validates cleanly both with and without --profile visual-review,
    and without BROWSERLESS_TOKEN set (confirms the fix above actually holds)
  • Built the image locally with --build-arg INSTALL_VISUAL_REVIEW=true; confirmed puppeteer-core
    is actually present in the resulting image
  • Ran the new smoke-test mode for real: booted a real browserless/chromium container + the app,
    hit /gittensory/shot?url=https://example.com, got back a genuine 20526-byte PNG — a real
    end-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
  • Ran the baseline (non-visual-review) smoke test afterward to confirm zero regression on the
    default path
  • npx tsc --noEmit -p . — no src/** changes in this PR, so no Codecov patch-coverage obligation
  • git diff --check, shellcheck scripts/smoke-selfhost.sh (clean)

…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-security

Copy link
Copy Markdown
Contributor

Superagent didn't find any vulnerabilities or security issues in this PR.

@loopover-orb loopover-orb Bot added the gittensor:feature Gittensor-scored feature linked to a feature issue — scores a 0.25x multiplier. label Jul 5, 2026
@loopover-orb

loopover-orb Bot commented Jul 5, 2026

Copy link
Copy Markdown
Contributor

Tip

🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩

✅ Gittensory review result - approve/merge recommended

Review updated: 2026-07-05 20:17:04 UTC

4 files · 1 AI reviewer · no blockers · readiness 82/100 · CI green · clean

✅ Suggested Action - Approve/Merge

  • safe to merge

Review summary
This PR wires up an optional `--profile visual-review` browserless/chromium sidecar in docker-compose plus a matching smoke-test path, building on already-merged code support. The compose default for `TOKEN` correctly uses a soft `${BROWSERLESS_TOKEN:-}` (avoiding the config-interpolation footgun the author describes hitting), the service is internal-network-only via `expose`, resource-bounded, and marked `required: false` so it doesn't break default `docker compose up`. The smoke-test extension actually boots the container and asserts a real PNG comes back from `/gittensory/shot`, which is a meaningfully stronger check than just app-health.

Nits — 7 non-blocking
  • scripts/smoke-selfhost.sh: the browserless readiness probe and the compose healthcheck both assume `curl` is present inside the `ghcr.io/browserless/chromium` image — worth confirming that image ships curl, since if it doesn't both checks silently loop to timeout/failure rather than giving a clear 'curl not found' error.
  • docker-compose.yml: the browserless `depends_on: condition: service_healthy, required: false` on the app service is a nice touch for start ordering, but consider a short comment noting the app's own screenshot capture is fail-safe regardless (already documented on the service block, just not on the depends_on itself).
  • .env.example: BROWSERLESS_CONCURRENT/QUEUED/TIMEOUT_MS/MEM_LIMIT are documented as commented-out with inline defaults shown — consistent with the rest of the file's style, no issue, just confirm the values match the compose defaults exactly (2/4/30000/2g) since a drift here would only surface as confusing docs.
  • scripts/smoke-selfhost.sh: SHOT_BYTES uses a hardcoded 1024-byte floor as a 'not a placeholder' heuristic — reasonable but somewhat arbitrary; a comment already justifies it, so this is just a minor readability nit.
  • Consider adding a one-line troubleshooting note in docker-compose.yml or .env.example for the case where the browserless healthcheck fails due to a missing curl binary in future image tags, since the image is pulled as `:latest` and could change.
  • PR author also opened the linked issue — Link an issue that was opened by a different contributor, or provide a rationale for why this self-authored issue represents genuine discovery work.
  • Diff looks like trivial or whitespace-only churn — Reduce whitespace-only or formatting-only churn and keep the diff focused on substantive changes.
Signal Result Evidence
Code review ✅ No blockers 1 reviewer
Linked issue ✅ Linked #3608
Related work ⚠️ 3 scoped overlaps Top overlaps are listed below; lower-confidence bulk is hidden.
Change scope ❌ 8/20 High review scope from cached public metadata (1 linked issue).
Validation posture ✅ 25/25 PR body includes validation/test evidence.
Contributor workload ✅ 10/10 Author activity: 56 registered-repo PR(s), 46 merged, 493 issue(s).
Contributor context ✅ Confirmed Gittensor contributor JSONbored; Gittensor profile; 56 PR(s), 493 issue(s).
Gate result ✅ Passing No configured blocker found.
Review context
  • Author: JSONbored
  • Role context: owner (maintainer lane)
  • Public audience mode: oss maintainer
  • Lane context: Repository registration is not available in the local Gittensory cache.
  • Public profile languages: Python, TypeScript, JavaScript, Ruby, Go, Kotlin, MDX, Shell
  • Official Gittensor activity: 56 PR(s), 493 issue(s).
  • Related work: Titles/paths share 5 meaningful terms. (issue #2189, issue #1681)
  • Related work: Titles/paths share 5 meaningful terms. (issue #1680, issue #1681)
  • Related work: Titles/paths share 5 meaningful terms. (issue #1681, issue #1683)
Contributor next steps
  • Treat this as maintainer-lane context rather than normal contributor-lane activity.
  • Review top overlaps.
  • Add a concise scope and risk note.
  • No action.
  • Check active issues and PRs before submitting.
Signal definitions
  • Related work = same linked issue, overlapping active PRs, or title/path similarity.
  • Change scope = cached public metadata such as size labels, draft state, and review-burden hints.
  • Validation posture = whether the PR provides enough public validation/test evidence for maintainer review.
  • Contributor workload = public contributor activity and cleanup pressure, not a repo-wide quality failure.
  • Contributor context = public GitHub/Gittensor identity context; non-Gittensor status is not a blocker.

🟩 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.

  • Re-run Gittensory review

@codecov

codecov Bot commented Jul 5, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 93.20%. Comparing base (c0c97d5) to head (dbecbd7).
⚠️ Report is 3 commits behind head on main.
✅ All tests successful. No failed tests found.

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:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@loopover-orb loopover-orb Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Gittensory approves — the gate is satisfied and CI is green.

@loopover-orb
loopover-orb Bot merged commit 5bc2711 into main Jul 5, 2026
11 checks passed
@loopover-orb
loopover-orb Bot deleted the feat/selfhost-browserless-visual-review branch July 5, 2026 20:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

gittensor:feature Gittensor-scored feature linked to a feature issue — scores a 0.25x multiplier.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Deploy self-host browser-rendering infrastructure (browserless/chrome + BROWSER_WS_ENDPOINT)

1 participant