feat(bundler): Truly portable appimage (experimental) - #12491
feat(bundler): Truly portable appimage (experimental)#12491FabianLars wants to merge 76 commits into
Conversation
Package Changes Through 503bdbcThere are 14 changes which include tauri with minor, tauri-cli with minor, @tauri-apps/cli with minor, tauri-runtime with minor, tauri-runtime-wry with minor, tauri-utils with minor, tauri-bundler with minor, tauri-build with minor, tauri-macos-sign with minor, tauri-codegen with minor, tauri-macros with minor, tauri-plugin with minor, tauri-driver with minor, @tauri-apps/api with minor Planned Package VersionsThe following package releases are the planned based on the context of changes in this pull request.
Add another change file through the GitHub UI by following this link. Read about change files or the docs at github.com/jbolda/covector |
|
anyone else seeing this lately or just me? |
Pin the quick-sharun tooling to a reviewed revision and verify its checksum. It was re-downloaded from the tip of a third-party branch on every build, so an upstream push could silently change or break a release build, and offline builds were impossible. The revision is part of the cached file name, so a build only hits the network the first time it sees a new pin. TAURI_BUNDLER_QUICK_SHARUN_SCRIPT points the bundler at a local script for testing the tooling itself. The anylinux helper library the script compiles into the bundle is pinned to the same revision. Deploy the libraries injected through appimage.files. The CLI adds libappindicator there when the tray icon feature is enabled, but copy_custom_files puts it under the AppDir's usr/lib, which sharun never looks at, so the tray library was missing from the bundle. Stream the tooling output instead of capturing it. On failure the user saw "failed to run /bin/sh" with the actual diagnostics discarded, which is how a missing patchelf presented itself. Also fail with a clear error when the run succeeds but no AppImage was produced. Return the .zsync file next to the AppImage. appimagetool writes one whenever update information is embedded, which it also guesses from GITHUB_REPOSITORY, and it is part of the release. Honor bundleMediaFramework by forcing gstreamer deployment. The option existed but did nothing on this code path. Reject cross-architecture builds. The deployment collects libraries from the build system, so building for a foreign target used to produce an AppImage named for the target and filled with the host's libraries. Pass the script to sh as an argument rather than building a shell command string, so paths containing spaces survive, and return an error instead of panicking when no square icon is available.
Remove the squashfs option. The appimagetool the tooling uses only produces DwarFS images and exposes no squashfs switch, so the option was read from the config but never had any effect. Correct the useNewFormat documentation. It claimed the format can only be built on Debian/Ubuntu 24.04 and above, while Arch is the recommended build host and Ubuntu 24.04 is known to lose hardware acceleration on Wayland with the proprietary NVIDIA driver. It now also states that the bundler runs the application once during bundling to discover the libraries it loads at runtime, and that it cannot cross-compile. Drop the zsyncmake requirement from the updateInformation docs, since appimagetool generates the zsync file itself, and document that the UPINFO environment variable takes precedence over the config value. Point the deprecation warning at the config option instead of a docs anchor that does not exist yet.
…nging quick-sharun runs each binary it deploys for a few seconds to discover which libraries get dlopened, then kills it with a process-group signal. That signal only reaches the process if the shell put it in its own process group, which is what `set -m` does. dash does not create the group when there is no controlling terminal. On Debian and Ubuntu, where /bin/sh is dash, every terminal-less build, which is every CI run, therefore hangs forever on the first traced process that does not exit by itself. On Ubuntu that is webkit2gtk's MiniBrowser, which the tooling deploys and traces. bash creates the group with or without a terminal, which is why the same build works on Arch and Fedora, where /bin/sh is bash. Reproduced in a container: an ubuntu:24.04 build stopped with "can't access tty; job control turned off" and sat on MiniBrowser until it was killed. With bash, the same build traces MiniBrowser and finishes. Verified on ubuntu:22.04, ubuntu:24.04 and ubuntu:26.04. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
quick-sharun silently skips wrapping the application binary when the AppDir path contains whitespace. It still exits successfully, so the build looks fine and the AppImage dies on the user's machine with "error while loading shared libraries: libgdk-3.so.0". Isolated with two otherwise identical runs: with a space in the path the binary stayed unwrapped in bin/ and shared/bin/ was never created, and without one it was hardlinked to sharun as expected. A space in any parent directory triggers it too. Product names with spaces are common, tauri's own api example is called "Tauri API", so derive the AppDir, desktop and icon file names by replacing whitespace. The product name still reaches users through the desktop entry's Name and the AppImage file name, and the resource directory keeps the product name because the runtime resolves resources by it. The project path is not ours to rename, so a remaining space in the path is now an error that says what to do rather than a bundle that fails to start. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…mage Both the AppImageKit runtime and uruntime extract to $TMPDIR/appimage_extracted_* with --appimage-extract-and-run, which the ".mount_" only check flagged as a possible security issue on every start. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The sharun bundler returns the .zsync next to the AppImage so it can be published; it is not an update package and must not get a .sig. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ment the tooling switches The desktop entry refers to the icon by the main binary name and the AppImage spec expects AppDir/<Icon>.png, which is what desktop integration tools look for. Also state that sidecars are launched during bundling and that quick-sharun's environment variables are forwarded. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
|
I tried to run some LLM-based tests on VMs and pushed a few fixes, let me know if you spot any issue (i also have a folder with a bunch of logs and screenshots if needed, but appimage ran on a bunch of different systems |
The CEF AppImage carries the build host's glibc, so every library it does not bundle is resolved from the host at run time and links against a glibc the bundled one cannot satisfy on any distro newer than the builder. Chromium dlopens libsoftokn3.so, gets the host copy and aborts on first use (FATAL crypto/nss_util.cc, nss_error=-5925), so the nightly AppImage dies ~1.5s after launch on anything past the builder's glibc 2.35 — Ubuntu 24.04+, Fedora 42, Arch. ANGLE loses libGL.so.1 the same way, which is why the GPU process exits during startup. quick-sharun's DEPLOY_CHROMIUM rules already list these modules, but the script cli-cef 3.0.0-alpha.26 downloads is a fork ~1100 lines behind pkgforge upstream that expands its deployment array without `eval`: every path keeps its literal quotes, so all DEPLOY_* extras are silently skipped and only ldd-derived libraries reach the bundle. Hand the modules over through appimage.files instead — the /usr/lib channel the bundler passes to quick-sharun as plain ELF arguments — and flatten Debian's <triple>/nss/ first so they land next to the libnss3.so that loads them. libsqlite3 follows as libsoftokn3's own dependency. A post-build check fails the leg when one is missing, because the tooling skips what it cannot find without erroring. tauri-apps/tauri#12491 replaces that fork with a pinned, checksummed upstream revision and fixes the hang the STRACE_MODE pin works around; the comments spell out what to drop once cli-cef carries it. Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Ubuntu 25 by chance? likely this bug |
| // quick-sharun runs each binary it deploys for a few seconds to see which | ||
| // libraries get dlopened, then kills it with a process-group signal. That | ||
| // only reaches the process if the shell put it in its own group, which is | ||
| // what `set -m` is for. dash does not create the group when there is no | ||
| // controlling terminal, so on Debian and Ubuntu, where /bin/sh is dash, | ||
| // every terminal-less build - which is every CI run - hangs forever on the | ||
| // first traced process that does not exit by itself. bash creates the group | ||
| // either way, so prefer it and fall back to sh where it is missing. | ||
| let shell = which::which("bash") | ||
| .map(|p| p.to_string_lossy().into_owned()) | ||
| .unwrap_or_else(|_| "/bin/sh".into()); | ||
|
|
||
| // Passing the script to the shell as an argument rather than building a | ||
| // `-c` string keeps paths containing spaces intact. | ||
| let mut cmd = Command::new(shell); |
There was a problem hiding this comment.
@lucasfernog This shouldn't be needed anymore: pkgforge-dev/Anylinux-AppImages#853
Nope that happened on my arch build machine Edit: happened when building tauri's webkitgtk version, cef version worked fine. Just mentioned because the project in the error is named ceftest |
* fix(ci): bundle the audio libraries the CEF AppImage dlopens TTS runs to completion in the AppImage but nothing is audible: Chromium reaches its audio backend through dlopen, and the bundle carries none of it. libpulse.so.0 resolves to the host copy and fails against the bundled glibc 2.35 (host libpulsecommon needs GLIBC_2.38), and the ALSA fallback dies the same way — its `default` PCM is a config hook that loads the host's libasound_module_conf_pulse.so, so snd_pcm_open() returns "Unknown PCM default" and only a raw hw: device opens, which Chromium will not use. libspeechd.so.2 fails identically, leaving the Web Speech engine with no system voices. Stage all three through the same appimage.files channel as the NSS modules (#6101). quick-sharun then pulls the rest of the closure (libsndfile, FLAC, vorbis, opus, ogg, asyncns...) from the runner by ldd. libpulse keeps its private half in <triple>/pulseaudio/ and finds it by RUNPATH, which points at the host directory: verified that the private lib is deployed to lib/pulseaudio/ and that lib.path gains `+/pulseaudio` so sharun's library path wins over the RUNPATH, and the guard now checks both. Verified against the shipped 0.12.6-2026090706 bundle on glibc 2.41: in its runtime environment dlopen of libpulse/libpipewire/libspeechd fails and ALSA has no usable PCM, while staging these libraries makes dlopen succeed and a tone play through PipeWire's PulseAudio server. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(linux): stop the AppImage asking for a root password (#6110) Every new AppImage pops a modal on first launch and asks for the root password through pkexec. That is the bundler's fix-namespaces.hook: on distros that restrict unprivileged user namespaces (Ubuntu 24.04+) it offers to install an AppArmor profile granting `userns`, and since the profile it writes is keyed to the AppImage's exact path, every nightly asks again. Readest never needs that capability. tauri-runtime-cef sets CefSettings.no_sandbox unless its `sandbox` feature is enabled, and this build compiles it without: every Chromium child of the shipped AppImage runs `--type=zygote --no-sandbox`, and the app starts fine on a machine where `unshare -Urm` is denied and no profile is installed. The hook is boilerplate for Chromium apps that do sandbox; here it buys a root prompt and nothing else. Ship an AppRun hook that sorts ahead of it and stubs the two helpers it prompts through — both come from AppRun.lib and have no other callers in the bundle, so the rest of that hook still runs, it just cannot reach the user. Verified against the shipped 0.12.6-2026090706 bundle: stock hooks block startup on the dialog (three lines of log, app never comes up), with this hook the app starts normally and no zenity or pkexec process appears. Also verified in a jammy container that a file shipped through appimage.files survives quick-sharun and lands in bin/ ahead of the bundler's own hooks. The entry lives in tauri.conf.json rather than the workflows so local Linux builds get it too; the CI staging step now merges into that map instead of replacing it. This leaves the AppImage exactly as sandboxed as it is today, which is also what the WebKitGTK builds before it were — Tauri never enabled WebKit's sandbox either. Sandboxing the renderer on Linux is a separate change: it needs the CEF `sandbox` feature plus /etc/apparmor.d/readest shipped in deb/rpm, and this hook would then have to go. Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> * ci: pin down the quick-sharun regression behind the AppImage staging The staging comment blamed drift — a fork "~1100 lines behind upstream" that "expands its deployment array without eval". The gap is real but not the cause. The fork's HEAD, FabianLars/Anylinux-AppImages@3e280d1b "fix handling of spaces in input paths", is a deliberate one-line change: eval set -- "$TO_DEPLOY_ARRAY" "$@" -> set -- $TO_DEPLOY_ARRAY "$@" Upstream's line really does split positional paths on spaces, and tauri names the AppDir after the product, so the change fixed something. But the array entries come single-quoted from _save_array, so without the eval each keeps its literal quotes, fails `[ -f ]`, and every DEPLOY_* extra is silently skipped. Measured in a dash one-liner: upstream splits "My App/libfoo.so" into two arguments, the fork keeps the array entry as '/path/libextra.so' quotes and all, and quoting the positional args through the same helper — eval set -- "$TO_DEPLOY_ARRAY" "$(_save_array "$@")" — keeps both. Record that, and the two things upstream review pointed out: pulse is already implied by DEPLOY_CHROMIUM through DEPLOY_PIPEWIRE, so the libpulse entry turns redundant the moment the fork's main is fixed (the CLI re-fetches it on every build), and libpipewire is not needed for audio at all, only for the screen-share portal, so it stays out. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * ci(linux): build the CEF tauri CLI from readest/tauri with pkgforge pinned The published @tauri-apps/cli-cef (3.0.0-alpha.26) bundles the AppImage with a fork of quick-sharun whose deployment array is broken, and it re-downloads that fork on every build with no way to point it anywhere else - no env var, no mirror (TAURI_BUNDLER_TOOLS_GITHUB_MIRROR only rewrites github.com URLs), and no cache seeding, since the guard that made the pre-#6074 seed work is commented out. Build the CLI on the Linux legs from readest/tauri@c62e9f90 instead, the way the workflow did until #6074: there sharun_cef.rs fetches pkgforge-dev/Anylinux-AppImages at facb95e8 verified by SHA-256 (the revision tauri-apps/tauri#12491 pins), runs the tooling with bash and streamed output, passes the deploy list as arguments, keeps whitespace out of the AppDir name, and adds no fix-namespaces hook. Pinned to a rev so a release cannot change behaviour under us; rust-cache keeps ~/.cargo/bin and a marker file skips the rebuild while the pin holds. scripts/tauri.mjs runs `cargo tauri` when TAURI_CEF_CARGO=1, with the same command line as the npm CLI. STRACE_MODE stays off in this step; the hang it works around is fixed by the bash/streamed-output change, but lifting it is its own change. The hand-staged NSS and audio entries stay too, until a nightly built this way confirms the tooling deploys them itself. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * ci: trim the AppImage comments to the essentials Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
will add description later
Edit1: issue with bun's single exe output oven-sh/bun#28281fixed with a hack: #12491 (comment)fixes #10617 (xdg-open)
fixes #10078 (xdg-open)
fixes #6172 (xdg-open)
fixes #14208 (NO_STRIP)