Skip to content

2.40.0: the Linux updater installs the right package, and every device links attachments the same way - #699

Merged
adibhanna merged 5 commits into
mainfrom
v2.40.0
Aug 31, 2026
Merged

2.40.0: the Linux updater installs the right package, and every device links attachments the same way#699
adibhanna merged 5 commits into
mainfrom
v2.40.0

Conversation

@adibhanna

@adibhanna adibhanna commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Opens the 2.40.0 cycle. Two fixes so far.


1. The Linux updater installs the package you actually run

Report (Kelv, Discord, CachyOS): "Install and Relaunch" asked for a root password and then ran dpkg -i, a Debian command that does not exist on Arch, so the updater never worked and every update had to be installed from a terminal by hand.

Cause, which is in the build. electron-builder stamps a package-type file into the install, and electron-updater reads it to decide which release asset to download and which installer to run. All five Linux targets are cut from one staging directory, they build concurrently (platformPackager.js:105-126; isAsyncSupported defaults true in core.js:49), and only the deb and rpm targets write that file (FpmTarget.js:117, supportsAutoUpdate at :234). A package ships whichever value a neighbour left behind.

Verified against the published 2.39.0 downloads:

asset stamp consequence
.pacman deb downloads the .deb and runs dpkg, the reported bug
.deb deb correct
.rpm rpm correct
.tar.gz none falls back to the AppImage updater, which self-disables

Fix. One file with several racing writers cannot be made reliable at build time, so the app stops trusting the stamp's contents and takes the format from /etc/os-release (ID, then ID_LIKE, which carries derivatives like CachyOS without enumerating them). The stamp is consulted for presence only, so an AppImage and an AUR or tarball install keep behaving exactly as they do today. A build-time hook was written first and dropped on purpose: with the targets racing over one file it is only usually right, and "usually" is what shipped this bug. As a backstop, an update whose format contradicts the system never reaches pkexec.

Arch users on 2.39.0 or earlier carry the wrong stamp already, so 2.40.0 needs one last manual sudo pacman -U; after that it installs itself.


2. Every device links an attached file the same way

Attaching or dropping a file wrote a different link depending on where you did it. Paste, and an existing asset dragged from the sidebar, embedded a vault-relative wikilink; a drop wrote a note-relative markdown link ![pic](<../assets/pic.png>); the self-hosted server produced that same note-relative form for the web client; and the mobile apps had a third answer. Same file, same folder, three different links.

The note-relative form was also wrong, not merely different: nothing rewrites relative asset paths when a note moves, so ../assets/pic.png broke the moment the note changed depth.

One rule now, everywhere: the file lands in assets/ and is linked by vault-relative path, an image as ![[assets/pic.png]] and anything else as [name](<assets/report.pdf>).

route before now
Desktop paste wikilink unchanged
Desktop drag existing asset wikilink unchanged
Desktop drop / attach ![alt](<../assets/x>) wikilink
Web paste wikilink unchanged
Web drop (via server) note-relative link wikilink
Go server import note-relative link wikilink
Mobile paste, both vault kinds wikilink unchanged
Mobile attach, local vault vault root + ../ link wikilink

Existing notes are unaffected: resolveAssetVaultRelativePath tries note-relative, then vault-root, then a unique basename, so both the old and the new form keep resolving on both rendering surfaces.

The mobile half ships separately, in ZenNotes/zennotesandroid#41 and ZenNotes/zennotesios#15, which also fix corrupted attachment uploads to a self-hosted server.


Verification

  • Full production dependency audit: 0 vulnerabilities
  • All workspace and desktop type checks/tests passed
  • Production desktop, web, and Go server builds passed
  • npm run pack completed and produced a locally signed macOS app
  • Filename sanitization tests cover traversal, control characters, wikilink delimiters, and header injection on both Electron and Go paths
  • Linux updater tests cover ID/ID_LIKE ordering, official /opt/ZenNotes/resources package detection, leaked stamps in tar/AUR layouts, and explicit updater selection

Release preparation

