From 9c75e74ea331a40645b70f412576f12693acea5a Mon Sep 17 00:00:00 2001 From: Duncan Tourolle Date: Sat, 22 Aug 2026 02:52:32 +0200 Subject: [PATCH] fix(ci): give the builder image what linuxdeploy needs for the AppImage The v0.10.0 release build failed in Build Linux after 16 minutes: failed to bundle project: xdg-open binary not found /usr/bin/xdg-open: No such file or directory linuxdeploy embeds xdg-open into the AppImage and aborts the whole bundle when it is absent. deb and rpm had already bundled fine; only AppImage was affected. This is the one failure tonight that building locally could not have caught, and the reason is worth writing down: a developer machine is a desktop and always has xdg-utils, so the AppImage builds there and fails on a minimal server image. The asymmetry is the bug. Every other release defect this evening was found by building locally first; this one needed the runner. xdg-utils, desktop-file-utils and zsync are added together rather than one at a time. Each round trip costs an image rebuild plus a failed release build, and those three are what linuxdeploy commonly reaches for (xdg-open, desktop-file-validate, and zsync for delta updates). Workflows move to jellytau-builder:2026.08.1, built and pushed with all three verified present inside it before this commit. ci-operations.md gains two things learned here: that an apt addition invalidates the layer above the cargo-install steps, so it is a ~20 minute rebuild rather than the ~2 minutes the trailing layer normally gives; and that Tauri's AppImage bundler downloads linuxdeploy, AppRun and two plugin scripts from GitHub during the build, so an AppImage build depends on GitHub being reachable from the runner. --- .gitea/workflows/build-and-test.yml | 6 +++--- .gitea/workflows/build-release.yml | 10 +++++----- .gitea/workflows/publish-docs.yml | 2 +- .gitea/workflows/traceability-check.yml | 2 +- Dockerfile.builder | 11 +++++++++++ docs/build/ci-operations.md | 24 ++++++++++++++++++++++++ 6 files changed, 45 insertions(+), 10 deletions(-) diff --git a/.gitea/workflows/build-and-test.yml b/.gitea/workflows/build-and-test.yml index 6b3a856c..e25cbecc 100644 --- a/.gitea/workflows/build-and-test.yml +++ b/.gitea/workflows/build-and-test.yml @@ -28,7 +28,7 @@ jobs: if: "!startsWith(github.event.head_commit.message, 'chore(release)')" runs-on: linux/amd64 container: - image: gitea.tourolle.paris/dtourolle/jellytau-builder:2026.08 + image: gitea.tourolle.paris/dtourolle/jellytau-builder:2026.08.1 steps: - name: Checkout repository @@ -187,7 +187,7 @@ jobs: runs-on: linux/amd64 needs: test container: - image: gitea.tourolle.paris/dtourolle/jellytau-builder:2026.08 + image: gitea.tourolle.paris/dtourolle/jellytau-builder:2026.08.1 env: ANDROID_HOME: /opt/android-sdk ANDROID_SDK_ROOT: /opt/android-sdk @@ -256,7 +256,7 @@ jobs: name: Supply Chain runs-on: linux/amd64 container: - image: gitea.tourolle.paris/dtourolle/jellytau-builder:2026.08 + image: gitea.tourolle.paris/dtourolle/jellytau-builder:2026.08.1 steps: - name: Checkout repository diff --git a/.gitea/workflows/build-release.yml b/.gitea/workflows/build-release.yml index 624118d1..c89fce65 100644 --- a/.gitea/workflows/build-release.yml +++ b/.gitea/workflows/build-release.yml @@ -21,7 +21,7 @@ jobs: name: Run Tests runs-on: linux/amd64 container: - image: gitea.tourolle.paris/dtourolle/jellytau-builder:2026.08 + image: gitea.tourolle.paris/dtourolle/jellytau-builder:2026.08.1 steps: - name: Checkout repository uses: actions/checkout@v4 @@ -94,7 +94,7 @@ jobs: runs-on: linux/amd64 needs: test container: - image: gitea.tourolle.paris/dtourolle/jellytau-builder:2026.08 + image: gitea.tourolle.paris/dtourolle/jellytau-builder:2026.08.1 steps: - name: Checkout repository uses: actions/checkout@v4 @@ -235,7 +235,7 @@ jobs: # baked into the builder image. No toolchain installs here — the image has # cargo-xwin, clang/clang-cl, lld, llvm, nsis and the msvc target. container: - image: gitea.tourolle.paris/dtourolle/jellytau-builder:2026.08 + image: gitea.tourolle.paris/dtourolle/jellytau-builder:2026.08.1 steps: - name: Checkout repository uses: actions/checkout@v4 @@ -308,7 +308,7 @@ jobs: runs-on: linux/amd64 needs: test container: - image: gitea.tourolle.paris/dtourolle/jellytau-builder:2026.08 + image: gitea.tourolle.paris/dtourolle/jellytau-builder:2026.08.1 env: ANDROID_HOME: /opt/android-sdk ANDROID_SDK_ROOT: /opt/android-sdk @@ -411,7 +411,7 @@ jobs: needs: [build-linux, build-windows, build-android] if: startsWith(github.ref, 'refs/tags/v') container: - image: gitea.tourolle.paris/dtourolle/jellytau-builder:2026.08 + image: gitea.tourolle.paris/dtourolle/jellytau-builder:2026.08.1 steps: - name: Checkout repository uses: actions/checkout@v4 diff --git a/.gitea/workflows/publish-docs.yml b/.gitea/workflows/publish-docs.yml index a740666c2..7d08d86b 100644 --- a/.gitea/workflows/publish-docs.yml +++ b/.gitea/workflows/publish-docs.yml @@ -21,7 +21,7 @@ jobs: name: Build & publish docs to gitea-pages runs-on: linux/amd64 container: - image: gitea.tourolle.paris/dtourolle/jellytau-builder:2026.08 + image: gitea.tourolle.paris/dtourolle/jellytau-builder:2026.08.1 steps: - name: Checkout code diff --git a/.gitea/workflows/traceability-check.yml b/.gitea/workflows/traceability-check.yml index 58e9562d..e8fd7fb5 100644 --- a/.gitea/workflows/traceability-check.yml +++ b/.gitea/workflows/traceability-check.yml @@ -17,7 +17,7 @@ jobs: runs-on: linux/amd64 name: Check Requirement Traces container: - image: gitea.tourolle.paris/dtourolle/jellytau-builder:2026.08 + image: gitea.tourolle.paris/dtourolle/jellytau-builder:2026.08.1 steps: - name: Checkout repository diff --git a/Dockerfile.builder b/Dockerfile.builder index 8f581d59..a86e464d 100644 --- a/Dockerfile.builder +++ b/Dockerfile.builder @@ -141,6 +141,17 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ lld \ llvm \ nsis \ + # AppImage bundling. linuxdeploy embeds xdg-open into the AppImage and + # aborts the whole bundle if it is missing: + # failed to bundle project: xdg-open binary not found + # It is present on most desktop distros, which is why the AppImage built on + # a developer machine and failed here. desktop-file-utils and zsync are the + # other two linuxdeploy commonly wants (desktop-file-validate, and zsync for + # delta updates), added together so a missing one does not cost another + # image rebuild and another failed release build. + xdg-utils \ + desktop-file-utils \ + zsync \ && rm -rf /var/lib/apt/lists/* \ # Ubuntu's clang package ships clang but NOT the clang-cl alias that cc-rs # invokes for MSVC targets. clang-cl is the same binary in MSVC-compat mode, diff --git a/docs/build/ci-operations.md b/docs/build/ci-operations.md index fd058075..beb38ca9 100644 --- a/docs/build/ci-operations.md +++ b/docs/build/ci-operations.md @@ -61,6 +61,11 @@ filled. Keep a couple of dated tags live and prune the rest. The order matters — CI breaks if the workflow lands before the image exists. +A caveat learned the hard way: the *trailing* layer is only fast for `cargo +install` tools. Adding an **apt** package invalidates the packaging layer, which +sits above the `cargo-xwin`/`cargo-deny` installs, so those recompile too — a +~20 minute rebuild rather than ~2. + ```bash # 1. Edit Dockerfile.builder. Put new tools in the TRAILING layer: it exists so # a tool change is a ~2 min rebuild instead of ~15. @@ -103,6 +108,25 @@ transitive upgrade (bumping `tauri-plugin-log` to 2.9.0 also moved `wry`, therefore video playback. That is a change to make deliberately, with a full build and a playback check — not one to slip into a release. +## AppImage needs more than the Rust toolchain + +`linuxdeploy` (which Tauri downloads at build time to assemble the AppImage) +shells out to distro tools that a minimal server image does not have. It aborts +the whole bundle on the first one missing: + +``` +failed to bundle project: xdg-open binary not found +``` + +The image therefore carries `xdg-utils`, `desktop-file-utils` and `zsync`. This +is a class of failure that **cannot be caught by building locally**: a developer +machine is a desktop and has all three, so the AppImage builds there and fails in +CI. It cost one release build to find. + +Tauri's AppImage bundler also downloads `linuxdeploy`, `AppRun` and two plugin +scripts from GitHub during the build. That is Tauri's behaviour, not ours, but it +means an AppImage build depends on GitHub being reachable from the runner. + ## Secrets Managed with the `tea` CLI (`tea actions secrets list`) or the repo settings UI.