name: AppImage Build on: workflow_dispatch: schedule: - cron: '23 6 * * 1' push: branches: [master, 'release/**'] pull_request: branches: [master] jobs: appimage_build: runs-on: ubuntu-22.04 permissions: contents: read id-token: write attestations: write artifact-metadata: write steps: - uses: actions/checkout@v7 with: submodules: true - name: Install build dependencies run: | set -eux sudo apt-get update sudo apt-get install -y --no-install-recommends \ build-essential pkg-config meson ninja-build cmake \ gettext \ libcanberra-dev libglib2.0-dev \ libarchive-dev libsecret-1-dev \ libgtk-3-dev libfontconfig1-dev libpango1.0-dev xvfb xauth dbus-daemon at-spi2-core \ libwayland-client0 libwayland-cursor0 libwayland-egl1 \ libxkbcommon0 \ libgtk-3-bin libglib2.0-bin glib-networking shared-mime-info gsettings-desktop-schemas \ liblua5.4-dev libpci-dev libperl-dev libssl-dev libayatana-appindicator3-dev \ perl python3 python3-sphinx mono-devel desktop-file-utils \ libbz2-dev libffi-dev libgdbm-dev liblzma-dev libncurses-dev \ libreadline-dev libsqlite3-dev libzstd-dev tk-dev uuid-dev zlib1g-dev \ openssl ca-certificates \ breeze-gtk-theme \ patchelf file curl - name: Build private Python runtime run: | set -eux python_prefix="$RUNNER_TEMP/zoitechat-python" bash packaging/appimage/build-python.sh "$python_prefix" echo "$python_prefix/bin" >> "$GITHUB_PATH" echo "ZOITECHAT_PYTHON_PREFIX=$python_prefix" >> "$GITHUB_ENV" echo "LD_LIBRARY_PATH=$python_prefix/lib" >> "$GITHUB_ENV" echo "PKG_CONFIG_PATH=$python_prefix/lib/pkgconfig" >> "$GITHUB_ENV" - name: Configure run: | set -eux rm -rf build meson setup build \ --prefix=/usr \ -Dinstall-docs=true \ -Ddocs-python=/usr/bin/python3 \ -Dtext-frontend=true \ -Dwith-perl=perl \ -Dwith-python=python3 \ -Dauto_features=enabled \ -Dappindicator=enabled - name: Build run: | set -eux ninja -C build - name: Test emoji catalog, font coverage and picker run: xvfb-run -a dbus-run-session -- meson test -C build --print-errorlogs "Emoji Data Tests" "Emoji Font Tests" "Emoji UI Tests" - name: Install to AppDir run: | set -eux rm -rf AppDir DESTDIR="${PWD}/AppDir" ninja -C build install - name: Bundle scripting runtimes in AppDir run: | set -eux "$ZOITECHAT_PYTHON_PREFIX/bin/python3" packaging/appimage/bundle-python.py AppDir install -Dm755 /usr/bin/perl AppDir/usr/bin/perl if [ -d "/usr/lib/x86_64-linux-gnu/perl-base" ]; then install -d AppDir/usr/lib/x86_64-linux-gnu cp -a /usr/lib/x86_64-linux-gnu/perl-base AppDir/usr/lib/x86_64-linux-gnu/ fi if [ -d "/usr/lib/x86_64-linux-gnu/perl" ]; then install -d AppDir/usr/lib/x86_64-linux-gnu cp -a /usr/lib/x86_64-linux-gnu/perl AppDir/usr/lib/x86_64-linux-gnu/ fi if [ -d "/usr/lib/x86_64-linux-gnu/perl5" ]; then install -d AppDir/usr/lib/x86_64-linux-gnu cp -a /usr/lib/x86_64-linux-gnu/perl5 AppDir/usr/lib/x86_64-linux-gnu/ fi if [ -d "/usr/share/perl" ]; then install -d AppDir/usr/share cp -a /usr/share/perl AppDir/usr/share/ fi if [ -d "/usr/share/perl5" ]; then install -d AppDir/usr/share cp -a /usr/share/perl5 AppDir/usr/share/ fi perl -MFile::Spec -e 'print "Build host File::Spec: $INC{\"File/Spec.pm\"}\n"' find AppDir/usr -path '*/File/Spec.pm' -print -quit | grep -q . # Every package ships the pinned font, including portable builds. cmp data/fonts/NotoColorEmoji.ttf AppDir/usr/share/fonts/zoitechat/NotoColorEmoji.ttf if [ -d "/usr/lib/x86_64-linux-gnu/gtk-3.0/modules" ]; then install -d AppDir/usr/lib/x86_64-linux-gnu/gtk-3.0 cp -a /usr/lib/x86_64-linux-gnu/gtk-3.0/modules AppDir/usr/lib/x86_64-linux-gnu/gtk-3.0/ fi if [ -d "/usr/lib/gtk-3.0/modules" ]; then install -d AppDir/usr/lib/gtk-3.0 cp -a /usr/lib/gtk-3.0/modules AppDir/usr/lib/gtk-3.0/ fi # HTTPS inline-image downloads use GSocketClient TLS on Linux. # GLib provides the API, but TLS and system-proxy support live in # loadable GIO modules shipped by glib-networking. Bundle those # modules so the AppImage does not depend on the host copy. gio_module_dir="$(pkg-config --variable=giomoduledir gio-2.0)" test -n "$gio_module_dir" test -f "${gio_module_dir}/libgiognutls.so" install -d "AppDir${gio_module_dir}" cp -a "${gio_module_dir}"/libgio*.so "AppDir${gio_module_dir}/" gio-querymodules "AppDir${gio_module_dir}" test -f "AppDir${gio_module_dir}/libgiognutls.so" test -s "AppDir${gio_module_dir}/giomodule.cache" - name: Verify bundled plugins run: | set -eux # Include every built-in plugin except the Windows-only update plugin. find AppDir/usr/lib -maxdepth 4 -type f -name '*.so' -path '*/zoitechat/plugins/*' -print | sort for plugin in checksum fishlim lua perl python sysinfo; do find "AppDir/usr/lib" -maxdepth 4 -type f -name "${plugin}.so" -path '*/zoitechat/plugins/*' -print -quit | grep -q . done - name: Build AppImage env: APPIMAGE_EXTRACT_AND_RUN: 1 DEPLOY_GTK_VERSION: 3 LDAI_NO_APPSTREAM: 1 run: | set -eux curl -fL --retry 3 -o linuxdeploy-x86_64.AppImage \ https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage chmod +x linuxdeploy-x86_64.AppImage curl -fL --retry 3 -o linuxdeploy-plugin-gtk \ https://raw.githubusercontent.com/linuxdeploy/linuxdeploy-plugin-gtk/7a3fbc31a9e5075073ff8790f26effbac5f84453/linuxdeploy-plugin-gtk.sh # The generated GTK hook otherwise overrides both Wayland selection # and the user's explicit GDK_BACKEND before our AppRun is reached. grep -Fx 'export GDK_BACKEND=x11 # Crash with Wayland backend on Wayland' linuxdeploy-plugin-gtk sed -i '/^export GDK_BACKEND=x11 # Crash with Wayland backend on Wayland$/d' linuxdeploy-plugin-gtk chmod +x linuxdeploy-plugin-gtk export PATH="${PWD}:${PATH}" install -Dm644 /etc/ssl/certs/ca-certificates.crt \ AppDir/etc/ssl/certs/ca-certificates.crt install -m755 packaging/appimage/AppRun AppRun export VERSION="$(cat VERSION)" ./linuxdeploy-x86_64.AppImage \ --appdir AppDir \ --desktop-file AppDir/usr/share/applications/net.zoite.Zoitechat.desktop \ --icon-file AppDir/usr/share/icons/hicolor/48x48/apps/net.zoite.Zoitechat.png \ --custom-apprun ./AppRun \ --plugin gtk \ --output appimage appimage_path="$(ls -1 *.AppImage | grep -v linuxdeploy | head -n 1)" output_name="Zoitechat-${VERSION}-x86_64.AppImage" if [ "$appimage_path" != "$output_name" ]; then mv -- "$appimage_path" "$output_name" fi - name: Test packaged runtime and plugins run: | set -eux appimage_path="$(find . -maxdepth 1 -name 'Zoitechat-*-x86_64.AppImage' -print -quit)" test -n "$appimage_path" mkdir -p "$RUNNER_TEMP/appimage check" (cd "$RUNNER_TEMP/appimage check" && "$GITHUB_WORKSPACE/$appimage_path" --appimage-extract) # Hide the build interpreter so it cannot mask broken relocation. mv "$ZOITECHAT_PYTHON_PREFIX" "$ZOITECHAT_PYTHON_PREFIX.hidden" trap 'mv "$ZOITECHAT_PYTHON_PREFIX.hidden" "$ZOITECHAT_PYTHON_PREFIX"' EXIT env -u LD_LIBRARY_PATH -u PYTHONHOME -u PYTHONPATH \ /usr/bin/python3 packaging/appimage/check-runtime.py "$RUNNER_TEMP/appimage check/squashfs-root" - name: Record dependency versions and checksum run: | set -eux mkdir -p appimage-audit dpkg-query -W -f='${binary:Package}\t${Version}\n' > appimage-audit/build-packages.tsv cp "$RUNNER_TEMP/appimage check/runtime.json" appimage-audit/runtime.json sha256sum linuxdeploy-x86_64.AppImage linuxdeploy-plugin-gtk > appimage-audit/build-tools.sha256 sha256sum Zoitechat-*-x86_64.AppImage > appimage-audit/SHA256SUMS - name: Upload dependency audit uses: actions/upload-artifact@v7 with: name: zoitechat-appimage-audit path: appimage-audit/ if-no-files-found: error - name: Attest AppImage (Build Provenance) if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }} uses: actions/attest-build-provenance@v3 with: subject-path: Zoitechat-*-x86_64.AppImage - name: Upload AppImage artifact uses: actions/upload-artifact@v7 with: name: zoitechat-appimage path: Zoitechat-*-x86_64.AppImage if-no-files-found: error