Skip to content

Linux packaging (AppImage + deb) and self-update - #169

Merged
fylorn merged 2 commits into
devfrom
linux/packaging-update
Sep 24, 2026
Merged

fylorn merged 2 commits into
devfrom
linux/packaging-update

Conversation

@fylorn

@fylorn fylorn commented Sep 24, 2026 •

Copy link
Copy Markdown
Contributor

Linux distribution: AppImage + deb for x86_64 and aarch64, and self-update for both.

Packaging

  • src-tauri/tauri.linux.conf.json: appimage + deb targets; Linux icons 32/128/256/512 (256 and 512 newly rendered by icons/render.py; existing icons are byte-identical after re-rendering).
  • The bundler already writes Depends: libayatana-appindicator3-1, libwebkit2gtk-4.1-0, libgtk-3-0 (tauri-cli interface/rust.rs, from the tray-icon feature) and the x-scheme-handler/thinkwatch MimeType from the deep-link config. dpkg-shlibdeps on the built app + twcore lists only libraries those three pull in transitively.
  • src-tauri/linux/main.desktop (deb desktopTemplate, also used for the AppImage): the bundler's default has Exec=thinkwatch-lite with no field code, so GLib appends %f and drops non-file URLs (gdesktopappinfo.c expand_application_parameters / expand_macro 'f'). The template uses Exec=thinkwatch-lite %u and terminates the MimeType list. Desktop file name stays ThinkWatch Lite.desktop.
  • The bundler names the deb package think-watch-lite (kebab-case of the product name, no config key). release.yml repacks it as thinkwatch-lite before signing (control file only; payload untouched).
  • AppImage bundling needs /usr/bin/xdg-mime when deep links are configured, so CI installs xdg-utils.
  • fetch-core.sh: the two *-unknown-linux-gnu twcore assets. Core has not published them yet, so a Linux tauri build fails at the fetch step until the next core release includes them.

Finding the bundled core

bundled_core() gets a Linux branch: when tauri::utils::platform::bundle_type() is set (the bundler binary-patches it per package type), the only candidate is <exe>/../lib/<productName>/twcore (deb: /usr/lib/ThinkWatch Lite/, AppImage: same path under the mount). Missing → the existing "bundle is missing twcore" error; no dev/env/PATH fallback. Derived from the executable rather than resource_dir(), which falls back to $APPDIR from the environment.

Self-update

  • update::kind() on Linux reads bundle_type(): AppImage (and $APPIMAGE set) → Standalone; deb → new Install::Deb; anything else (cargo run, extracted AppImage, rpm) → Dev.
  • AppImage: the updater plugin replaces $APPIMAGE in place; app.restart() relaunches $APPIMAGE (tauri process::current_binary). An unwritable location gets a specific message with the releases link.
  • deb: plugin download() (verifies the signature before returning bytes), then pkexec /usr/bin/apt-get install -y <file> from a fresh 0700 dir. The plugin's install() is deliberately not used: it falls back to prompting for the password via zenity/kdialog and piping it to sudo. Core is stopped before installing; on failure (126 cancelled, 127 not authorized, apt error) the gateway is resumed via the shared resume_after_failed_update (now cfg(any(windows, linux))).
  • Update window: deb gets one sentence saying installation requires administrator authorization.

