Skip to content

fix(update): stop offering an update that a package-managed install cannot apply - #573

Merged
PathGao merged 1 commit into
masterfrom
fix/570-package-managed-updates
Aug 10, 2026
Merged

fix(update): stop offering an update that a package-managed install cannot apply#573
PathGao merged 1 commit into
masterfrom
fix/570-package-managed-updates

Conversation

@PathGao

@PathGao PathGao commented Aug 10, 2026

Copy link
Copy Markdown
Collaborator

Closes #570.

What a user sees today

①  .deb install → Settings → "Check for updates…"
②  "Markpad v2.7.3 is available. You're on v2.7.2."     ← the check succeeds
③  Download & Install → fails

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: .deb 15, .rpm 3), plus snap.

Why both halves behave that way

The check succeeds because latest.json publishes the plain linux-x86_64 key, which every Linux build asks for. That is correct and must stay — build.yml explains 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.

AppImage   $APPIMAGE — a file the user owns              →  works
.deb       /usr/bin/Markpad                              →  needs root
.rpm       /usr/bin/Markpad                              →  needs root
snap       /snap/markpad/<rev>/…  read-only squashfs     →  impossible

It would normally pick install_deb / install_rpm instead:

// tauri-plugin-updater-2.10.1/src/updater.rs
match installer_for_bundle_type(bundle_type()) {
    Some(Installer::Deb) => self.install_deb(bytes),
    Some(Installer::Rpm) => self.install_rpm(bytes),
    _ => self.install_appimage(bytes),      // ← None lands here
}

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:

Warn Failed to add bundler type to the binary: __TAURI_BUNDLE_TYPE variable not
found in binary. … Updater plugin may not be able to update this package.

So bundle_type() is None, all three formats take the AppImage path, and install_deb/install_rpm are unreachable code in our builds.

This symbol is already documented in this repository. build.yml's comment on latest.json explains 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 new package-managed phase, which tells the user where updates actually come from.

Two details worth stating:

It reads tauri's Env, not std::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 check clean (the pre-existing unused import: State warning is on master too — checked, not introduced here)
npm test 965 pass
npm run check 675 files, 0 errors
mutation: move the gate after check() the ordering test fails, the other four still pass

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 the ubuntu-22.04 job 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, .rpm or snap install can display it — worth a look on the first release that carries this.

🤖 Generated with Claude Code

…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>
@PathGao
PathGao merged commit 553ba0a into master Aug 10, 2026
4 checks passed
@PathGao
PathGao deleted the fix/570-package-managed-updates branch August 10, 2026 12:48
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Linux: .deb, .rpm and snap installs are offered an update they cannot install

1 participant