ship: bump BINARY_VERSION to firefox-9 + refuse the broken firefox-8 (audit B5)

firefox-9 is published (build 5/5, drive-gate 5/5, full e2e 137/1-skip, fppro
ALL CRITICAL CLEAN, WebRTC real, sha256-verified download path). Point the
wrapper at it.

Guard: ensure_binary() now refuses any version in BROKEN_VERSIONS (={firefox-8})
with a clear error instead of handing over an undrivable binary — a cached
firefox-8 from before this bump would otherwise keep being used silently. Tests:
the current BINARY_VERSION can never be in BROKEN_VERSIONS; firefox-8 stays
flagged; ensure_binary("firefox-8") raises.
This commit is contained in:
feder-cr 2026-06-09 16:02:52 +02:00
parent 62cdf626a0
commit 0b53e18e23
4 changed files with 38 additions and 1 deletions

View file

@ -7,7 +7,14 @@ bugfixes don't force a multi-hour Firefox rebuild.
from __future__ import annotations
# Bump this when a new patched Firefox build is released on GitHub.
BINARY_VERSION: str = "firefox-8"
BINARY_VERSION: str = "firefox-9"
# Releases known to be broken — ensure_binary() refuses them with a clear error
# instead of handing the user an unusable binary. firefox-8 was packaged without
# the juggler automation layer, so Playwright cannot drive it (TargetClosedError);
# fixed in firefox-9 (package-manifest.in now ships chrome/juggler). A cached
# firefox-8 from before the bump would otherwise keep being used silently.
BROKEN_VERSIONS: frozenset[str] = frozenset({"firefox-8"})
# Underlying Firefox version (for display only; does not drive downloads).
FIREFOX_UPSTREAM_VERSION: str = "150.0.1"

View file

@ -21,6 +21,7 @@ from .constants import (
ARCHIVE_NAME,
BINARY_ENTRY_REL,
BINARY_VERSION,
BROKEN_VERSIONS,
GEOIP_ASSET,
GEOIP_MMDB_NAME,
GEOIP_MMDB_VERSION,
@ -148,6 +149,12 @@ def _post_extract_darwin(app_root: Path, entry: Path) -> None:
def ensure_binary(version: str = BINARY_VERSION) -> Path:
"""Return a path to a runnable Firefox executable. Download if needed."""
if version in BROKEN_VERSIONS:
raise RuntimeError(
f"{version} is a known-broken release (the juggler automation layer is "
f"missing, so Playwright cannot drive it). Upgrade invisible_playwright "
f"(current BINARY_VERSION={BINARY_VERSION}) or pass a newer version."
)
plat = sys.platform
mach = platform.machine()
asset = ARCHIVE_NAME(plat, mach)

View file

@ -5,11 +5,26 @@ from invisible_playwright.constants import (
BINARY_BASENAME,
BINARY_ENTRY_REL,
BINARY_VERSION,
BROKEN_VERSIONS,
FIREFOX_UPSTREAM_VERSION,
RELEASE_URL_TEMPLATE,
)
@pytest.mark.unit
def test_broken_versions_excludes_current():
"""The current BINARY_VERSION must NEVER be in BROKEN_VERSIONS — otherwise
every default ensure_binary() call would raise and the wrapper is unusable."""
assert BINARY_VERSION not in BROKEN_VERSIONS
@pytest.mark.unit
def test_firefox_8_is_marked_broken():
"""firefox-8 shipped without the juggler layer (undrivable by Playwright);
it must stay flagged so a stale cache can't silently hand it to a user."""
assert "firefox-8" in BROKEN_VERSIONS
@pytest.mark.unit
def test_binary_version_format():
assert BINARY_VERSION.startswith("firefox-")

View file

@ -832,3 +832,11 @@ def test_parse_owner_repo_handles_repos_with_dashes_and_underscores():
)
assert owner == "my-org"
assert repo == "my_cool.repo"
@pytest.mark.unit
def test_ensure_binary_refuses_known_broken_version():
"""A known-broken release (firefox-8, no juggler) must be refused with a
clear error BEFORE any download never silently handed to the user."""
with pytest.raises(RuntimeError, match="known-broken"):
ensure_binary("firefox-8")