Files
amethyst/.github/workflows/smoke-test-desktop.yml
Claude 26318792a3 feat(desktop): draw natively on Wayland in every Linux release
The Linux packages ran on X11 only: the bundled Temurin runtime's AWT has
no Wayland toolkit, and the Flatpak required --socket=x11. On a Wayland
session the app needed XWayland, and with no Xwayland it could not start.

- Linux packages now bundle the JetBrains Runtime 21, whose AWT has a
  Wayland toolkit (WLToolkit). CI's Linux legs install it with
  actions/setup-java (distribution: jetbrains), and release CI fails a
  Linux build whose runtime lacks libawt_wlawt.so. macOS and Windows keep
  Temurin.
- WaylandDisplay picks WLToolkit in main() when $WAYLAND_DISPLAY names a
  live socket and the runtime has it, and sets the Wayland app_id
  ($FLATPAK_ID, else "Amethyst", which the desktop entries already match).
  AMETHYST_X11=1 keeps a Wayland session on XWayland.
- Compose's Window can't draw under WLToolkit (Skiko's surface locks the
  window through JAWT for an X11 handle), so AppWindow hosts the main
  window and the fullscreen video window in a Swing frame with a
  ComposePanel that draws through Swing on Skiko's CPU renderer, carrying
  the WindowState both ways, the close request, window-wide shortcuts and
  the crash window. On X11, macOS and Windows it is Compose's Window, as
  before. The crash dialog follows the same split.
- JBR's Wayland toolkit detaches every window from a shutdown hook off
  the event thread, which Compose rejects: each logout or kill left a
  crash report. The Wayland frames skip that teardown off the event
  thread.
- Flatpak: --socket=wayland + --socket=fallback-x11 instead of x11.
- .rpm requires libwayland-client, libwayland-cursor and libxkbcommon;
  scripts/add-deb-wayland-deps.sh adds libxkbcommon0 (opened with dlopen)
  to the .deb.
- Compose marks its jlink/jpackage tasks' JDK @Internal, so the build
  cache restored a Temurin runtime image into a JBR build. The JDK's
  release file is now a task input.
- scripts/wayland-smoke-test.sh starts a packaged app on a headless sway
  with Xwayland off and passes only on a native Wayland window; the PR
  .deb smoke test runs it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UEcMZXsXFEWpfxgSMAgFKq
2026-10-13 03:43:41 +00:00

223 lines
9.0 KiB
YAML

