From 1acbf5ba45d1b1eef8a86f42a0503e597e33437e Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Fri, 17 Apr 2026 23:26:24 +0300 Subject: [PATCH] feat(electron): generate appimage (closes #409) --- .github/actions/build-electron/action.yml | 18 +++- apps/desktop/scripts/build-appimage.sh | 101 ++++++++++++++++++++++ 2 files changed, 118 insertions(+), 1 deletion(-) create mode 100755 apps/desktop/scripts/build-appimage.sh diff --git a/.github/actions/build-electron/action.yml b/.github/actions/build-electron/action.yml index 58fa9743a5..d4dfc5b570 100644 --- a/.github/actions/build-electron/action.yml +++ b/.github/actions/build-electron/action.yml @@ -66,12 +66,20 @@ runs: if: ${{ inputs.os == 'linux' }} shell: ${{ inputs.shell }} run: | - sudo apt-get update && sudo apt-get install rpm flatpak-builder elfutils + sudo apt-get update && sudo apt-get install rpm flatpak-builder elfutils libfuse2 flatpak remote-add --user --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo FLATPAK_ARCH=$(if [[ ${{ inputs.arch }} = 'arm64' ]]; then echo 'aarch64'; else echo 'x86_64'; fi) FLATPAK_VERSION='24.08' flatpak install --user --no-deps --arch $FLATPAK_ARCH --assumeyes runtime/org.freedesktop.Platform/$FLATPAK_ARCH/$FLATPAK_VERSION runtime/org.freedesktop.Sdk/$FLATPAK_ARCH/$FLATPAK_VERSION org.electronjs.Electron2.BaseApp/$FLATPAK_ARCH/$FLATPAK_VERSION + - name: Install appimagetool + if: ${{ inputs.os == 'linux' }} + shell: ${{ inputs.shell }} + run: | + APPIMAGETOOL_ARCH=$(if [[ ${{ inputs.arch }} = 'arm64' ]]; then echo 'aarch64'; else echo 'x86_64'; fi) + wget -q "https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-${APPIMAGETOOL_ARCH}.AppImage" -O /usr/local/bin/appimagetool + chmod +x /usr/local/bin/appimagetool + - name: Update build info shell: ${{ inputs.shell }} run: pnpm run chore:update-build-info @@ -90,6 +98,14 @@ runs: TARGET_ARCH: ${{ inputs.arch }} run: pnpm run --filter desktop electron-forge:make --arch=${{ inputs.arch }} --platform=${{ inputs.forge_platform }} + - name: Build AppImage + if: ${{ inputs.os == 'linux' }} + shell: ${{ inputs.shell }} + env: + TRILIUM_ARTIFACT_NAME_HINT: TriliumNotes-${{ github.ref_name }}-${{ inputs.os }}-${{ inputs.arch }} + APPIMAGE_EXTRACT_AND_RUN: "1" + run: bash apps/desktop/scripts/build-appimage.sh ${{ inputs.arch }} + # Add DMG signing step - name: Sign DMG if: inputs.os == 'macos' diff --git a/apps/desktop/scripts/build-appimage.sh b/apps/desktop/scripts/build-appimage.sh new file mode 100755 index 0000000000..d4016ce134 --- /dev/null +++ b/apps/desktop/scripts/build-appimage.sh @@ -0,0 +1,101 @@ +#!/usr/bin/env bash +# +# Build an AppImage from the packaged Electron app. +# +# Usage: ./build-appimage.sh [arch] +# arch: x64 or arm64 (default: x64) +# +# Prerequisites: +# - The Electron app must already be packaged via `electron-forge make` or `electron-forge package` +# - appimagetool must be available in PATH +# +# Environment variables: +# TRILIUM_ARTIFACT_NAME_HINT: If set, used as the base name for the output file + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +DESKTOP_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" +FORGE_DIR="$DESKTOP_DIR/electron-forge" + +ARCH="${1:-x64}" +EXECUTABLE_NAME="trilium" +PRODUCT_NAME="Trilium Notes" + +# Map architecture names +case "$ARCH" in + x64) APPIMAGE_ARCH="x86_64" ;; + arm64) APPIMAGE_ARCH="aarch64" ;; + *) echo "Unsupported architecture: $ARCH"; exit 1 ;; +esac + +# Find the packaged app directory +PACKAGED_DIR="$DESKTOP_DIR/out/$PRODUCT_NAME-linux-$ARCH" +if [ ! -d "$PACKAGED_DIR" ]; then + echo "Error: Packaged app not found at $PACKAGED_DIR" + echo "Run 'electron-forge make' or 'electron-forge package' first." + exit 1 +fi + +echo "Building AppImage from: $PACKAGED_DIR" + +# Create AppDir structure +APPDIR="$DESKTOP_DIR/out/$PRODUCT_NAME.AppDir" +rm -rf "$APPDIR" +mkdir -p "$APPDIR" + +# Copy the packaged app contents into the AppDir +cp -a "$PACKAGED_DIR"/. "$APPDIR/" + +# Create the AppRun entry point +cat > "$APPDIR/AppRun" << 'APPRUN_EOF' +#!/bin/bash +HERE="$(dirname "$(readlink -f "$0")")" +exec "$HERE/trilium" "$@" +APPRUN_EOF +chmod +x "$APPDIR/AppRun" + +# Create the .desktop file +cat > "$APPDIR/$EXECUTABLE_NAME.desktop" << DESKTOP_EOF +[Desktop Entry] +Name=$PRODUCT_NAME +Comment=Build your personal knowledge base with Trilium Notes +GenericName=Note Taking Application +Exec=$EXECUTABLE_NAME %U +Icon=$EXECUTABLE_NAME +Type=Application +StartupNotify=true +StartupWMClass=$PRODUCT_NAME +Categories=Office;Utility; +DESKTOP_EOF + +# Copy the icon (AppImage expects it at the root of AppDir) +if [ -f "$FORGE_DIR/app-icon/png/256x256.png" ]; then + cp "$FORGE_DIR/app-icon/png/256x256.png" "$APPDIR/$EXECUTABLE_NAME.png" +elif [ -f "$APPDIR/icon.png" ]; then + cp "$APPDIR/icon.png" "$APPDIR/$EXECUTABLE_NAME.png" +else + echo "Warning: No icon found" +fi + +# Determine output filename +UPLOAD_DIR="$DESKTOP_DIR/upload" +mkdir -p "$UPLOAD_DIR" + +if [ -n "${TRILIUM_ARTIFACT_NAME_HINT:-}" ]; then + OUTPUT_NAME="${TRILIUM_ARTIFACT_NAME_HINT//\//-}.AppImage" +else + VERSION=$(node -e "console.log(require('$DESKTOP_DIR/package.json').version)") + OUTPUT_NAME="TriliumNotes-v${VERSION}-linux-${ARCH}.AppImage" +fi + +OUTPUT_PATH="$UPLOAD_DIR/$OUTPUT_NAME" + +# Build the AppImage +echo "Creating AppImage: $OUTPUT_PATH" +ARCH="$APPIMAGE_ARCH" appimagetool "$APPDIR" "$OUTPUT_PATH" + +# Clean up the AppDir +rm -rf "$APPDIR" + +echo "AppImage created successfully: $OUTPUT_PATH"