feat: ship a Flatpak bundle of the desktop app on release CI

The linux-portable release leg now wraps the createReleaseDistributable
tree it already builds into a single-file Flatpak bundle and attaches it
to the GH Release as amethyst-desktop-<version>-linux-x64.flatpak.

Packaging fixes to the existing (previously unwired) Flathub manifest:
- add the missing 512x512 icon (copy of desktopApp icon.png) and install
  it under hicolor/512x512 instead of the never-present 256px path
- drop the openjdk module + sdk-extension: the jpackage tree bundles its
  own trimmed JRE, so /app/jre was pure bloat
- grant --socket=x11 instead of wayland/fallback-x11: Compose Desktop
  renders via AWT/skiko (X11-only on Linux, XWayland on Wayland), so
  fallback-x11 left the app socketless on Wayland sessions

CI wiring:
- install flatpak tooling + the freedesktop runtime/sdk (version greped
  from the manifest so the pin can't drift), retried like other fetches
- inject the AppStream <release> entry for the tagged version at build
  time (the checked-in metainfo deliberately carries none)
- flatpak-builder with --disable-rofiles-fuse (GH runners) and a
  --state-dir under desktopApp/build so the repo tree stays clean
- collect via the existing asset-name.sh contract (new flatpak ext)

Verified end-to-end locally: built the bundle from the manifest with a
stubbed jpackage tree, installed it, and ran the exported command inside
the sandbox (freedesktop 24.08, args forwarded through the wrapper).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GYfNxhPZC3WRrn82Dm2Cb4
This commit is contained in:
Claude
2026-07-14 14:06:37 +00:00
parent d043f4d143
commit 7e78204336
7 changed files with 144 additions and 42 deletions

View File

@@ -44,7 +44,7 @@ jobs:
- { os: ubuntu-latest, arch: x64, family: linux, tasks: "packageReleaseDeb packageReleaseRpm" }
- { os: ubuntu-latest, arch: x64, family: linux-portable, tasks: "createReleaseAppImage createReleaseDistributable" }
runs-on: ${{ matrix.os }}
timeout-minutes: 45
timeout-minutes: 60 # linux-portable leg also downloads the freedesktop runtime + builds the Flatpak bundle
defaults:
run:
shell: bash
@@ -101,6 +101,25 @@ jobs:
fi
chmod +x desktopApp/packaging/appimage/appimagetool-x86_64.AppImage
# Flatpak tooling + the freedesktop runtime/sdk the manifest pins
# (runtime-version is greped from the manifest so this never drifts).
# Retried: the runtime download from Flathub is ~1 GB and flatpak
# install resumes cleanly on re-run.
- name: Install Flatpak tooling + runtimes (linux-portable only)
if: matrix.family == 'linux-portable'
uses: nick-fields/retry@ad984534de44a9489a53aefd81eb77f87c70dc60 # v4.0.0
with:
max_attempts: 3
timeout_minutes: 15
command: |
set -euo pipefail
sudo apt-get update && sudo apt-get install -y flatpak flatpak-builder
flatpak remote-add --user --if-not-exists flathub https://dl.flathub.org/repo/flathub.flatpakrepo
FDO_VER=$(grep -E "^runtime-version:" desktopApp/packaging/flatpak/com.vitorpamplona.amethyst.Desktop.yml | cut -d"'" -f2)
flatpak install --user --noninteractive flathub \
"org.freedesktop.Platform//${FDO_VER}" \
"org.freedesktop.Sdk//${FDO_VER}"
# macOS only: import the Developer ID Application cert into a throwaway
# keychain so jpackage's codesign pass can find it. Soft — if the
# MAC_CERTIFICATE_P12 secret isn't set (forks, or before Apple creds are
@@ -161,6 +180,36 @@ jobs:
( cd "$APP" && tar czf "../../../../portable/amethyst-desktop-${VER}-linux-x64.tar.gz" Amethyst/ )
fi
# Flatpak bundle: wraps the same createReleaseDistributable tree the
# AppImage uses. The manifest (desktopApp/packaging/flatpak/) copies the
# prebuilt jpackage tree into /app — no Gradle runs inside the sandbox.
# build-bundle emits a single-file .flatpak whose baked-in runtime-repo
# lets the user's flatpak fetch the freedesktop runtime from Flathub on
# install. --disable-rofiles-fuse: GH runners lack a usable rofiles-fuse.
- name: Build Flatpak bundle (linux-portable only)
if: matrix.family == 'linux-portable'
run: |
set -euo pipefail
VER="${{ steps.ver.outputs.version }}"
PKG="desktopApp/packaging/flatpak"
APP_ID="com.vitorpamplona.amethyst.Desktop"
OUT="desktopApp/build/flatpak"
# Inject the AppStream <release> entry for this build (the checked-in
# metainfo deliberately carries none — CI is the source of truth).
sed -i "s|<releases>|<releases>\n <release version=\"${VER}\" date=\"$(date -u +%F)\" />|" \
"${PKG}/${APP_ID}.metainfo.xml"
mkdir -p "$OUT"
flatpak-builder --user --force-clean --disable-rofiles-fuse \
--state-dir="${OUT}/.flatpak-builder" \
--repo="${OUT}/repo" \
"${OUT}/build-dir" \
"${PKG}/${APP_ID}.yml"
flatpak build-bundle "${OUT}/repo" \
"${OUT}/Amethyst-${VER}-x86_64.flatpak" \
"$APP_ID" \
--runtime-repo=https://dl.flathub.org/repo/flathub.flatpakrepo
ls -la "$OUT"
- name: Collect + rename assets
run: |
set -euo pipefail

5
.gitignore vendored
View File

@@ -180,6 +180,11 @@ desktopApp/src/jvmMain/appResources/*/ffmpeg/*
desktopApp/packaging/appimage/appimagetool-x86_64.AppImage
desktopApp/packaging/appimage/squashfs-root/
# flatpak-builder state/cache from local builds (CI uses --state-dir under desktopApp/build/)
.flatpak-builder/
desktopApp/packaging/flatpak/build-dir/
desktopApp/packaging/flatpak/repo/
# Git worktrees
.worktrees/
.claude/worktrees/

View File

@@ -37,7 +37,9 @@ Platform-specific:
- **macOS**: Xcode Command Line Tools (`xcode-select --install`)
- **Windows**: WiX Toolset 3.x on PATH (for MSI). `winget install WiXToolset.WiXToolset`
- **Linux (all)**: nothing extra for `.deb`; `rpm` + `fakeroot` for `.rpm`;
`appimagetool` + `desktop-file-utils` for AppImage
`appimagetool` + `desktop-file-utils` for AppImage; `flatpak` +
`flatpak-builder` for the Flatpak bundle (see
[`desktopApp/packaging/flatpak/README.md`](desktopApp/packaging/flatpak/README.md))
Install Linux RPM tooling:
@@ -109,6 +111,7 @@ are **not** required to build Amethyst from the committed sources.
| Linux `.deb` | `./gradlew :desktopApp:packageReleaseDeb` | `desktopApp/build/compose/binaries/main-release/deb/amethyst_*.deb` |
| Linux `.rpm` | `./gradlew :desktopApp:packageReleaseRpm` | `desktopApp/build/compose/binaries/main-release/rpm/amethyst-*.rpm` |
| Linux AppImage | `./gradlew :desktopApp:createReleaseAppImage` | `desktopApp/build/appimage/Amethyst-*-x86_64.AppImage` |
| Linux Flatpak | `flatpak-builder` over `createReleaseDistributable` output — see [`desktopApp/packaging/flatpak/README.md`](desktopApp/packaging/flatpak/README.md) | `desktopApp/build/flatpak/Amethyst-*-x86_64.flatpak` (CI) |
| Windows `.zip` portable | See below (inline `7z`) | — |
| Linux `.tar.gz` portable | See below (inline `tar`) | — |
@@ -148,7 +151,7 @@ Where:
| `<version>` | Tag stripped of leading `vX.YY.ZZ` |
| `<family>` | `macos`, `windows`, `linux` |
| `<arch>` | `x64`, `arm64` |
| `<ext>` | `dmg`, `msi`, `zip`, `deb`, `rpm`, `AppImage`, `tar.gz` |
| `<ext>` | `dmg`, `msi`, `zip`, `deb`, `rpm`, `AppImage`, `flatpak`, `tar.gz` |
Single source of truth: [`scripts/asset-name.sh`](scripts/asset-name.sh).
Package manager manifests (Homebrew cask, Winget) depend on this exact scheme —
@@ -160,6 +163,7 @@ Examples:
- `amethyst-desktop-1.12.1-macos-arm64.dmg`
- `amethyst-desktop-1.12.1-windows-x64.msi`
- `amethyst-desktop-1.12.1-linux-x64.AppImage`
- `amethyst-desktop-1.12.1-linux-x64.flatpak`
---
@@ -625,12 +629,18 @@ State is shared across install channels (DMG, Homebrew, MSI, Winget, .deb,
expose downgrade migration risks — **prefer a single install channel per
machine**.
**Exception: Flatpak.** The sandbox redirects XDG dirs into
`~/.var/app/com.vitorpamplona.amethyst.Desktop/`, so a Flatpak install keeps
its own separate state and does not see (or risk downgrading) state written
by any other channel.
| OS | App location | State directories |
|---|---|---|
| macOS | `/Applications/Amethyst.app` | `~/Library/Application Support/Amethyst`<br>`~/Library/Preferences/com.vitorpamplona.amethyst.desktop.plist`<br>`~/Library/Caches/Amethyst` |
| Windows | `%LOCALAPPDATA%\Amethyst` or `C:\Program Files\Amethyst` | `%APPDATA%\Amethyst`<br>`%LOCALAPPDATA%\Amethyst` |
| Linux (deb/rpm) | `/opt/amethyst` | `~/.config/amethyst`<br>`~/.local/share/amethyst`<br>`~/.cache/amethyst` |
| Linux (AppImage/tar.gz) | user-chosen | Same as above |
| Linux (Flatpak) | `/var/lib/flatpak` or `~/.local/share/flatpak` | `~/.var/app/com.vitorpamplona.amethyst.Desktop/` |
Uninstall:
@@ -639,6 +649,8 @@ Uninstall:
- .deb: `sudo apt remove amethyst`
- .rpm: `sudo dnf remove amethyst`
- AppImage / tar.gz: delete the file / extracted directory
- Flatpak: `flatpak uninstall com.vitorpamplona.amethyst.Desktop` (add
`--delete-data` to also remove `~/.var/app/…`)
- macOS `.dmg`: drag from `/Applications` to Trash, then delete state dirs manually
---

View File

@@ -1,44 +1,65 @@
# Flathub packaging for Amethyst Desktop
# Flatpak packaging for Amethyst Desktop
This directory contains the Flatpak manifest and associated metadata for
publishing Amethyst Desktop on Flathub.
This directory contains the Flatpak manifest and associated metadata. It is
used two ways:
1. **Release CI** (`.github/workflows/create-release.yml`, `linux-portable`
leg) builds a single-file bundle from it on every tag and attaches it to
the GitHub Release as `amethyst-desktop-<version>-linux-x64.flatpak`.
2. **Flathub submission** (future) — the same manifest is the starting
point, but Flathub requires building from downloadable sources instead of
the local `type: dir` tree, so it will need a source rewrite at
submission time.
## Files
- `com.vitorpamplona.amethyst.Desktop.yml` — Flatpak manifest
- `com.vitorpamplona.amethyst.Desktop.metainfo.xml` — AppStream metadata
(categories, license, screenshots — needs screenshots added before
submission)
- `com.vitorpamplona.amethyst.Desktop.yml` — Flatpak manifest. It packages
the **prebuilt** jpackage tree from
`./gradlew :desktopApp:createReleaseDistributable` (which bundles its own
trimmed JRE — that's why there is no openjdk module/sdk-extension).
- `com.vitorpamplona.amethyst.Desktop.metainfo.xml` — AppStream metadata.
Release CI injects the `<release>` entry for the version being built
(the checked-in file deliberately carries none); screenshots still need
to be added before any Flathub submission.
- `com.vitorpamplona.amethyst.Desktop.desktop` — XDG desktop entry
- `icons/256/com.vitorpamplona.amethyst.Desktop.png`TODO: copy a 256x256
PNG icon from `desktopApp/src/jvmMain/resources/icon.png` before
submission
## Build prerequisites
- `./gradlew :desktopApp:createReleaseDistributable` — produces
`desktopApp/build/compose/binaries/main-release/app/Amethyst/`
- `flatpak install org.freedesktop.Platform//24.08 org.freedesktop.Sdk//24.08 org.freedesktop.Sdk.Extension.openjdk21//24.08`
- `icons/512/com.vitorpamplona.amethyst.Desktop.png`512x512 icon (copy of
`desktopApp/src/jvmMain/resources/icon.png`)
## Local build
```bash
# 1. Build the app tree the manifest packages
./gradlew :desktopApp:createReleaseDistributable
# 2. Tooling + Flathub remote (one-time)
sudo apt-get install -y flatpak flatpak-builder # or distro equivalent
flatpak remote-add --user --if-not-exists flathub https://dl.flathub.org/repo/flathub.flatpakrepo
# 3. Build + install locally
cd desktopApp/packaging/flatpak
flatpak-builder --user --install --force-clean build-dir com.vitorpamplona.amethyst.Desktop.yml
flatpak-builder --user --install --install-deps-from=flathub --force-clean \
build-dir com.vitorpamplona.amethyst.Desktop.yml
flatpak run com.vitorpamplona.amethyst.Desktop
```
## Submission to Flathub
To produce the distributable single-file bundle instead (what CI ships):
Follow https://docs.flathub.org/docs/for-app-authors/submission
```bash
flatpak-builder --user --install-deps-from=flathub --force-clean \
--repo=repo build-dir com.vitorpamplona.amethyst.Desktop.yml
flatpak build-bundle repo amethyst.flatpak com.vitorpamplona.amethyst.Desktop \
--runtime-repo=https://dl.flathub.org/repo/flathub.flatpakrepo
```
1. Fork `flathub/flathub` on GitHub.
2. Branch from `new-pr` (NOT `master`).
3. Copy this manifest + AppStream + desktop into a new directory matching
the app id.
4. Open a PR titled "Add com.vitorpamplona.amethyst.Desktop".
5. After merge, a per-app repo is created with write access for ongoing
updates.
Installing the bundle: `flatpak install --user ./amethyst.flatpak`. The
`--runtime-repo` baked in above lets flatpak fetch the freedesktop runtime
from Flathub automatically on the user's machine.
## Sandbox notes
- `--socket=x11` (not wayland/fallback-x11): Compose Desktop renders through
AWT/skiko, which is X11-only on Linux and runs under XWayland on Wayland
sessions.
## Codec coverage
@@ -47,6 +68,19 @@ Follow https://docs.flathub.org/docs/for-app-authors/submission
- HLS, H.264, AAC, MP3, Opus: covered by the GStreamer plugin set in
`org.freedesktop.Platform 24.08` itself.
## Submission to Flathub
Follow https://docs.flathub.org/docs/for-app-authors/submission
1. Fork `flathub/flathub` on GitHub.
2. Branch from `new-pr` (NOT `master`).
3. Copy this manifest + AppStream + desktop into a new directory matching
the app id, and rework the `type: dir` source into a
buildable-from-source or downloadable-artifact form per Flathub policy.
4. Open a PR titled "Add com.vitorpamplona.amethyst.Desktop".
5. After merge, a per-app repo is created with write access for ongoing
updates.
## License metadata
The manifest declares the binary as

View File

@@ -8,8 +8,6 @@ app-id: com.vitorpamplona.amethyst.Desktop
runtime: org.freedesktop.Platform
runtime-version: '24.08'
sdk: org.freedesktop.Sdk
sdk-extensions:
- org.freedesktop.Sdk.Extension.openjdk21
command: amethyst-desktop
add-extensions:
@@ -26,8 +24,11 @@ cleanup-commands:
finish-args:
- --share=network
- --share=ipc
- --socket=fallback-x11
- --socket=wayland
# Compose Desktop renders through AWT/skiko, which is X11-only on Linux
# (runs under XWayland on Wayland sessions). fallback-x11 + wayland would
# leave the app without a usable display socket on Wayland sessions, so
# grant x11 unconditionally.
- --socket=x11
- --socket=pulseaudio
- --device=dri
- --filesystem=xdg-download
@@ -37,12 +38,10 @@ finish-args:
# GStreamer plugin/cache paths (org.freedesktop.Platform exposes them by default).
- --env=GST_PLUGIN_SYSTEM_PATH=/usr/lib/x86_64-linux-gnu/gstreamer-1.0
# No openjdk module / sdk-extension: the jpackage tree copied below already
# bundles its own trimmed JRE under Amethyst/lib/runtime/, so installing a
# second JRE at /app/jre would only bloat the bundle.
modules:
- name: openjdk
buildsystem: simple
build-commands:
- /usr/lib/sdk/openjdk21/install.sh
- name: amethyst-desktop
buildsystem: simple
build-commands:
@@ -53,7 +52,7 @@ modules:
- install -Dm755 amethyst-desktop.sh /app/bin/amethyst-desktop
- install -Dm644 com.vitorpamplona.amethyst.Desktop.metainfo.xml -t /app/share/metainfo/
- install -Dm644 com.vitorpamplona.amethyst.Desktop.desktop -t /app/share/applications/
- install -Dm644 icons/256/com.vitorpamplona.amethyst.Desktop.png -t /app/share/icons/hicolor/256x256/apps/
- install -Dm644 icons/512/com.vitorpamplona.amethyst.Desktop.png -t /app/share/icons/hicolor/512x512/apps/
sources:
# Built artifact from `./gradlew :desktopApp:createReleaseDistributable`.
# Path matches Compose Multiplatform 1.11's output layout.
@@ -73,5 +72,6 @@ modules:
- type: file
path: com.vitorpamplona.amethyst.Desktop.desktop
- type: file
path: icons/256/com.vitorpamplona.amethyst.Desktop.png
- type: dir
path: icons
dest: icons

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View File

@@ -8,7 +8,7 @@
# <version> = tag stripped of leading 'v' (e.g. "1.08.0")
# <family> = macos | windows | linux
# <arch> = x64 | arm64
# <ext> = dmg | msi | zip | deb | rpm | AppImage | tar.gz
# <ext> = dmg | msi | zip | deb | rpm | AppImage | flatpak | tar.gz
#
# Consumed by: .github/workflows/create-release.yml, .github/workflows/bump-*.yml,
# BUILDING.md, local release runbooks.
@@ -26,6 +26,7 @@
# amethyst-desktop-1.08.0-linux-x64.deb
# amethyst-desktop-1.08.0-linux-x64.rpm
# amethyst-desktop-1.08.0-linux-x64.AppImage
# amethyst-desktop-1.08.0-linux-x64.flatpak
# amethyst-desktop-1.08.0-linux-x64.tar.gz
# amy-1.08.0-macos-arm64.tar.gz
# amy-1.08.0-macos-x64.tar.gz
@@ -74,6 +75,7 @@ collect_assets() {
desktopApp/build/compose/binaries/main-release/deb/*.deb \
desktopApp/build/compose/binaries/main-release/rpm/*.rpm \
desktopApp/build/appimage/*.AppImage \
desktopApp/build/flatpak/*.flatpak \
desktopApp/build/portable/*.tar.gz \
desktopApp/build/portable/*.zip \
; do