fix(ci): give the builder image what linuxdeploy needs for the AppImage
🏗️ Build and Test JellyTau / Run Tests (push) Successful in 15m52s
🏗️ Build and Test JellyTau / Supply Chain (push) Failing after 29s
Publish Documentation / Build & publish docs to gitea-pages (push) Successful in 5m35s
Traceability Validation / Check Requirement Traces (push) Successful in 11s
Build & Release / Run Tests (push) Successful in 14m53s
🏗️ Build and Test JellyTau / Android Compile Check (push) Successful in 4m22s
Build & Release / Build Linux (push) Successful in 20m53s
Build & Release / Build Windows (push) Successful in 15m41s
Build & Release / Build Android (push) Successful in 30m46s
Build & Release / Create Release (push) Successful in 38s

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.
This commit is contained in:
2026-08-22 02:52:32 +02:00
parent 76a2d9609b
commit 9c75e74ea3
6 changed files with 45 additions and 10 deletions
+3 -3
View File
@@ -28,7 +28,7 @@ jobs:
if: "!startsWith(github.event.head_commit.message, 'chore(release)')" if: "!startsWith(github.event.head_commit.message, 'chore(release)')"
runs-on: linux/amd64 runs-on: linux/amd64
container: container:
image: gitea.tourolle.paris/dtourolle/jellytau-builder:2026.08 image: gitea.tourolle.paris/dtourolle/jellytau-builder:2026.08.1
steps: steps:
- name: Checkout repository - name: Checkout repository
@@ -187,7 +187,7 @@ jobs:
runs-on: linux/amd64 runs-on: linux/amd64
needs: test needs: test
container: container:
image: gitea.tourolle.paris/dtourolle/jellytau-builder:2026.08 image: gitea.tourolle.paris/dtourolle/jellytau-builder:2026.08.1
env: env:
ANDROID_HOME: /opt/android-sdk ANDROID_HOME: /opt/android-sdk
ANDROID_SDK_ROOT: /opt/android-sdk ANDROID_SDK_ROOT: /opt/android-sdk
@@ -256,7 +256,7 @@ jobs:
name: Supply Chain name: Supply Chain
runs-on: linux/amd64 runs-on: linux/amd64
container: container:
image: gitea.tourolle.paris/dtourolle/jellytau-builder:2026.08 image: gitea.tourolle.paris/dtourolle/jellytau-builder:2026.08.1
steps: steps:
- name: Checkout repository - name: Checkout repository
+5 -5
View File
@@ -21,7 +21,7 @@ jobs:
name: Run Tests name: Run Tests
runs-on: linux/amd64 runs-on: linux/amd64
container: container:
image: gitea.tourolle.paris/dtourolle/jellytau-builder:2026.08 image: gitea.tourolle.paris/dtourolle/jellytau-builder:2026.08.1
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v4 uses: actions/checkout@v4
@@ -94,7 +94,7 @@ jobs:
runs-on: linux/amd64 runs-on: linux/amd64
needs: test needs: test
container: container:
image: gitea.tourolle.paris/dtourolle/jellytau-builder:2026.08 image: gitea.tourolle.paris/dtourolle/jellytau-builder:2026.08.1
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v4 uses: actions/checkout@v4
@@ -235,7 +235,7 @@ jobs:
# baked into the builder image. No toolchain installs here — the image has # baked into the builder image. No toolchain installs here — the image has
# cargo-xwin, clang/clang-cl, lld, llvm, nsis and the msvc target. # cargo-xwin, clang/clang-cl, lld, llvm, nsis and the msvc target.
container: container:
image: gitea.tourolle.paris/dtourolle/jellytau-builder:2026.08 image: gitea.tourolle.paris/dtourolle/jellytau-builder:2026.08.1
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v4 uses: actions/checkout@v4
@@ -308,7 +308,7 @@ jobs:
runs-on: linux/amd64 runs-on: linux/amd64
needs: test needs: test
container: container:
image: gitea.tourolle.paris/dtourolle/jellytau-builder:2026.08 image: gitea.tourolle.paris/dtourolle/jellytau-builder:2026.08.1
env: env:
ANDROID_HOME: /opt/android-sdk ANDROID_HOME: /opt/android-sdk
ANDROID_SDK_ROOT: /opt/android-sdk ANDROID_SDK_ROOT: /opt/android-sdk
@@ -411,7 +411,7 @@ jobs:
needs: [build-linux, build-windows, build-android] needs: [build-linux, build-windows, build-android]
if: startsWith(github.ref, 'refs/tags/v') if: startsWith(github.ref, 'refs/tags/v')
container: container:
image: gitea.tourolle.paris/dtourolle/jellytau-builder:2026.08 image: gitea.tourolle.paris/dtourolle/jellytau-builder:2026.08.1
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v4 uses: actions/checkout@v4
+1 -1
View File
@@ -21,7 +21,7 @@ jobs:
name: Build & publish docs to gitea-pages name: Build & publish docs to gitea-pages
runs-on: linux/amd64 runs-on: linux/amd64
container: container:
image: gitea.tourolle.paris/dtourolle/jellytau-builder:2026.08 image: gitea.tourolle.paris/dtourolle/jellytau-builder:2026.08.1
steps: steps:
- name: Checkout code - name: Checkout code
+1 -1
View File
@@ -17,7 +17,7 @@ jobs:
runs-on: linux/amd64 runs-on: linux/amd64
name: Check Requirement Traces name: Check Requirement Traces
container: container:
image: gitea.tourolle.paris/dtourolle/jellytau-builder:2026.08 image: gitea.tourolle.paris/dtourolle/jellytau-builder:2026.08.1
steps: steps:
- name: Checkout repository - name: Checkout repository
+11
View File
@@ -141,6 +141,17 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
lld \ lld \
llvm \ llvm \
nsis \ 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/* \ && rm -rf /var/lib/apt/lists/* \
# Ubuntu's clang package ships clang but NOT the clang-cl alias that cc-rs # 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, # invokes for MSVC targets. clang-cl is the same binary in MSVC-compat mode,
+24
View File
@@ -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. 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 ```bash
# 1. Edit Dockerfile.builder. Put new tools in the TRAILING layer: it exists so # 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. # 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 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. 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 ## Secrets
Managed with the `tea` CLI (`tea actions secrets list`) or the repo settings UI. Managed with the `tea` CLI (`tea actions secrets list`) or the repo settings UI.