name: Desktop Smoke Test
on:
workflow_dispatch:
pull_request:
paths:
- 'desktopApp/**'
- '.github/workflows/smoke-test-desktop.yml'
# The keyring self-test exercises these, and the libraries they pin.
- 'commons/src/jvmMain/kotlin/com/vitorpamplona/amethyst/commons/keystorage/**'
- 'scripts/keyring-self-test.sh'
- 'gradle/libs.versions.toml'
permissions:
contents: read
concurrency:
group: smoke-desktop-${{ github.ref }}
cancel-in-progress: true
jobs:
# -------------------------------------------------------------------------
# 1) Compose UI test — verifies the composable tree renders (login screen)
# under the dev classpath. Catches missing string resources, broken
# composables, and basic dependency-graph issues.
# -------------------------------------------------------------------------
compose-ui-test:
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- name: Checkout code
uses: actions/checkout@v7
- name: Set up JDK 21
uses: actions/setup-java@v6.0.1
with:
distribution: 'temurin'
java-version: 21
- name: Set up Gradle
uses: gradle/actions/setup-gradle@v6
with:
cache-read-only: true
- name: Install xvfb
run: sudo apt-get update && sudo apt-get install -y xvfb
- name: Run desktop tests (including UI smoke test)
run: xvfb-run --auto-servernum ./gradlew :desktopApp:test
# -------------------------------------------------------------------------
# 2) Release .deb build + launch — builds the ProGuard'd, jlink'd .deb
# package, installs it, and verifies the process stays alive for 10s.
# Catches ProGuard stripping (JNI, reflection), missing jlink modules
# (java.management, java.prefs), and native lib bundling issues.
#
# Runs on both x64 and arm64 hosted runners so release-time arm64 breakage
# (e.g. ProGuard rules missing an arch-specific reflection root) is caught
# at PR time instead of on the tag build.
# -------------------------------------------------------------------------
release-deb-launch:
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, ubuntu-24.04-arm]
runs-on: ${{ matrix.os }}
timeout-minutes: 45
steps:
- name: Checkout code
uses: actions/checkout@v7
# As in create-release.yml: Linux packages bundle the JetBrains Runtime (native Wayland).
- name: Set up JDK 21
uses: actions/setup-java@v6.0.1
with:
distribution: 'jetbrains'
java-version: 21
- name: Set up Gradle
uses: gradle/actions/setup-gradle@v6
with:
cache-read-only: true
- name: Install xvfb + packaging deps
# gnome-keyring + dbus: the keyring self-test below needs a Secret Service session.
# sway: the Wayland-only launch below.
run: sudo apt-get update && sudo apt-get install -y xvfb fakeroot gnome-keyring dbus sway
- name: Build release .deb
uses: nick-fields/retry@ad984534de44a9489a53aefd81eb77f87c70dc60 # v4.0.0
with:
max_attempts: 2
timeout_minutes: 20
command: ./gradlew --no-daemon :desktopApp:packageReleaseDeb
- name: Relax build-host-pinned deps
run: |
set -euo pipefail
chmod +x scripts/relax-deb-libicu.sh
scripts/relax-deb-libicu.sh desktopApp/build/compose/binaries/main-release/deb/*.deb
# Mirrors the same step in create-release.yml so this job exercises the
# exact .deb release ships. libskiko-linux-arm64.so has libEGL.so.1 in
# DT_NEEDED and jpackage does not scan lib/app/ for Depends, so without
# this the arm64 app dies at startup with
# UnsatisfiedLinkError: libEGL.so.1: cannot open shared object file
# See scripts/add-deb-libegl-dep.sh for the full rationale.
- name: Add libegl1 dep to arm64 .deb
run: |
set -euo pipefail
chmod +x scripts/add-deb-libegl-dep.sh
scripts/add-deb-libegl-dep.sh desktopApp/build/compose/binaries/main-release/deb/*.deb
# Also mirrors create-release.yml, so the install below resolves the same Depends.
- name: Add GStreamer deps to .deb
run: |
set -euo pipefail
chmod +x scripts/add-deb-gstreamer-deps.sh
scripts/add-deb-gstreamer-deps.sh desktopApp/build/compose/binaries/main-release/deb/*.deb
# Also mirrors create-release.yml: the bundled JBR opens libxkbcommon with dlopen.
- name: Add Wayland deps to .deb
run: |
set -euo pipefail
chmod +x scripts/add-deb-wayland-deps.sh
scripts/add-deb-wayland-deps.sh desktopApp/build/compose/binaries/main-release/deb/*.deb
# Also mirrors create-release.yml. Without it, jpackage's postinst runs
# xdg-desktop-menu, which fails here as on any system without menu
# directories ("No writable system menu directory found").
- name: Fix desktop entry in .deb
run: |
set -euo pipefail
chmod +x scripts/fix-deb-desktop-entry.sh
scripts/fix-deb-desktop-entry.sh desktopApp/build/compose/binaries/main-release/deb/*.deb
- name: Install .deb
run: |
set -euo pipefail
# Installed via apt (not `dpkg -i`) so the .deb's declared Depends are
# actually resolved — that is what pulls in libegl1 on the arm64
# runner, which does not ship it preinstalled. A failed install,
# maintainer scripts included, fails the job.
sudo apt-get install -y ./desktopApp/build/compose/binaries/main-release/deb/*.deb
dpkg-query -W -f='${Status}\n' amethyst | grep -qx 'install ok installed'
test -f /usr/share/applications/amethyst-Amethyst.desktop
echo "Installed files:"
dpkg -L amethyst | sed -n '1,30p'
# Fail if the binary wasn't actually extracted
test -d /opt/amethyst || test -d /opt/Amethyst || {
echo "FAIL: /opt/amethyst not found after dpkg -i"
exit 1
}
- name: Smoke test — app launches and stays alive
run: |
set -euo pipefail
# Find the launcher binary
LAUNCHER=$(find /opt -name "Amethyst" -type f -executable 2>/dev/null | head -1)
if [[ -z "$LAUNCHER" ]]; then
# Fallback: search dpkg file list
LAUNCHER=$(dpkg -L amethyst | grep -E '/bin/[Aa]methyst$' | head -1)
fi
if [[ -z "$LAUNCHER" ]]; then
echo "FAIL: could not find Amethyst launcher binary"
dpkg -L amethyst
exit 1
fi
echo "Launcher: $LAUNCHER"
# Launch under xvfb with a timeout safety net
xvfb-run --auto-servernum timeout 30 "$LAUNCHER" &
APP_PID=$!
echo "PID: $APP_PID"
# Wait 10s — if the process is still alive, the app launched successfully
sleep 10
if kill -0 "$APP_PID" 2>/dev/null; then
echo "PASS: Application launched and stayed alive for 10s"
kill "$APP_PID" || true
wait "$APP_PID" 2>/dev/null || true
else
wait "$APP_PID" 2>/dev/null
EXIT_CODE=$?
echo "FAIL: Application exited with code $EXIT_CODE within 10s"
exit 1
fi
# Wayland-only session (sway, Xwayland off): the app must open a native Wayland window,
# which needs the bundled JetBrains Runtime and the app picking its Wayland toolkit.
- name: Smoke test - app opens a native Wayland window
run: |
set -euo pipefail
LAUNCHER=$(find /opt -name "Amethyst" -type f -executable 2>/dev/null | head -1)
if [[ -z "$LAUNCHER" ]]; then
LAUNCHER=$(dpkg -L amethyst | grep -E '/bin/[Aa]methyst$' | head -1)
fi
[[ -n "$LAUNCHER" ]] || { echo "FAIL: could not find Amethyst launcher binary"; exit 1; }
bash scripts/wayland-smoke-test.sh "$LAUNCHER"
# The release build must keep keys in the OS keyring: ProGuard once stripped the D-Bus
# transport and broke a signed jar, so every Linux release silently stopped using it.
# See commons/plans/2026-10-12-desktop-key-storage.md.
- name: Keyring self-test - the installed app keeps keys in GNOME Keyring
run: |
set -euo pipefail
LAUNCHER=$(find /opt -name "Amethyst" -type f -executable 2>/dev/null | head -1)
if [[ -z "$LAUNCHER" ]]; then
LAUNCHER=$(dpkg -L amethyst | grep -E '/bin/[Aa]methyst$' | head -1)
fi
[[ -n "$LAUNCHER" ]] || { echo "FAIL: could not find Amethyst launcher binary"; exit 1; }
bash scripts/keyring-self-test.sh "$LAUNCHER"
- name: Upload .deb artifact (for manual testing)
if: always()
uses: actions/upload-artifact@v7
with:
# Artifact names must be unique across a run — disambiguate per arch.
name: Release DEB (smoke-tested, ${{ matrix.os }})
path: desktopApp/build/compose/binaries/main-release/deb/*.deb