fix(updater): never open a raw .app.zip from the Download button (auto-update S3)#23
Merged
Merged
Conversation
…o-update S3) The notify-only updater's Download action was `feed.url || product.downloadUrl || base`. That was correct while `feed.url` was a release page — but S2 made it the arch-matched LevelCode-<arch>.app.zip, an artifact for Squirrel to INSTALL. Left alone, the button would start a raw zip download instead of showing a page. Extracted the choice into a pure `U.downloadUrl(feed, product, base)`: `product.downloadUrl` → `feed.releaseNotesUrl` → base. `feed.url` is dropped from the human path entirely, so no future feed change can put a zip behind it. Sequencing correction (the reason this is worth shipping now, not with S4): the notify-only client is served a 200 REGARDLESS of LEVELCODE_UPDATE_FEED_SIGNED — `unless notify_only_client? || (signed_feed? && rel[:installable])`. So this broke on the next release carrying an S1 zip, not on flipping the flag. Nothing in the code would have caught that, unlike the flag-ordering constraint which the controller now enforces via `installable`. AUTO-UPDATE.md said S3 merely preceded S4; it now records both constraints and marks S1-S3 done. Verified: 9 unit tests pass (2 new), and restoring the old preference order fails the new test with actual=…/LevelCode-arm64.app.zip vs expected=levelcode.ai/download. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Updates the notify-only updater extension so the Download action never opens a raw LevelCode-<arch>.app.zip (now used for Squirrel install artifacts), and instead always routes humans to a page-style URL.
Changes:
- Added
U.downloadUrl(feed, product, base)helper to explicitly prefer human-facing URLs (product.downloadUrl→feed.releaseNotesUrl→base) and excludefeed.url. - Updated the extension’s Download action to use the helper rather than
feed.url. - Added regression/unit tests and updated
docs/AUTO-UPDATE.mdto capture the sequencing risk and completed slices.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| extensions/levelcode-updater/update.js | Introduces downloadUrl() helper that avoids feed.url and encodes the intended precedence for human-facing links. |
| extensions/levelcode-updater/extension.js | Switches the “Download” notification action to call U.downloadUrl(...). |
| extensions/levelcode-updater/test/update.test.js | Adds regression tests ensuring downloadUrl() never returns feed.url and degrades safely with null inputs. |
| docs/AUTO-UPDATE.md | Documents the new behavior, marks S1–S3 done, and clarifies sequencing constraints/risk. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
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.
What
The notify-only updater's Download action was
feed.url || product.downloadUrl || base. That was right whilefeed.urlwas a release page — but S2 (thin.ly #371) made it the arch-matchedLevelCode-<arch>.app.zip, an artifact for Squirrel to install, not a page to open. Left alone, the button starts a raw zip download.Now a pure helper,
U.downloadUrl(feed, product, base):feed.urlis dropped from the human path entirely, so no future feed change can put a zip behind that button.Why now, and not with S4
This gated the next release, not the flag flip. The notify-only client is served a 200 regardless of
LEVELCODE_UPDATE_FEED_SIGNED:So it would have broken on the first release carrying an S1 zip, with the flag still off. That is a different constraint from the documented "don't flip the flag early" one — and unlike that one, nothing in the code would have caught it (the controller enforces flag-ordering via
installable; there is no equivalent guard on the notify path).docs/AUTO-UPDATE.mdrecorded S3 as merely preceding S4; it now records both constraints and marks S1–S3 done.Verification
actual: …/LevelCode-arm64.app.zipvsexpected: https://levelcode.ai/download.node --checkclean on both files.Next
S4 — flip
LEVELCODE_UPDATE_FEED_SIGNED=1, then the real install-N → publish-N+1 test. Note S4 is inert until a release actually carries the S1 zips: everything published so far resolves toinstallable: false, so Squirrel still gets 204.🤖 Generated with Claude Code