Release pipeline

  • New linux matrix job on ubuntu-22.04 / ubuntu-22.04-arm (label listed for public repos in GitHub's hosted-runner reference), so the glibc floor is 2.35.
  • Looks inside both packages: app + twcore present and of the right ELF arch, the package's own bundle-type marker, highest GLIBC_ ≤ 2.35, twcore --version, desktop-file-validate, MimeType and %u; deb Package/Version/Architecture/Depends.
  • Signs AppImage and deb; manifest.py requires linux-{x86_64,aarch64}-{appimage,deb} (the plugin looks up <os>-<arch>-<installer> first) and rejects a file with the wrong extension for a key.

Verified in an Ubuntu 22.04 aarch64 container

  • tauri build → AppImage + deb; the "Name the packages" and "Look inside" steps extracted from this release.yml pass on them.
  • deb: apt-get install pulls nothing unexpected; twcore runs from /usr/lib/ThinkWatch Lite/twcore; gio open thinkwatch://… reaches the running instance with the URL in argv; WM_CLASS is thinkwatch-lite = StartupWMClass; with twcore removed the app logs the missing-bundle error and does not run a twcore from THINKWATCH_CORE_BIN or PATH; apt-get remove cleans up.
  • AppImage self-update, end to end: two builds with a throwaway key, local latest.json: auto-check finds the update, the update window installs it, the plugin rewrites $APPIMAGE, core stops, the app relaunches from $APPIMAGE as the new version (new file hash, new PID, .updated-from consumed).
  • Not verifiable in a container (acceptance list): pkexec dialog + deb self-update on a real desktop, Wayland app_id, AppImage under FUSE on Ubuntu 24.04.

Needs a rehearse/** run before the next tag, after core publishes the Linux twcore assets.

🤖 Generated with Claude Code

@fylorn
fylorn force-pushed the linux/packaging-update branch from 15fd0dc to 86db61a Compare September 24, 2026 10:42
- tauri.linux.conf.json: AppImage + deb targets, Linux icon sizes
  (256/512 rendered by icons/render.py), category/publisher/homepage.
  The bundler already writes the deb Depends (webkit2gtk-4.1, gtk3,
  ayatana-appindicator) and the thinkwatch:// MimeType.
- linux/main.desktop: the bundler's template with `Exec=... %u` (without
  a field code GLib appends %f, which drops non-file URLs, so sign-in
  callbacks never reached the app) and a terminated MimeType list.
- fetch-core.sh: x86_64/aarch64-unknown-linux-gnu twcore assets, with an
  ELF-specific architecture check.
- bundled_core(): on Linux, an installed build (bundle_type() is set by
  the bundler's binary patch) only runs <exe>/../lib/<product>/twcore and
  never falls through to the dev candidates. Derived from the executable,
  not resource_dir(), which consults $APPDIR.
- update::kind(): Linux reads bundle_type(). AppImage (with $APPIMAGE) is
  Standalone and is replaced in place by the updater plugin; a new
  Install::Deb downloads + verifies via the plugin and installs with
  `pkexec apt-get install -y` -- never the plugin's install(), which falls
  back to asking for the password via zenity/kdialog and feeding sudo.
  Failures say where to download; the gateway is resumed through the
  shared resume_after_failed_update (now Windows + Linux).
- Update window: deb copy states that installing needs administrator
  authorization.
- release.yml: Linux matrix (ubuntu-22.04 / ubuntu-22.04-arm) builds
  AppImage + deb, renames the deb package from the bundler's
  `think-watch-lite` to `thinkwatch-lite`, looks inside both (twcore
  arch/version, bundle-type marker, glibc <= 2.35, desktop entry, deb
  fields and Depends) and signs both.
- manifest.py: linux-{x86_64,aarch64}-{appimage,deb} keys, all required;
  rejects a file with the wrong extension for a key.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@fylorn
fylorn force-pushed the linux/packaging-update branch from 86db61a to f74c15f Compare September 24, 2026 10:47
A deb build started from inside an AppImage (a terminal or launcher
packaged that way) inherits that AppImage's APPIMAGE and APPDIR. Tauri's
Env::default reads both without checking: restart() relaunches $APPIMAGE,
resource_dir() falls back to $APPDIR, and twcore inherits them. Unless
bundle_type() says this is an AppImage, remove both first thing in run(),
while the process is still single-threaded.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@fylorn
fylorn merged commit 138eb13 into dev Sep 24, 2026
4 checks passed
@fylorn
fylorn deleted the linux/packaging-update branch September 24, 2026 11:06
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.

1 participant