From 5f395279ae72a7ad5a67a927178c7cc04a14762e Mon Sep 17 00:00:00 2001 From: Namnarak Date: Fri, 6 Nov 2026 21:15:28 +0700 Subject: [PATCH] ci: smoke test packaged Linux installers --- .github/workflows/release.yml | 43 ++++++++++--- electron/main.mjs | 7 ++- packaging/README.md | 3 +- scripts/verify-linux-packages.sh | 105 +++++++++++++++++++++++++++++++ 4 files changed, 144 insertions(+), 14 deletions(-) create mode 100755 scripts/verify-linux-packages.sh diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ab62f11..6d43996 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -24,7 +24,7 @@ jobs: with: bun-version: '1.4.4' - name: Install Electron Linux runtime dependencies - run: sudo apt-get update && sudo apt-get install -y --no-install-recommends libgtk-3-0 libnss3 libxss1 libgbm1 libasound2t64 libatk-bridge2.0-0 rpm file patchelf + run: sudo apt-get update && sudo apt-get install -y --no-install-recommends libgtk-3-0 libnss3 libxss1 libgbm1 libasound2t64 libatk-bridge2.0-0 rpm file patchelf libarchive-tools xvfb xdotool - name: Install locked dependencies run: bun install --frozen-lockfile - name: Verify tag version @@ -33,14 +33,8 @@ jobs: run: test "${GITHUB_REF_NAME#v}" = "$(node -p 'require("./package.json").version')" - name: Build engine and desktop packages run: npm run electron:build - - name: Verify desktop packages - shell: bash - run: | - set -euo pipefail - ls -lh release-electron/*.AppImage release-electron/*.deb release-electron/*.rpm - test -s release-electron/Liney-Electron-Preview-$(node -p 'require("./package.json").version')-x86_64.AppImage - test -s release-electron/Liney-Electron-Preview-$(node -p 'require("./package.json").version')-amd64.deb - test -s release-electron/Liney-Electron-Preview-$(node -p 'require("./package.json").version')-x86_64.rpm + - name: Verify and smoke-test AppImage, DEB and RPM + run: bash scripts/verify-linux-packages.sh release-electron - name: Upload desktop package artifacts uses: actions/upload-artifact@v4 with: @@ -68,7 +62,7 @@ jobs: name: liney-electron-packages-${{ github.sha }} path: release-electron - name: Install Flatpak build tools - run: sudo apt-get update && sudo apt-get install -y --no-install-recommends flatpak flatpak-builder file elfutils + run: sudo apt-get update && sudo apt-get install -y --no-install-recommends flatpak flatpak-builder file elfutils xvfb - name: Set up Flathub runtime run: | flatpak remote-add --user --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo @@ -84,6 +78,35 @@ jobs: test -s "$BUNDLE" ls -lh "$BUNDLE" flatpak build-bundle --help >/dev/null + flatpak install --user --noninteractive --assumeyes --bundle "$BUNDLE" + flatpak info --show-ref com.cyoriasmp.liney | grep -Fx "app/com.cyoriasmp.liney/x86_64/preview" + METADATA="$(flatpak info --show-metadata com.cyoriasmp.liney)" + grep -Fx 'name=com.cyoriasmp.liney' <<<"$METADATA" + grep -Fx 'runtime=org.gnome.Platform/x86_64/50' <<<"$METADATA" + grep -Fx 'command=liney-electron-preview' <<<"$METADATA" + LOG="$(mktemp)" + set +e + timeout -k 2s 60s xvfb-run -a --server-args='-screen 0 1280x800x24' \ + flatpak run --user \ + --env=LINEY_DISABLE_AUTO_RESTORE=1 \ + --env=LINEY_ELECTRON_SMOKE=1 \ + --env=LINEY_ELECTRON_SMOKE_SKIP_SHORTCUT_INPUT=1 \ + com.cyoriasmp.liney >"$LOG" 2>&1 + SMOKE_STATUS=$? + set -e + cat "$LOG" + test "$SMOKE_STATUS" -eq 0 + for marker in \ + '"authDialog":true' \ + '"logoLoaded":true' \ + '"menuHidden":true' \ + '"nativeEditMenu":true' \ + '"shortcutInputSkipped":true' \ + '"nativeHelpers":5' \ + '"enginePong":true'; do + grep -Fq "$marker" "$LOG" + done + flatpak uninstall --user --noninteractive --assumeyes com.cyoriasmp.liney - name: Upload Flatpak artifact uses: actions/upload-artifact@v4 with: diff --git a/electron/main.mjs b/electron/main.mjs index 6a02bd4..88b6af8 100644 --- a/electron/main.mjs +++ b/electron/main.mjs @@ -276,16 +276,17 @@ function createWindow(label="main", opts={}) { logoLoaded: Boolean(document.querySelector(".auth-brand img")?.naturalWidth), bridge: Boolean(window.lineyDesktop), })`); - const nativeEditShortcuts = await verifyNativeEditShortcuts(win); + const skipShortcutInput = process.env.LINEY_ELECTRON_SMOKE_SKIP_SHORTCUT_INPUT === "1"; + const nativeEditShortcuts = skipShortcutInput ? null : await verifyNativeEditShortcuts(win); await ensureEngine(); const pong=await requestEngine("ping"); const helpers=bundledHelperEnvironment(); const appMenu=Menu.getApplicationMenu(); const editMenu=appMenu?.items?.find(item=>item.label==="Edit"); const editItems=(editMenu?.submenu?.items ?? []).map(item=>({label:item.label,role:item.role,accelerator:item.accelerator,registerAccelerator:item.registerAccelerator})); - const outcome={...checks,nativeHelpers:Object.keys(helpers).length,menuHidden:!win.isMenuBarVisible(),nativeEditMenu:hasNativeEditShortcuts(appMenu),editItems,nativeEditShortcuts,enginePong:Boolean(pong),renderer:"React original",system:"Electron"}; + const outcome={...checks,nativeHelpers:Object.keys(helpers).length,menuHidden:!win.isMenuBarVisible(),nativeEditMenu:hasNativeEditShortcuts(appMenu),editItems,nativeEditShortcuts,shortcutInputSkipped:skipShortcutInput,enginePong:Boolean(pong),renderer:"React original",system:"Electron"}; console.log("LINEY_ELECTRON_SMOKE_RESULT="+JSON.stringify(outcome)); - const shortcutsPassed=Object.values(nativeEditShortcuts).every(Boolean); + const shortcutsPassed=skipShortcutInput || Object.values(nativeEditShortcuts).every(Boolean); app.exit(checks.authDialog && checks.logoLoaded && checks.bridge && outcome.nativeHelpers===5 && outcome.menuHidden && outcome.nativeEditMenu && shortcutsPassed && Boolean(pong) ? 0 : 3); }catch(e){console.error("LINEY_ELECTRON_SMOKE_ERROR="+(e?.stack || String(e)));app.exit(4);} },1000); diff --git a/packaging/README.md b/packaging/README.md index 0dd594b..940af24 100644 --- a/packaging/README.md +++ b/packaging/README.md @@ -26,6 +26,7 @@ Install the resulting `release-electron/Liney-Electron-Preview--x86_64. ```sh flatpak install --user ./release-electron/Liney-Electron-Preview--x86_64.flatpak +flatpak run com.cyoriasmp.liney ``` -The build requires `flatpak`, `flatpak-builder`, and the Flathub GNOME 50 runtime/SDK. The Flatpak application ID is `com.cyoriasmp.liney` (lowercase, standards-compliant). The Flatpak application ID is `com.cyoriasmp.liney` (lowercase, standards-compliant). The manifest grants network, desktop/audio, Secret Service, StatusNotifier, and PipeWire portal access; it does not grant unrestricted host filesystem access. +The build requires `flatpak`, `flatpak-builder`, and the Flathub GNOME 50 runtime/SDK. The Flatpak application ID is `com.cyoriasmp.liney` (lowercase, standards-compliant); the bundle uses the `preview` branch. The manifest grants network, desktop/audio, Secret Service, StatusNotifier, and PipeWire portal access; it does not grant unrestricted host filesystem access. diff --git a/scripts/verify-linux-packages.sh b/scripts/verify-linux-packages.sh new file mode 100755 index 0000000..47f76c2 --- /dev/null +++ b/scripts/verify-linux-packages.sh @@ -0,0 +1,105 @@ +#!/usr/bin/env bash +set -euo pipefail + +OUTPUT_DIR="${1:-release-electron}" +VERSION="$(node -p 'require("./package.json").version')" +APPIMAGE="$OUTPUT_DIR/Liney-Electron-Preview-${VERSION}-x86_64.AppImage" +DEB="$OUTPUT_DIR/Liney-Electron-Preview-${VERSION}-amd64.deb" +RPM="$OUTPUT_DIR/Liney-Electron-Preview-${VERSION}-x86_64.rpm" + +for tool in file dpkg-deb rpm bsdtar xvfb-run xdotool timeout ldd; do + command -v "$tool" >/dev/null || { + echo "Missing required package verification tool: $tool" >&2 + exit 1 + } +done + +for artifact in "$APPIMAGE" "$DEB" "$RPM"; do + test -s "$artifact" + file "$artifact" +done + +# Validate package metadata instead of only checking that an archive exists. +[[ "$(dpkg-deb --field "$DEB" Package)" == "liney" ]] +[[ "$(dpkg-deb --field "$DEB" Version)" == "$VERSION" ]] +[[ "$(dpkg-deb --field "$DEB" Architecture)" == "amd64" ]] +RPM_ID="$(rpm -qp --queryformat '%{VERSION} %{ARCH}' "$RPM")" +[[ "$RPM_ID" == "${VERSION} x86_64" ]] +rpm -qpl "$RPM" | grep -Fqx '/opt/Liney Electron Preview/liney' + +PACKAGE_ROOT="$(mktemp -d)" +trap 'rm -rf "$PACKAGE_ROOT"' EXIT +dpkg-deb --extract "$DEB" "$PACKAGE_ROOT/deb" +DEB_APP="$PACKAGE_ROOT/deb/opt/Liney Electron Preview/liney" +test -x "$DEB_APP" +test -s "$PACKAGE_ROOT/deb/opt/Liney Electron Preview/resources/app.asar" +test -x "$PACKAGE_ROOT/deb/opt/Liney Electron Preview/resources/app.asar.unpacked/electron/native/liney-media" +grep -Fqx 'Exec="/opt/Liney Electron Preview/liney" %U' \ + "$PACKAGE_ROOT/deb/usr/share/applications/dev.cytechteam.liney.electron-preview.desktop" + +mkdir -p "$PACKAGE_ROOT/rpm" +bsdtar -xf "$RPM" -C "$PACKAGE_ROOT/rpm" +RPM_APP="$PACKAGE_ROOT/rpm/opt/Liney Electron Preview/liney" +test -x "$RPM_APP" +test -s "$PACKAGE_ROOT/rpm/opt/Liney Electron Preview/resources/app.asar" + +for payload in deb rpm; do + NATIVE="$PACKAGE_ROOT/$payload/opt/Liney Electron Preview/resources/app.asar.unpacked/electron/native" + for helper in liney-media liney-audio liney-screen liney-screen-wayland liney-screen-x11; do + test -x "$NATIVE/$helper" + ! ldd "$NATIVE/$helper" | grep -q 'not found' + done + "$NATIVE/liney-media" --liney-keyring-probe | grep -q '"available":' +done + +run_smoke() { + local label="$1" + local logfile="$PACKAGE_ROOT/${label}.log" + shift + local data_dir="$PACKAGE_ROOT/data-${label}" + mkdir -m 700 "$data_dir" + + echo "=== Smoke test: ${label} ===" + set +e + LINEY_DATA_DIR="$data_dir" \ + LINEY_DISABLE_AUTO_RESTORE=1 \ + LINEY_ELECTRON_SMOKE=1 \ + timeout -k 2s 60s xvfb-run -a --server-args='-screen 0 1280x800x24' \ + "$@" --no-sandbox >"$logfile" 2>&1 + local status=$? + set -e + cat "$logfile" + if [[ "$status" -ne 0 ]]; then + echo "${label} smoke test exited with status ${status}" >&2 + return "$status" + fi + + for marker in \ + '"authDialog":true' \ + '"logoLoaded":true' \ + '"menuHidden":true' \ + '"nativeEditMenu":true' \ + '"selectAll":true' \ + '"copy":true' \ + '"cut":true' \ + '"paste":true' \ + '"nativeHelpers":5' \ + '"enginePong":true'; do + grep -Fq "$marker" "$logfile" || { + echo "${label} smoke test did not report ${marker}" >&2 + return 1 + } + done +} + +# Exercise the real AppImage launcher without requiring FUSE. +run_smoke appimage "$APPIMAGE" --appimage-extract-and-run + +# Exercise the executable and resources from the actual DEB archive without +# installing anything into the CI runner. +run_smoke deb "$DEB_APP" + +# Extract the RPM payload without installing it into the CI runner. +run_smoke rpm "$RPM_APP" + +echo "All AppImage, DEB and RPM package checks passed for ${VERSION}."