Skip to content

Commit 10c3759

Browse files
committed
Puppeteer/Playwright uninstall: resolve path via load() then rmtree
``puppeteer-browsers clear`` wipes the whole cache (no per-browser filter) and ``playwright uninstall`` rejects browser-name arguments, so neither CLI can do the per-browser uninstall abxpkg exposes. Switch both uninstall handlers to: 1. ``self.load(bin_name, no_cache=True)`` — playwright-core's ``executablePath()`` / puppeteer-browsers' ``list`` already read the right path from the subprocess env, so load() handles all three cases uniformly: - managed ``install_root`` → abxpkg exports the env var itself - unmanaged default → CLI's own OS default - custom ``PUPPETEER_CACHE_DIR`` / ``PLAYWRIGHT_BROWSERS_PATH`` → ambient env passes through via ``build_exec_env`` 2. Walk up from the resolved abspath to find the browser's top-level dir (``<cache>/<browser_name>/`` for puppeteer, ``<cache>/<browser_name>-<buildId>/`` for playwright) and rmtree it. No more ``self.cache_dir`` lookups in either uninstall handler, and the existing ``bin_dir`` symlink cleanup still runs first so ``load`` stops resolving a stale shim if rmtree fails partway through.
1 parent 20b1d6f commit 10c3759

2 files changed

Lines changed: 45 additions & 21 deletions

File tree

abxpkg/binprovider_playwright.py

Lines changed: 23 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -739,23 +739,32 @@ def default_uninstall_handler(
739739
install_args: InstallArgs | None = None,
740740
**context,
741741
) -> bool:
742-
# Drop the symlink first (if we're managing one) so ``load()``
743-
# stops seeing the tool even if browser dir removal partially
744-
# fails.
742+
# Drop the managed shim first so ``load()`` stops returning the
743+
# symlink even if the browser-dir rmtree partially fails.
745744
if self.bin_dir is not None:
746745
(self.bin_dir / bin_name).unlink(missing_ok=True)
747746

748-
# ``playwright uninstall`` only removes *unused* browsers from
749-
# the entire host, so drop the matching directories ourselves
750-
# from the resolved ``cache_dir`` (= ``PLAYWRIGHT_BROWSERS_PATH``).
751-
# Only touch it if the caller pinned one explicitly or via
752-
# ``install_root`` — we don't delete from playwright's own
753-
# OS-default cache.
754-
if self.cache_dir is not None and self.cache_dir.is_dir():
755-
for entry in self.cache_dir.iterdir():
756-
if entry.is_dir() and entry.name.startswith(f"{bin_name}-"):
757-
logger.info("$ %s", format_command(["rm", "-rf", str(entry)]))
758-
shutil.rmtree(entry, ignore_errors=True)
747+
# Use ``load()`` to resolve the actual installed browser
748+
# executable — ``playwright-core``'s ``executablePath()`` reads
749+
# ``PLAYWRIGHT_BROWSERS_PATH`` from the subprocess env, which
750+
# the provider exports when ``install_root`` is set and which
751+
# otherwise passes through from the ambient env. This single
752+
# call covers managed, OS-default, and user-env-var modes.
753+
# Then walk up from that abspath to find the
754+
# ``<bin_name>-<buildId>/`` dir and rmtree it — playwright's
755+
# own ``uninstall`` CLI has no per-browser argument, so this
756+
# is still the only way to remove a specific browser.
757+
try:
758+
loaded = self.load(bin_name, quiet=True, no_cache=True)
759+
except Exception:
760+
loaded = None
761+
loaded_abspath = loaded.loaded_abspath if loaded else None
762+
if loaded_abspath is not None:
763+
for parent in Path(loaded_abspath).resolve().parents:
764+
if parent.name.startswith(f"{bin_name}-"):
765+
logger.info("$ %s", format_command(["rm", "-rf", str(parent)]))
766+
shutil.rmtree(parent, ignore_errors=True)
767+
break
759768
return True
760769

761770

abxpkg/binprovider_puppeteer.py

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -699,16 +699,31 @@ def default_uninstall_handler(
699699
install_args: InstallArgs | None = None,
700700
**context,
701701
) -> bool:
702-
install_args = list(install_args or self.get_install_args(bin_name))
703-
browser_name = self._browser_name(bin_name, install_args)
702+
# Drop the managed shim first so ``load()`` stops returning the
703+
# symlink even if the browser-dir rmtree partially fails.
704704
if self.bin_dir is not None:
705705
bin_path = self.bin_dir / bin_name
706706
if bin_path.exists() or bin_path.is_symlink():
707707
logger.info("$ %s", format_command(["rm", "-f", str(bin_path)]))
708708
bin_path.unlink(missing_ok=True)
709-
if self.cache_dir is not None:
710-
browser_dir = self.cache_dir / browser_name
711-
if browser_dir.exists():
712-
logger.info("$ %s", format_command(["rm", "-rf", str(browser_dir)]))
713-
shutil.rmtree(browser_dir, ignore_errors=True)
709+
710+
# Use ``load()`` to resolve the actual installed browser
711+
# executable — load honours managed install_root, the ambient
712+
# PUPPETEER_CACHE_DIR env var, and puppeteer-browsers' own
713+
# default, so this single call handles all three cases. Then
714+
# walk up from that abspath to find the browser's top-level
715+
# directory (``<cache>/<browser_name>/``) and rmtree it.
716+
install_args = list(install_args or self.get_install_args(bin_name))
717+
browser_name = self._browser_name(bin_name, install_args)
718+
try:
719+
loaded = self.load(bin_name, quiet=True, no_cache=True)
720+
except Exception:
721+
loaded = None
722+
loaded_abspath = loaded.loaded_abspath if loaded else None
723+
if loaded_abspath is not None:
724+
for parent in Path(loaded_abspath).resolve().parents:
725+
if parent.name == browser_name:
726+
logger.info("$ %s", format_command(["rm", "-rf", str(parent)]))
727+
shutil.rmtree(parent, ignore_errors=True)
728+
break
714729
return True

0 commit comments

Comments
 (0)