All workspace manifests are now 2.40.0. Electron Builder is upgraded to 26.15.7 for pacman updater metadata, with the upstream dependency-cycle guard carried as a patch; packaging succeeds with that patch applied. Local packaging intentionally skipped notarization because Apple credentials are held by the GitHub release workflow.

The pacman installer itself cannot be exercised end to end on this macOS host; the release matrix will build and validate Linux x64 and arm64 artifacts in CI.

On Arch, "Install and Relaunch" asked for a root password and then ran
`dpkg -i`, a Debian command that does not exist there, so the in-app
updater never worked and every update had to be fetched and installed
from a terminal by hand. Reported on Discord by Kelv, on CachyOS.

The cause is in the build, not the app. electron-builder stamps a
`package-type` file into the install, and electron-updater reads it to
decide which release asset to download and which installer to run. All
five Linux targets are cut from ONE staging directory, they build
concurrently (isAsyncSupported defaults to true), and only the deb and
rpm targets write that file, so a package ships whichever value a
neighbour happened to leave behind. Confirmed against the published
2.39.0 downloads: the .pacman carried `deb`, while the .deb and .rpm
were correct. One file and several racing writers cannot be fixed at
build time, so this stops trusting the stamp's contents.

The format now comes from the running system: /etc/os-release ID, then
ID_LIKE, which is what carries the derivatives (CachyOS declares
ID_LIKE=arch) we would otherwise have to enumerate one by one. The stamp
is still consulted, but only for whether it exists, because that is the
one true thing it says: only a ZenNotes system package carries it. An
AppImage and an AUR or tarball install have none and are deliberately
left exactly as they were, updating in userspace and through their own
package manager respectively. An unrecognized distro changes nothing.

A build-time fix was written first and dropped on purpose: a hook can
stamp each package as it is built, but with the targets racing over one
file it is only usually right, and "usually" is what shipped this bug.

As a backstop, an update whose format contradicts the system no longer
reaches pkexec. Instead of a root prompt for a command the machine does
not have, ZenNotes names the package it needs and links the download.

Arch users on 2.39.0 or earlier carry the wrong stamp in the copy they
already have, so this one still needs a manual install; from here it
installs itself.

Not exercisable on the release host (no Linux machine): the decision and
the guard are unit-tested, and the packaged macOS build was launch
checked over CDP, but the pacman install has not been run end to end.

Claude-Session: https://claude.ai/code/session_01AYTRixg5TJmxn2j6FCqfUD
Comment thread apps/desktop/src/main/updater.ts Fixed
Attaching or dropping a file wrote a different link depending on which
surface you did it from. Paste, and an existing asset dragged in from the
sidebar, embedded a vault-relative wikilink; a drop wrote a markdown link
with a path relative to the note, `![pic](<../assets/pic.png>)`, and the
self-hosted server produced that same note-relative form for the web
client. The mobile apps had a third answer again. Same file, same folder,
three different links.

The note-relative form was also wrong, not merely different: nothing
rewrites relative asset paths when a note moves, so `../assets/pic.png`
broke the moment the note changed depth.

So there is now one rule, everywhere: the file lands in `assets/` and is
linked by VAULT-relative path, an image as `![[assets/pic.png]]` and
anything else as `[name](<assets/report.pdf>)`. That is what paste and
the sidebar drag already wrote, so this moves the odd routes onto the
form the app already used most.

Existing notes are unaffected: resolveAssetVaultRelativePath tries
note-relative, then vault-root, then a unique basename, so both the old
and the new form keep resolving on both rendering surfaces.

`ImportAsset` no longer reads notePath, since where the note lives can no
longer change what is written; it stays in the signature because the
handler and clients still send it. The matching mobile fixes ship in the
zennotesandroid and zennotesios repos.

Claude-Session: https://claude.ai/code/session_01AYTRixg5TJmxn2j6FCqfUD
@adibhanna adibhanna changed the title Fix(updater): a Linux update installs the package you actually run 2.40.0: the Linux updater installs the right package, and every device links attachments the same way Aug 29, 2026
@adibhanna
adibhanna merged commit 7f028c8 into main Aug 31, 2026
7 checks passed
@adibhanna
adibhanna deleted the v2.40.0 branch August 31, 2026 13:49
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.

2 participants