feat: add a playwright feature - #11
Conversation
Installs the Playwright CLI globally and downloads a Chromium build, so containers do not pay for an npm install and a browser download on every start. Browsers land in /usr/local/share/ms-playwright rather than Playwright's default "$HOME/.cache", because sandboxes may replace HOME with a per-pod volume: anything baked into the image's home directory is invisible there. PLAYWRIGHT_BROWSERS_PATH is set through containerEnv so the install and every later run agree on that location. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MB7zNmdzPYMpwkUWxAjSFt
| "id": "playwright", | ||
| "installsAfter": [ | ||
| "ghcr.io/devcontainers/features/common-utils", | ||
| "ghcr.io/devcontainers/features/node" |
This comment was marked as outdated.
This comment was marked as outdated.
Sorry, something went wrong.
There was a problem hiding this comment.
Declining dependsOn — it would break the consumer this feature exists for, so I documented the requirement instead.
The spec deduplicates Features only when the id and the options match exactly ("two Features are identical if their manifest digests are equal, and the options executed against the Feature are equal"). orianna-ai/devcontainer already selects ghcr.io/devcontainers/features/node:2 with {"version": "24.18.0"}. A dependsOn entry has to name some tag and some options, and whatever I pick will not match that, so both copies install and whichever runs last wins the nvm default alias — silently unpinning a node version renovate manages. Hardcoding node:2 + 24.18.0 into a shared feature to dodge that is worse.
The other suggestion, having the installer provision node itself, reimplements the node feature inside this one and hits the same collision.
So: NOTES.md now gives the README a Requirements section showing node selected alongside playwright, and the installer keeps failing fast with npm was not found; this feature has to install after the node feature. The documented setup is the two-feature one; the single-feature example above it is generated from the manifest and I cannot edit it.
Generated by Claude Code
|
|
||
| # install-deps installs Chromium's shared libraries with apt, and an earlier feature may already | ||
| # have dropped the package lists. | ||
| apt-get update -y |
This comment was marked as outdated.
This comment was marked as outdated.
Sorry, something went wrong.
There was a problem hiding this comment.
Took the second option — an explicit supported-distribution check. Playwright only knows how to install Chromium's shared libraries with apt, so there is no Fedora/RHEL branch to implement; the feature is inherently Debian and Ubuntu only.
install.sh now checks for apt-get before the npm install, so a non-Debian build stops in a second with apt-get was not found; this feature only supports debian and ubuntu base images instead of after a package download. NOTES.md documents the constraint and it shows up in the generated README.
Not adding the non-Ubuntu scenario: devcontainer features test scenarios assert that a build succeeds and the checks pass, so there is no way to express "this build should fail" — the scenario would just be a red job.
Generated by Claude Code
The installer only ever installed Chromium's shared libraries with apt, so it died mid-install on a non-Debian base with "apt-get: command not found". Check for apt-get up front instead, before the npm install, and say what the feature supports. Node stays in installsAfter rather than dependsOn: the spec only deduplicates Features whose id and options match exactly, so a dependsOn entry would install a second, separately versioned copy of the node feature for anyone who already pins one. NOTES.md documents the requirement instead, so the generated README shows node alongside playwright. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MB7zNmdzPYMpwkUWxAjSFt
Drops the comments that restate the code, the blank lines before exit, and the name and description that ran long next to buildbuddy and cloud-sql-proxy. Drops the version proposals too, so the option looks like cloud-sql-proxy's. The test loses its local path variable for the same reason. What stays is the three things a reader cannot get from the code: why browsers go to a system-wide path, why the global module tree is chowned back, and why playwright-core is located rather than invoked by name. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MB7zNmdzPYMpwkUWxAjSFt
|
Generated by Claude Code |
|
Generated by Claude Code |
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MB7zNmdzPYMpwkUWxAjSFt
Says what the feature actually installs, which was missing: chromium only, no firefox or webkit. Notes the ownership the install leaves behind, now that install.sh no longer explains it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MB7zNmdzPYMpwkUWxAjSFt
Drops the ownership paragraph and the installsAfter-versus-dependsOn rationale, which explained our implementation rather than telling anyone what to do. The reader keeps the four things they cannot get from the manifest: node is required, debian and ubuntu only, chromium only, and where the browsers live. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MB7zNmdzPYMpwkUWxAjSFt
The generated Example Usage block shows this feature on its own and cannot be edited, so a reader copying it hit the npm error with nothing warning them first. The description renders above that block, so put it there. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MB7zNmdzPYMpwkUWxAjSFt
Softlight Overview
UX Score: 5/5
Softlight reviewed this change and found no issues.
To run Softlight again, click here or comment @softlight.
Adds a
playwrightfeature that installs the Playwright CLI globally and downloads a Chromium build at image-build time, so containers do not pay for an npm install and a browser download on every start.This is the feature from orianna-ai/devcontainer#82, moved here so it is published and shared rather than living locally under that repo's
.devcontainer/features.Changes
src/playwright/devcontainer-feature.json— setsPLAYWRIGHT_BROWSERS_PATHviacontainerEnv, declaresinstallsAfteroncommon-utilsandnode, and exposes aversionoption for the@playwright/clinpm package.src/playwright/install.sh— sources nvm whennpmis not already onPATH, installs@playwright/cli, then runsinstall-deps chromiumandinstall chromiumthrough theplaywright-coreCLI (@playwright/clinever links that binary). Restores the pre-existing ownership of the global module tree afterwards, so the node feature's remote user can keep installing global packages at run time.src/playwright/README.md— generated withdevcontainer features generate-docs.test/playwright/— a scenario onbase:ubuntu-22.04plus the node feature, checking thatplaywright-cliis onPATH, thatPLAYWRIGHT_BROWSERS_PATHis exported, that a Chromium build landed there, and that the path is still readable and traversable as the remote user.Notes
Browsers land in
/usr/local/share/ms-playwrightrather than Playwright's default$HOME/.cache, because a sandbox may pointHOMEat a per-pod volume — anything baked into the image's home directory is invisible there.PLAYWRIGHT_BROWSERS_PATHis declared indevcontainer-feature.jsonso it is baked into the image environment, which points both the install and every later run at the same directory.The
versionoption defaults tolatesthere, matchingcloud-sql-proxy; the pin to0.1.17from the original PR moves to the call site in the devcontainer repo.Testing
pre-commit run --all-filespasses, and the manifest validates against the devcontainer feature schema. The scenario test needs Docker, so it runs in CI rather than locally.Generated by Claude Code
Note
Low Risk
New optional devcontainer feature and install script only; no changes to existing runtime services or auth/data paths.
Overview
Adds a new
playwrightdevcontainer feature that bakes @playwright/cli and a Chromium build into the image at build time, avoiding npm install and browser download on every container start.The feature sets
PLAYWRIGHT_BROWSERS_PATHto/usr/local/share/ms-playwright(HOME-independent for sandboxes with per-podHOME), depends on node and common-utils, and exposes aversionoption for the CLI package.install.shinstalls the global CLI, runs Chromiuminstall-deps/installviaplaywright-core, restores global npm tree ownership for the remote user, and makes the browsers directory world-readable.Documentation and an Ubuntu 22.04 scenario test assert
playwright-clionPATH, the env var, Chromium under the shared path, and remote-user read/execute access.Reviewed by Cursor Bugbot for commit 02a87d8. Configure here.
Greptile Summary
Adds a Playwright devcontainer feature that installs the global CLI, Chromium, and its Debian/Ubuntu system dependencies during image construction.
@playwright/cliversion and shared browser path.Confidence Score: 4/5
The PR is not yet safe to merge because its primary standalone example still causes image builds without Node to fail.
The reply says the missing Node dependency was addressed through documentation, but the README’s primary Example Usage still omits Node, while the installer exits whenever npm is unavailable; users copying that example therefore encounter the same previously reported failure.
Files Needing Attention: src/playwright/README.md
Important Files Changed
Reviews (2): Last reviewed commit: "style: trim the playwright feature to th..." | Re-trigger Greptile