fix: verify the downloaded browser can resolve its libraries - #8639
Open
RadiumGu wants to merge 1 commit into
Open
fix: verify the downloaded browser can resolve its libraries#8639RadiumGu wants to merge 1 commit into
RadiumGu wants to merge 1 commit into
Conversation
Playwright's host validation cannot be trusted to notice that a browser it just downloaded cannot launch, so `install-browser` reported success on a host missing 12 shared libraries, `browser_ok` reported true, the panel went green, and the failure only arrived at the user's first browse as an opaque stack trace. The cause is upstream and specific to linux-arm64: Playwright's registry declares the directory to scan as `chrome-linux`, while the arm64 build unpacks into `chrome-linux-arm64`. It scans a path that does not exist, finds no dependencies at all, concludes the host is fine and writes its DEPENDENCIES_VALIDATED marker -- which then suppresses re-validation for 30 days. MEASURED on Amazon Linux 2023 arm64: that marker was written 23 minutes BEFORE the libraries were installed. The same run on webkit, whose declared directory does match, threw and wrote no marker -- the two side by side are what identify the directory name as the cause. So this is a false negative, not a missing warning, and `host_deps_unsatisfied` -- which keys on Playwright's warning text -- can never fire for it. `missing_shared_libraries` therefore reads the real files: it enumerates the ELF objects actually on disk and asks ldd what cannot be resolved, never hardcoding a build's subdirectory name, since that assumption is the upstream bug. A successful download is followed by that probe, and a missing library becomes its own failed step rather than flipping the download's verdict -- the download genuinely succeeded, and labelling it failed would send the operator to re-fetch bytes already on disk when what they need is root and a package manager. Two guards keep the check from blocking installs that work. Libraries the build ships itself are excluded, both by passing the build's own directories as LD_LIBRARY_PATH and by name: without that, firefox reported libxul.so and nine others missing while launching perfectly. And ldd's `<path>:` header line is not a soname -- unfiltered it was reported as a missing library named after an absolute path. "Could not determine" is distinct from "nothing missing" throughout: off Linux, with no ldd, or on timeout the probe answers None and the install proceeds. An absent probe is not evidence of a broken browser. Verified on the failing host: chromium and firefox both report no missing libraries and add no step, while webkit -- whose dependencies are not in the rpm package list -- reports 26 and fails the step with the remedy.
Contributor
|
👋 Hi! This PR's description is missing some required sections from our PR template. Workflow runs won't be auto-approved until the description is updated. Missing sections:
Please update your PR description to include these sections, then push or re-save the description. The workflows will be approved on the next cycle. |
1 similar comment
Collaborator
|
👋 Hi! This PR's description is missing some required sections from our PR template. Workflow runs won't be auto-approved until the description is updated. Missing sections:
Please update your PR description to include these sections, then push or re-save the description. The workflows will be approved on the next cycle. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
install-browserreported success on a host missing 12 shared libraries.browser_okreported true, the settings panel went green, and the failure only arrived at the user's first browse:os_deps.host_deps_unsatisfiedexists to catch exactly this — it keys on Playwright's "Host system is missing dependencies to run browsers" text, because Playwright exits 0 and classifies it as a warning. It never fired.Root cause is upstream, and specific to linux-arm64
Playwright's registry declares the directory to scan:
But the arm64 build unpacks into
chrome-linux-arm64. SoexecutablesOrSharedLibraries()walks a path that does not exist,missingDepsstays empty,if (!missingDeps.size) return;takes the clean exit — and the caller then writes aDEPENDENCIES_VALIDATEDmarker that suppresses re-validation for 30 days.MEASURED on Amazon Linux 2023 arm64:
chromium-1243/DEPENDENCIES_VALIDATEDwrittenatk/mesa-libgbmactually installedThe marker was written 23 minutes before the libraries existed. The controlled comparison is
webkitin the same session on the same host: its declared directory does match its layout, so validation threw, printed the warning, and wrote no marker. Same Playwright, same host, opposite outcome — the directory name is the variable.This is a false negative, not a missing warning, so no amount of text matching can fix it.
Change
os_deps.missing_shared_libraries()reads the real files: enumerate the ELF objects actually present and asklddwhat cannot be resolved. It never hardcodes a build subdirectory — that assumption is the upstream bug.A successful download is followed by that probe, and a missing library becomes its own failed step (
verify-browser-libraries-<engine>) rather than flipping the download's verdict. The download genuinely succeeded; labelling it failed would send the operator to re-fetch bytes already on disk when what they need is root and a package manager. The step carries the missing sonames plus the existingmissing_deps_hint()remedy, and reportsreturncode: 0honestly because nothing exited non-zero.The engine's cache dirs are prefix-matched so
chromiumalso coverschromium_headless_shell-<rev>— headless is the default launch mode, so the shell is the binary a browse actually starts, and checking onlychromium-<rev>would clear a build that is not the one being run.Two guards against blocking installs that work
Found by running the probe on a real host rather than only against mocks — the first version would have been worse than the bug:
LD_LIBRARY_PATH(and a second exclusion by file name), firefox reportedlibxul.so,liblgpllibs.so,libmozsqlite3.soand 7 more as missing while launching perfectly. Playwright passes its own directory list for this same reason.ldd's header line.lddprefixes its report with<path>:, which is not a soname. Unfiltered it was reported as a missing library named after an absolute path.Unknown is not failure
Off Linux, with no
ldd, or on timeout the probe answersNoneand the install proceeds untouched. The download needs no privilege and may be perfectly good; turning "could not check" into "broken" would withdraw working browsers.Verification
On the host that exhibited the bug:
verify-browser-libraries-webkit, ok=falsewebkitis a genuine positive, not a synthetic one: its dependencies (libavif,libenchant-2,libflite*, …) are deliberately absent from_RPM_CHROMIUM_PACKAGES, which is Chromium-scoped by design. Cross-checked by launching it — it fails to load exactly those libraries.15 new tests. 328 passed / 3 skipped across
test_browser_cli_install.py,test_browser_cli_os_deps.py,test_browser_cli_launch.py,test_browser_cli_view.py,test_playwright_cli_installer.py,test_spawn_audit.py,test_no_stale_browser_mcp_refs.py.test_spawn_auditcorrectly flagged the newlddspawn; it is added toBENIGN_SPAWNSwith a justification. It is a fixed two-element argv over paths enumerated from Playwright's own cache (never agent-named, and a hostile file name cannot become a command because argv is a list), and it must not route throughsandboxed_spawn_argv: the probe answers whether this host can resolve the libraries, and a scrubbed environment would answer for the sandbox instead —LD_LIBRARY_PATHis the one input that has to survive to the child.Scope
detect()/browsers_present()stay presence-and-revision only. They are on the/api/statuspoll path, so adding anlddsweep there would put a subprocess fan-out behind every dashboard poll. The consequence is that a browser whose libraries went missing after install still reads green in the panel until the next install attempt; closing that needs a cached probe keyed on directory mtime, which is a separate change.Independent of #8637 (unwritable npm prefix) — different functions in
install.py, no overlap.