fix(update): stop offering an update that a package-managed install cannot apply - #573
Merged
Merged
Conversation
…annot apply A .deb, .rpm or snap install is told a new version exists and then fails to install it. The check half succeeds -- latest.json publishes `linux-x86_64` and every Linux build asks for exactly that key -- and the install half renames the downloaded file over the running binary, which needs root for /usr/bin/Markpad and is impossible on a snap's read-only squashfs. The updater would normally pick install_deb or install_rpm instead, from a bundle type patched into the binary after the build. That patch fails on Linux -- three `Failed to add bundler type to the binary` warnings in every release build, one per bundle -- so bundle_type() is None and all three formats fall through to the AppImage path, where install_deb and install_rpm are unreachable code. build.yml already documents the same missing symbol for its other consequence, the shape of latest.json's platform keys; this is the consequence nobody had followed through to. So the question is asked before the check rather than discovered after it. `self_update_supported` reads tauri's Env rather than APPIMAGE directly, because tauri also verifies the executable sits under $TMPDIR/.mount_ -- the variable alone cannot make a package-managed install claim it is updatable. Windows and macOS are untouched: Windows runs the downloaded NSIS installer instead of overwriting in place, and macOS is the one platform where bundle_type() answers without the patch. The two new strings are added to the four languages that translate this dialog, not to all 26. The update dialog exists in en, zh-CN, zh-TW and ko only; the other 22 fall back to English for all 24 of its strings, so a wider set would put two localised lines in an otherwise English dialog. A test holds the two sets equal, in both directions. Verified: cargo check clean, 965 tests pass, svelte-check clean. The ordering assertion was mutation-checked -- moving the gate after check() fails it and nothing else. Not verifiable here: the Linux branch does not compile on macOS (cfg'd out) and the phase is unreachable on this platform by design, so the ubuntu build-test job is the first thing that type-checks it, and only a real .deb/.rpm/snap install can display it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This was referenced Aug 10, 2026
PathGao
pushed a commit
that referenced
this pull request
Aug 12, 2026
#570 was the last issue number in either README, and it was one I added a few days ago. It is a closed bug report, and after #573 the sentence it was attached to describes ordinary behaviour -- so a reader asking "how does my .deb update" now clicks through to a fault that no longer exists. The half-sentence in front of it went for the same reason: "rather than offering one it cannot install" describes what the app used to do. What is left is what the reader needs -- .deb and .rpm are one-time installs, there is no apt or dnf repository, and Check for Updates says so. Both READMEs keep the Report a Bug link, which is the only issue link either of them had before.
PathGao
added a commit
that referenced
this pull request
Aug 12, 2026
…660) * docs: bring the release runbook and the syntax reference up to 2.7.4 Four claims in the docs describe behaviour that PRs since 2.7.3 changed, and two features shipped without reaching the file that documents what Markpad can do. RELEASING.md - snapcraft.yaml no longer builds the app; it unpacks the release's own .deb (#579), so the `npm ci` note and the `rust-deps` troubleshooting row both described a file that no longer exists in that shape (#577). - The .deb/.rpm coverage note still said *Check for Updates…* offers an update and then fails to install it. #573 asks `self_update_supported` first, so it says where updates come from instead. - Adds the AppImage checks (#584, #658) as their own troubleshooting row, and notes that release builds are serialized (#612). README, README.zh-CN - The same pre-#573 claim, in both languages. The Chinese one also told .deb/.rpm users to update "through their distribution channels", which is the sentence #566 removed from the English one — there is no apt or dnf repository. snapcraft.yaml - The comment pointed at .github/workflows/test_snap.yml, deleted in #601. samples/markdown-syntax{,.zh-CN}.md - Lists: Enter continues the marker, Tab changes level, Enter on an empty item leaves the list (#636), and the three list chords (#652). - Tables: cell, row and column keys, why deleting a row has no chord (#645, #653). - Links: Ctrl/Cmd+K (#652). npm test 904 pass, vitest 365 pass. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * docs(syntax): drop two design arguments that belong in the PRs, not here The syntax reference carries no issue numbers, no history and no defence of a decision anywhere in its 500 lines; where it does give a reason -- the three deliberate incompatibilities -- the reason tells the reader what to write instead. Two sentences I added argued for the design at the reader: that four other editors use Mod+K, and why deleting a row has no chord while deleting a column does. Neither changes anything the reader would type. What is left of the second is the part that was actually useful: to delete a row, delete its line. * docs(readme): drop the only issue number a reader ever sees #570 was the last issue number in either README, and it was one I added a few days ago. It is a closed bug report, and after #573 the sentence it was attached to describes ordinary behaviour -- so a reader asking "how does my .deb update" now clicks through to a fault that no longer exists. The half-sentence in front of it went for the same reason: "rather than offering one it cannot install" describes what the app used to do. What is left is what the reader needs -- .deb and .rpm are one-time installs, there is no apt or dnf repository, and Check for Updates says so. Both READMEs keep the Report a Bug link, which is the only issue link either of them had before. * docs(releasing): cut the post-mortems back out of the runbook This file is what you read on the day you cut a release. Over three days in August I turned it into a runbook with post-mortems threaded through it: which run pushed Chocolatey 2.7.2 at 15:37, that the snap served 2.6.11 for three months and six versions, which two of the three v2.7.2 attempts the AppImage strip failed in, how the 2.7.2/2.7.3 lock skew was found. All true, none of it changes what you do next, and it sits between you and step 5 while a build is running. Test applied to each one: can it change an action taken on release day. Gone: the "Why package managers publish after the release" section entirely -- its one operational sentence (workflow_dispatch works against a published tag, the only way to exercise snapcraft.yaml) moves into step 7, where you would need it. The failure histories in three troubleshooting rows, keeping the symptom and the fix. The placeholder-pubkey era. Two design defences. Kept: reasons that stop you doing the wrong thing -- why not to create anything at alecdotdev/Markpad, why the Cargo.lock bump is the one that gets forgotten. A prohibition without its reason gets deleted as superstition by whoever comes next. 1957 -> 1563 words. The version before I started was 883. --------- Co-authored-by: PathGao <gaoyanbo@gaoyanbodeMacBook-Air.local> Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
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.
Closes #570.
What a user sees today
Being offered an update that cannot be installed is worse than not being offered one. Roughly 18 people per release can reach this (v2.7.2:
.deb15,.rpm3), plus snap.Why both halves behave that way
The check succeeds because
latest.jsonpublishes the plainlinux-x86_64key, which every Linux build asks for. That is correct and must stay —build.ymlexplains why at length, and it is the key the AppImage needs.The install fails because the updater has exactly one Linux strategy: rename the downloaded file over the running executable.
It would normally pick
install_deb/install_rpminstead:bundle_type()reads__TAURI_BUNDLE_TYPE, a static patched into the binary after the build — and that patch fails on Linux. From the v2.7.2 release build, three times, once per bundle:So
bundle_type()isNone, all three formats take the AppImage path, andinstall_deb/install_rpmare unreachable code in our builds.This symbol is already documented in this repository.
build.yml's comment onlatest.jsonexplains that Windows and Linux clients cannot ask for an installer-suffixed key precisely because they do not know their own bundle type. That consequence was understood; this one — that it also decides how the update installs — was not followed through.The change
Ask before checking, not after.
self_update_supported— one Rust command. On Linux, true only when running from an AppImage; elsewhere always true.runCheck()consults it first and enters a newpackage-managedphase, which tells the user where updates actually come from.Two details worth stating:
It reads tauri's
Env, notstd::env::var("APPIMAGE"). Tauri additionally verifies the running executable is under$TMPDIR/.mount_, so exporting the variable by hand cannot make a package-managed install claim it is updatable.Windows and macOS are untouched. Windows' updater runs the downloaded NSIS installer rather than overwriting in place, and macOS is the one platform where
bundle_type()answers without the patch. Gating either would break a working updater for 95% of downloads.Localisation: four languages, not 26
The update dialog is translated into en, zh-CN, zh-TW and ko only — the other 22 languages already fall back to English for all 24 of its strings. Adding the two new keys everywhere would put two localised lines inside an otherwise English dialog, so they match the existing set exactly. A test asserts the two sets are equal in both directions, so neither a missing translation nor an inconsistent one can land quietly.
(That 22-language gap is real and worth its own issue; it is not this PR's to fix.)
Verification
cargo checkunused import: Statewarning is on master too — checked, not introduced here)npm testnpm run checkcheck()The ordering test is the important one. Asserting that both calls exist would pass on the broken code, since the broken code also makes both calls — the defect is entirely in their sequence, so that is what is asserted.
What is not verified here, and cannot be: the Linux branch is
cfg'd out on macOS, so theubuntu-22.04job in this PR is the first thing that type-checks it. And the new phase is unreachable on macOS and Windows by design, so only a real.deb,.rpmor snap install can display it — worth a look on the first release that carries this.🤖 Generated with Claude Code