Something went wrong. Try again.
Hangar is your Bluesky home base—built natively for Linux. ✈️ hangar.blue
libadwaita gtk4 bluesky flatpak gnome atprotocol linux atproto appimage rust
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174# SPDX-License-Identifier: MPL-2.0
name: AppImage
on: workflow_dispatch:
jobs: appimage: name: Build AppImage runs-on: ubuntu-24.04
steps: - name: Checkout uses: actions/checkout@v7
- name: Install dependencies run: | sudo apt-get update sudo apt-get install -y \ libgtk-4-dev \ libadwaita-1-dev \ libsecret-1-dev \ librsvg2-dev \ libfuse2 \ libgstreamer1.0-dev \ libgstreamer-plugins-base1.0-dev \ libglib2.0-bin \ libssl-dev
- name: Install Rust uses: dtolnay/rust-toolchain@stable
- name: Build release binary run: HANGAR_VERSION="git-${GITHUB_SHA::7}" cargo build --release
- name: Check the glibc floor run: | # The AppImage bundles nothing, so the build host's glibc is the # floor it imposes on users. Rust std's process spawn pulls in # pidfd_spawnp (GLIBC_2.39), which no reading of the source shows, # so the binary is asked directly. Moving the runner to a newer # image must fail here rather than in someone's terminal. FLOOR=2.39 MAX=$(objdump -T target/release/hangar \ | grep -o 'GLIBC_[0-9][0-9.]*' \ | sed 's/^GLIBC_//' \ | sort -V | tail -n1) echo "binary requires glibc $MAX, advertised floor $FLOOR" if [ "$(printf '%s\n%s\n' "$FLOOR" "$MAX" | sort -V | tail -n1)" != "$FLOOR" ]; then echo "the binary requires glibc $MAX but every advertised floor says $FLOOR" >&2 exit 1 fi
- name: Download appimagetool run: | curl -sSL https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-x86_64.AppImage -o appimagetool chmod +x appimagetool
- name: Create AppDir structure run: | mkdir -p AppDir/usr/bin mkdir -p AppDir/usr/share/applications mkdir -p AppDir/usr/share/icons/hicolor/scalable/apps mkdir -p AppDir/usr/share/metainfo mkdir -p AppDir/usr/share/glib-2.0/schemas
cp target/release/hangar AppDir/usr/bin/ cp data/io.github.sethcottle.Hangar.desktop AppDir/usr/share/applications/ cp assets/icons/io.github.sethcottle.Hangar.svg AppDir/usr/share/icons/hicolor/scalable/apps/ cp data/io.github.sethcottle.Hangar.metainfo.xml AppDir/usr/share/metainfo/
# Window geometry lives in GSettings, and gio::Settings finds # nothing unless a compiled schema sits on XDG_DATA_DIRS, which # the AppRun points at this directory. cp data/io.github.sethcottle.Hangar.gschema.xml AppDir/usr/share/glib-2.0/schemas/ glib-compile-schemas AppDir/usr/share/glib-2.0/schemas
# Raster sizes for desktops whose SVG renderer cannot draw the # scalable icon's filters (KDE turns them into black plates). for size in 16 32 48 64 128 256; do mkdir -p "AppDir/usr/share/icons/hicolor/${size}x${size}/apps" cp "assets/icons/png/${size}/io.github.sethcottle.Hangar.png" "AppDir/usr/share/icons/hicolor/${size}x${size}/apps/" done
# Desktop file and icon symlinks at AppDir root (required by AppImage spec) ln -s usr/share/applications/io.github.sethcottle.Hangar.desktop AppDir/io.github.sethcottle.Hangar.desktop ln -s usr/share/icons/hicolor/scalable/apps/io.github.sethcottle.Hangar.svg AppDir/io.github.sethcottle.Hangar.svg # .DirIcon is used as the AppImage icon ln -s usr/share/icons/hicolor/scalable/apps/io.github.sethcottle.Hangar.svg AppDir/.DirIcon
- name: Create AppRun script run: | printf '%s\n' '#!/bin/bash' > AppDir/AppRun printf '%s\n' '# AppRun for Hangar, a GTK4 + libadwaita app' >> AppDir/AppRun printf '%s\n' '#' >> AppDir/AppRun printf '%s\n' '# Hangar does NOT bundle GTK4/libadwaita libraries. It uses the' >> AppDir/AppRun printf '%s\n' '# host system libraries. Minimum: GTK 4.14, libadwaita 1.5 and' >> AppDir/AppRun printf '%s\n' '# glibc 2.39, i.e. GNOME 46+ (Fedora 40+, Ubuntu 24.04+, etc).' >> AppDir/AppRun printf '%s\n' '# GTK 4.14 is gtk_graphics_offload, libadwaita 1.5 is AdwDialog,' >> AppDir/AppRun printf '%s\n' '# and glibc 2.39 is the build host itself: all three are linked' >> AppDir/AppRun printf '%s\n' '# non-weak, so a host below any of them cannot start the binary.' >> AppDir/AppRun printf '%s\n' '#' >> AppDir/AppRun printf '%s\n' '# This is intentional: bundling GTK4/libadwaita from the build' >> AppDir/AppRun printf '%s\n' '# environment (Ubuntu 24.04) causes theme/rendering failures on' >> AppDir/AppRun printf '%s\n' '# newer distros due to library version mismatches.' >> AppDir/AppRun printf '%s\n' '#' >> AppDir/AppRun printf '%s\n' '# Video also needs the host GStreamer 1.20+. Unlike the media' >> AppDir/AppRun printf '%s\n' '# backend GTK used to dlopen, this one is linked in, so its' >> AppDir/AppRun printf '%s\n' '# absence stops the binary from starting at all rather than' >> AppDir/AppRun printf '%s\n' '# just disabling video. Hence the check below.' >> AppDir/AppRun printf '%s\n' '' >> AppDir/AppRun printf '%s\n' 'HERE="$(dirname "$(readlink -f "$0")")"' >> AppDir/AppRun printf '%s\n' '' >> AppDir/AppRun printf '%s\n' '# Make system data dirs available for icon and GSettings schema lookup,' >> AppDir/AppRun printf '%s\n' '# while also including our own data dir for the app icon and metainfo.' >> AppDir/AppRun printf '%s\n' 'export XDG_DATA_DIRS="${HERE}/usr/share:/usr/share:/usr/local/share:${XDG_DATA_DIRS:-/usr/share}"' >> AppDir/AppRun printf '%s\n' '' >> AppDir/AppRun printf '%s\n' '# Probe for it with ldconfig, which lives in /usr/sbin and is therefore NOT' >> AppDir/AppRun printf '%s\n' '# on the PATH a desktop launcher hands us, hence the absolute fallbacks.' >> AppDir/AppRun printf '%s\n' '# If no ldconfig can be found at all the probe proves nothing, and a probe' >> AppDir/AppRun printf '%s\n' '# that proves nothing must never be the reason the app refuses to start.' >> AppDir/AppRun printf '%s\n' 'LDCONFIG=""' >> AppDir/AppRun printf '%s\n' 'for candidate in ldconfig /sbin/ldconfig /usr/sbin/ldconfig; do' >> AppDir/AppRun printf '%s\n' ' if command -v "$candidate" >/dev/null 2>&1; then' >> AppDir/AppRun printf '%s\n' ' LDCONFIG="$candidate"' >> AppDir/AppRun printf '%s\n' ' break' >> AppDir/AppRun printf '%s\n' ' fi' >> AppDir/AppRun printf '%s\n' 'done' >> AppDir/AppRun printf '%s\n' '' >> AppDir/AppRun printf '%s\n' 'if [ -n "$LDCONFIG" ] && ! "$LDCONFIG" -p 2>/dev/null | grep -q "libgstgl-1\.0\.so\.0"; then' >> AppDir/AppRun printf '%s\n' ' echo "Hangar: the host GStreamer libraries are missing." >&2' >> AppDir/AppRun printf '%s\n' ' echo " Debian/Ubuntu: sudo apt install libgstreamer1.0-0 libgstreamer-plugins-base1.0-0 libgstreamer-gl1.0-0 gstreamer1.0-plugins-good gstreamer1.0-plugins-bad gstreamer1.0-libav" >&2' >> AppDir/AppRun printf '%s\n' ' echo " Fedora: sudo dnf install gstreamer1 gstreamer1-plugins-base gstreamer1-plugins-good gstreamer1-plugins-bad-free gstreamer1-libav" >&2' >> AppDir/AppRun printf '%s\n' ' exit 1' >> AppDir/AppRun printf '%s\n' 'fi' >> AppDir/AppRun printf '%s\n' '' >> AppDir/AppRun printf '%s\n' '# A host below the floor cannot link. Say so before starting, using' >> AppDir/AppRun printf '%s\n' '# the same cheap ldconfig probe as above, then exec: a shell left in' >> AppDir/AppRun printf '%s\n' '# the middle would swallow SIGTERM and orphan the app at logout.' >> AppDir/AppRun printf '%s\n' 'if [ -n "$LDCONFIG" ]; then' >> AppDir/AppRun printf '%s\n' ' for lib in libadwaita-1.so.0 libgtk-4.so.1; do' >> AppDir/AppRun printf '%s\n' ' if ! "$LDCONFIG" -p 2>/dev/null | grep -q "$lib"; then' >> AppDir/AppRun printf '%s\n' ' echo "Hangar: $lib is missing." >&2' >> AppDir/AppRun printf '%s\n' ' echo " Hangar needs GTK 4.14, libadwaita 1.5 and glibc 2.39 or newer," >&2' >> AppDir/AppRun printf '%s\n' ' echo " which means GNOME 46 or later (Fedora 40+, Ubuntu 24.04+)." >&2' >> AppDir/AppRun printf '%s\n' ' exit 1' >> AppDir/AppRun printf '%s\n' ' fi' >> AppDir/AppRun printf '%s\n' ' done' >> AppDir/AppRun printf '%s\n' 'fi' >> AppDir/AppRun printf '%s\n' 'exec "${HERE}/usr/bin/hangar" "$@"' >> AppDir/AppRun chmod +x AppDir/AppRun
- name: Build AppImage run: | # Build the AppImage using appimagetool directly. # No linuxdeploy = no automatic library bundling. # Hangar relies on the host system's GTK4 + libadwaita. # # --appimage-extract-and-run: required in CI (no FUSE available) # -u: update information for delta updates via zsync ARCH=x86_64 ./appimagetool --appimage-extract-and-run \ -u "gh-releases-zsync|sethcottle|hangar|latest|Hangar*.AppImage.zsync" \ AppDir
- name: Upload AppImage uses: actions/upload-artifact@v7 with: name: hangar-appimage path: | Hangar*.AppImage Hangar*.AppImage.zsync retention-days: 7