Repository navigation
fix(macos): make TronBrowser findable and launchable on a Mac - #140
Conversation
…g without it
On macOS TronBrowser ships no engine of its own — it drives a Chromium.app
found on disk — so failing to find one is not a degraded mode: `tron` exits
with "runs Ungoogled Chromium ONLY" and never opens a window.
The Homebrew cask does not leave the app where we looked. It MOVES
Chromium.app to brew's `appdir` and keeps only a symlink in the Caskroom, so
the four /Applications and ~/Applications paths we probed can all miss an app
brew installed perfectly well. Nothing looked in the Caskroom — the one path
that holds whatever `appdir` is, and the place the two architectures actually
differ (/opt/homebrew on Apple Silicon, /usr/local on Intel; we hardcoded the
brew *binary* at both prefixes but never the Caskroom).
Worse, the installer could not tell. ensure_browser() took `brew install
--cask` exiting 0 as success and returned without checking the app had landed
anywhere the launcher looks, so install.sh printed "Installed TronBrowser" and
exited 0 while the browser was absent. The failure then surfaced at first
launch as advice to install the thing the user had just installed. Re-running
the installer could not repair it either: `install --cask --force` is a no-op
while the install receipt is intact.
* launcher: probe the Caskroom after the app paths, resolving brew's prefix
from brew's own location so a relocated Homebrew works, with both defaults
as fallback (brew is usually not on PATH in a GUI launch). Newest version
wins; a dangling symlink reads as missing. A live Caskroom path is itself
proof of the ungoogled cask, so it no longer needs the `brew list` check —
which requires brew on PATH and keeps reporting the cask installed after
the .app is gone.
* install.sh: new mac_app_path() mirroring that search; ensure_browser()
verifies through it after every attempt, escalates install --force to
reinstall, falls back to the direct download, and says so loudly instead
of exiting 0 on a broken install. The trust marker is written only for
paths that prove the build is de-googled, so a bare
/Applications/Chromium.app still has to pass the brew check.
* tron-doctor: had no macOS awareness at all, so it reported "No Ungoogled
Chromium is installed" with a Flatpak hint on a perfectly healthy Mac. It
now lists the app bundles, reads versions from Info.plist (never by running
the binary — from a shell it SIGABRTs), names a cask whose app was deleted
with `brew reinstall` as the fix, and suggests brew rather than Flatpak.
Three more bugs found on the way:
* download_ungoogled_macos asked for `x86-64-macos.dmg`; upstream publishes
`x86_64-macos.dmg`. The no-Homebrew fallback could never work on Intel.
* brand_macos_icon only looked at Chromium.app, so icon branding silently
no-opped for the direct-download path, which installs "Ungoogled
Chromium.app".
* the release JSON was fetched to a fixed /tmp path; use mktemp. Copy the
.app with ditto rather than cp -R, the supported way to move a bundle
without disturbing the signature arm64 code must carry.
TRONBROWSER_MAC_SEARCH_ROOT aims the machine-wide half of the search at a
fixture so the new tests run the same on any host. It grants nothing
TRONBROWSER_BROWSER does not already grant, and is unset in normal use.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
ThreatCrush Security Scan52 finding(s) HIGH/CRITICAL: 3 | MEDIUM: 28 | LOW: 21
…and 2 more. Full results in the Security tab. Snippets are redacted; ThreatCrush never prints matched credential material. |
Second fix on this branch: there was no app
Because it was never there. The one macOS-facing icon code we had,
|
The bug
On macOS TronBrowser ships no engine of its own — it drives a
Chromium.appfound on disk — so failing to find one is not a degraded mode:tronexits with "runs Ungoogled Chromium ONLY" and never opens a window.The Homebrew cask does not leave the app where we looked. It moves
Chromium.appto brew'sappdirand keeps only a symlink in the Caskroom, so the four/Applicationsand~/Applicationspaths we probed can all miss an app brew installed perfectly well. Nothing looked in the Caskroom — the one path that holds whateverappdiris, and the place the two architectures actually differ (/opt/homebrewon Apple Silicon,/usr/localon Intel; we hardcoded the brew binary at both prefixes but never the Caskroom).Worse, the installer could not tell.
ensure_browser()tookbrew install --caskexiting 0 as success and returned without checking the app had landed anywhere the launcher looks — soinstall.shprinted "Installed TronBrowser" and exited 0 with no browser present. The failure then surfaced at first launch as advice to install the thing the user had just installed. Re-running the installer could not repair it either:install --cask --forceis a no-op while the install receipt is intact.Reproduced
Found on an Apple Silicon Mac (M-series, macOS 26.6) in exactly this state:
brew list --cask ungoogled-chromium…/Caskroom/…/Chromium.app/Applications/Chromium.app/Applications/Chromium.appmdfind -name Chromium.appbrew reinstall --cask ungoogled-chromiumrestores it;install --forcedoes not.The fix
PATHin a GUI launch). Newest version wins; a dangling symlink reads as missing. A live Caskroom path is itself proof of the ungoogled cask, so it no longer needs thebrew listcheck — which requires brew onPATHand keeps reporting the cask installed after the.appis gone.mac_app_path()mirroring that search;ensure_browser()verifies through it after every attempt, escalatesinstall --forcetoreinstall, falls back to the direct download, and says so loudly instead of exiting 0 on a broken install. The trust marker is written only for paths that prove the build is de-googled, so a bare/Applications/Chromium.appstill has to pass the brew check.Darwin,Applications,breworCaskroomanywhere), so it reported "No Ungoogled Chromium is installed" with a Flatpak hint on a perfectly healthy Mac. It now lists the app bundles, reads versions fromInfo.plist(never by running the binary — from a shell it SIGABRTs, which is why the launcher usesopen -na), names a cask whose app was deleted withbrew reinstallas the fix, and suggests brew rather than Flatpak.Three more bugs found on the way
download_ungoogled_macosasked forx86-64-macos.dmg; upstream publishesx86_64-macos.dmg. Verified against the GitHub API: the no-Homebrew fallback could never work on Intel.brand_macos_icononly looked atChromium.app, so icon branding silently no-opped for the direct-download path, which installsUngoogled Chromium.app./tmppath; nowmktemp. The.appis copied withdittorather thancp -R— the supported way to move a bundle without disturbing the signature arm64 code must carry. (Hardening: I could not provecp -Rwas damaging signatures here.)Tests
7 new tests across
launcher.test.tsanddoctor.test.ts: Caskroom discovery through brew's prefix, newest-version selection, cask-path-as-proof, the dangling-symlink state, thebrew reinstallhint,Info.plistversion reading, and brew-not-Flatpak advice. 6 of the 7 fail against the unpatched scripts; the dangling-symlink case is a regression guard that holds either way.TRONBROWSER_MAC_SEARCH_ROOTaims the machine-wide half of the search at a fixture so they run the same on any host, with or without its own/Applications/Chromium.app. It grants nothingTRONBROWSER_BROWSERdoes not already grant, and is unset in normal use.Full desktop suite: 81 passed.
eslintclean on both changed test files (the 15 pre-existing errors inwindows-pit-browser.mjset al. are unchanged frommain).🤖 Generated with Claude Code