Refresh the CLI on upgrade, even when the release is current - #128
Merged
Merged
Conversation
We built a store with one-click install and Chromium auto-update -- crx.ts stamps our update_url into the manifest before signing, precisely so Chromium polls /api/store/updates.xml rather than Google. Then every listing was created by hand, which in practice meant almost none were: the store holds exactly one extension, and our own MarkSyncr is not in it. That is the gap worth closing. An extension in the store auto-updates for anyone who installed it. An extension outside the store reaches TronBrowser only by being rebuilt into the bundle, and reaches everyone else only when Google approves it. MarkSyncr has three releases sitting in Chrome review right now. So publishing runs from CI. scripts/store-publish.mjs resolves the listing by slug, creates it on first run, and posts the version with the manifest and the artifact URL. The reusable workflow wraps it so an extension repo adds six lines rather than its own half of the job. Two behaviours worth stating. A version that is already published exits clean instead of failing, so re-running a tag is safe. And the artifact is polled before submitting, because the store fetches it itself and a release asset can lag its tag by a few seconds -- submitting into that window fails for a reason that has nothing to do with the extension. Verified against the live store: an existing listing resolves by slug (coinpay-wallet), an unknown name takes the create path, and a manifest that is not MV3 is refused before anything is sent. Nothing publishes until TRONBROWSER_STORE_TOKEN exists. The API deliberately refuses to mint a publisher token from a token -- it has to come from a signed-in browser session -- so that one step is Anthony's and cannot be automated away. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Extensions load with --load-extension, and Chromium never auto-updates an extension loaded that way. That is why a bundled extension could only change when the whole browser was re-released: v3.15.0 shipped a MarkSyncr vault import bug that had already been fixed upstream, and there was nothing to do but wait for the next TronBrowser build. So the launcher updates them itself, against tronbrowser.dev/store, which has no review queue in front of it. The store already stamps its own update_url into each signed manifest for exactly this purpose; what was missing was anything on this side asking. The bundled copy is never written to -- it lives in the install tree, which may be read-only and is replaced wholesale on upgrade. Newer versions land in an overlay under the data dir, and an extension loads from the overlay only while it is genuinely newer. When a browser upgrade brings the bundle level with it, the overlay is deleted rather than left to shadow it forever. The check runs detached and applies on the NEXT launch. Blocking startup on the network to save one launch's staleness is a bad trade, and swapping an extension's files under a running Chromium is worse. Everything about it fails soft: no curl, no unzip, an unreachable store, an extension the store has never heard of, a download that is not the extension we asked for -- each leaves the bundled copy in place. A browser that will not start because a store was down would be a far worse bug than a stale extension. TRONBROWSER_NO_EXT_UPDATE=1 turns the whole thing off. Two things worth keeping in mind. Versions are compared numerically per field, because a string compare puts 1.2.10 below 1.2.9. And this script runs under `set -eu`, so _manifest_version returns 0 with no output for a missing file: the first version of it returned 1, which inside a command substitution would have taken the browser down on every launch where the overlay did not exist yet. The functional test caught that before it shipped. Verified against the live store under set -eu: a staged coinpay-wallet 0.0.1 is replaced by 0.10.1 and loads from the overlay, an extension the store does not have is left alone, and the run survives to the end. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The store auto-updates anything listed in it, and the launcher now pulls those updates, so getting an extension listed is the whole job. Doing that by hand is exactly why the store held one listing while we shipped five extensions. tron store login save a publisher token for this machine tron store whoami which publisher this machine is tron store list slugs currently in the store tron store publish --name ... --manifest ... --bundle-url ... Plain sh and curl on purpose: publishing has to work on a box with no Node and in a CI image holding nothing but this script. CI sets TRONBROWSER_STORE_TOKEN; a person runs login once and the token is saved 0600 under XDG_CONFIG_HOME. login is a paste rather than a flow we complete for them, because the API refuses to mint a publisher token from a token -- deliberately, so a leaked CI token cannot mint more. That one step stays human by design, and this is the shortest honest version of it. publish finds an existing listing by slug and creates one only when there is none, so re-running does not leave a second listing beside the first. It derives the slug the way the server does, and --slug overrides it for a listing whose slug was customised. A version that is already published exits clean, so re-running a tag is safe. install.sh writes this CLI from a quoted heredoc, and nothing in the build parses what is inside it. Extracted and `sh -n`'d, then run: help, list against the live store (returns coinpay-wallet), and publish with no token, which points at `tron store login` rather than failing with a 401 from curl. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`tron upgrade` gates everything on the release version, but the CLI does not come from the release: it is written from a heredoc in install.sh, which deploys from main on merge. Those are two different update paths, and only one of them was being checked. So a CLI-only change could never reach anyone. `tron store` was added, merged and deployed, and `tron upgrade` still answered "TronBrowser is already up to date (v3.16.1)" and left the old CLI in place -- the new command simply did not exist on the machine, and `tron store login` fell through to the handler that opens a URL, so it silently launched a browser instead. The only way through was TB_FORCE=1, which nobody would think to run for a command they had just been told exists. The up-to-date branch already refreshes the engine, the browser, Tor, certutil, the Moshpit root, Obscura and the macOS icon for exactly this reason: those are current-at-fetch-time, not current-at-release-time. The CLI belongs in that list and was missing from it. We are already executing the freshly fetched install.sh at that point, so rewriting it costs nothing and is always current. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Contributor
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. |
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.
Refresh the CLI on upgrade, even when the release is current
tron upgradegates everything on the release version, but the CLI does not comefrom the release: it is written from a heredoc in install.sh, which deploys from
main on merge. Those are two different update paths, and only one of them was
being checked.
So a CLI-only change could never reach anyone.
tron storewas added, merged anddeployed, and
tron upgradestill answered "TronBrowser is already up to date(v3.16.1)" and left the old CLI in place -- the new command simply did not exist
on the machine, and
tron store loginfell through to the handler that opens aURL, so it silently launched a browser instead. The only way through was
TB_FORCE=1, which nobody would think to run for a command they had just been
told exists.
The up-to-date branch already refreshes the engine, the browser, Tor, certutil,
the Moshpit root, Obscura and the macOS icon for exactly this reason: those are
current-at-fetch-time, not current-at-release-time. The CLI belongs in that list
and was missing from it. We are already executing the freshly fetched install.sh
at that point, so rewriting it costs nothing and is always current.
Co-Authored-By: Claude Opus 5 (1M context) noreply@anthropic.com