From 377fffce109571b2afb683484f778b301d1b365a Mon Sep 17 00:00:00 2001 From: Bentley Davis <10065854+NonPolynomialTim@users.noreply.github.com> Date: Thu, 30 Jul 2026 18:56:11 -0400 Subject: [PATCH 1/2] fix(ci): ship Linux release as AppImage again (glibc 2.31 floor) v2.0.0 replaced the v1.8.4 AppImage with a bare dynamically-linked binary, which needs matching system SDL libraries and a new-enough glibc to start - so players on other distros cannot run it. Rebuild build-linux in an ubuntu:20.04 container (glibc 2.31) and bundle the binary + its libs into OpenXcoop-x86_64.AppImage via linuxdeploy. The data folders still ship next to it (the game finds them via the "./" CWD entry in findDataFolders), plus a HOW_TO_RUN.txt. The container drops sudo and installs git/python3 before checkout, matching build-winxp. Co-Authored-By: Claude Opus 4.8 --- .github/workflows/ci-main.yml | 66 ++++++++++++++++++++++++++++++++--- CHANGELOG.md | 6 ++++ 2 files changed, 68 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci-main.yml b/.github/workflows/ci-main.yml index 1ac930fdf..7da76904b 100644 --- a/.github/workflows/ci-main.yml +++ b/.github/workflows/ci-main.yml @@ -512,22 +512,31 @@ jobs: build-linux: needs: meta runs-on: ubuntu-latest + # Build inside an old-glibc container (2.31) so the AppImage runs on nearly + # every current distro. An AppImage bundles its own libs but NOT glibc, so its + # portability floor is the build host's glibc - ubuntu-latest (2.39) would lock + # out older distros and reproduce the very "can't run it" bug this restores the + # AppImage to fix. Same container pattern as build-winxp. + container: ubuntu:20.04 timeout-minutes: 60 defaults: run: shell: bash steps: - name: Install deps - # Same mirror-flake retry as build-winxp - see the comment there. + # Same mirror-flake retry as build-winxp - see the comment there. No sudo + # (root in the container); git must land BEFORE checkout, which the + # `submodules: recursive` step needs and the bare container lacks. env: DEBIAN_FRONTEND: noninteractive run: | set -eu - apt_get() { sudo apt-get -o Acquire::Retries=5 -o Acquire::http::Timeout=30 "$@"; } + apt_get() { apt-get -o Acquire::Retries=5 -o Acquire::http::Timeout=30 "$@"; } ok=0 for n in 1 2 3 4; do if apt_get update && apt_get install -y \ - cmake g++ make \ + git ca-certificates wget file pkg-config python3 \ + cmake g++ make patchelf \ libsdl1.2-dev libsdl-mixer1.2-dev libsdl-gfx1.2-dev libsdl-image1.2-dev libsdl-net1.2-dev \ libyaml-cpp-dev libjsoncpp-dev libsodium-dev zlib1g-dev then ok=1; break; fi @@ -553,6 +562,38 @@ jobs: -DCMAKE_CXX_FLAGS="$JSONCPP_CFLAGS" "$GITHUB_WORKSPACE" make -j"$(nproc)" + - name: Build AppImage (linuxdeploy) + run: | + set -euxo pipefail + # Bundle the binary + its shared libs into a self-contained AppImage, + # restoring the pre-2.0.0 Linux UX. A bare dynamically-linked binary + # needs matching system SDL and a new-enough glibc; the AppImage carries + # its own libs, so it runs on any x86_64 desktop. Data folders still live + # NEXT TO the AppImage in the tarball - the game finds common/, standard/ + # and rendezvous.json via the "./" (CWD) entry in findDataFolders(), so it + # must be run from the extracted folder, same as the v1.8.4 release. + # + # linuxdeploy and its appimage plugin are themselves AppImages; hosted + # runners have no FUSE, so extract-and-run instead of mounting, and put + # the plugin on PATH where linuxdeploy discovers it by name. + export APPIMAGE_EXTRACT_AND_RUN=1 + export PATH="$PWD:$PATH" + base=https://github.com/linuxdeploy + wget -q "$base/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage" + wget -q "$base/linuxdeploy-plugin-appimage/releases/download/continuous/linuxdeploy-plugin-appimage-x86_64.AppImage" + chmod +x linuxdeploy-x86_64.AppImage linuxdeploy-plugin-appimage-x86_64.AppImage + # appimagetool wants the icon file named after the desktop's Icon= key (openxcom). + cp res/linux/icons/openxcom_128x128.png openxcom.png + # OUTPUT keeps the v1.8.4 asset name so existing docs/links still match. + OUTPUT=OpenXcoop-x86_64.AppImage \ + ./linuxdeploy-x86_64.AppImage \ + --appdir AppDir \ + --executable build/bin/openxcom \ + --desktop-file res/linux/openxcom.desktop \ + --icon-file openxcom.png \ + --output appimage + test -f OpenXcoop-x86_64.AppImage + - name: Package Linux tarball env: RELEASE_RENDEZVOUS_JSON: ${{ secrets.RELEASE_RENDEZVOUS_JSON }} @@ -561,7 +602,9 @@ jobs: v="${{ needs.meta.outputs.version }}" pkg="openxcom-coop-linux-$v-x86_64" mkdir -p "$pkg/UFO" "$pkg/TFTD" - cp build/bin/openxcom "$pkg/" + # Self-contained AppImage instead of the bare binary (see the AppImage step). + cp OpenXcoop-x86_64.AppImage "$pkg/" + chmod +x "$pkg/OpenXcoop-x86_64.AppImage" cp -r bin/common bin/standard "$pkg/" # coop art only - never the whole UFO/TFTD dir (licensed retail data). # Globe's ctor throws on a missing multiplayer/base.png, killing "new game". @@ -572,6 +615,21 @@ jobs: if [ -z "$RELEASE_RENDEZVOUS_JSON" ]; then echo "RELEASE_RENDEZVOUS_JSON secret empty"; exit 1; fi printf '%s' "$RELEASE_RENDEZVOUS_JSON" > "$pkg/rendezvous.json" python3 -c "import json; json.load(open('$pkg/rendezvous.json'))" || { echo "rendezvous.json not valid JSON"; exit 1; } + # The AppImage finds its data via CWD, so it must be launched from this + # folder - spell that out to avoid a "no data" launch from elsewhere. + cat > "$pkg/HOW_TO_RUN.txt" <<'EOF' + OpenXcom Coop - Linux + ===================== + Keep OpenXcoop-x86_64.AppImage in THIS folder (next to common/, standard/, + UFO/, TFTD/, rendezvous.json) and run it from here: + + chmod +x OpenXcoop-x86_64.AppImage + ./OpenXcoop-x86_64.AppImage + + The game looks for its data in the current folder, so launching the + AppImage from somewhere else (e.g. double-clicking after moving it out) + will fail to find the game data. + EOF sh tools/ci/assert_package_dir.sh "$pkg" tar czf "$pkg.tar.gz" "$pkg" diff --git a/CHANGELOG.md b/CHANGELOG.md index df9b823b0..aa7226fa4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,12 @@ Format: [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). Newest first. ## [Unreleased] - Notes for the next release accumulate here; rename to `## [x.y.z]` when tagging. +### Fixed +- Linux download is a self-contained AppImage again (as in v1.8.4) instead of a + bare dynamically-linked binary, so it no longer needs matching system SDL + libraries or a new-enough glibc to start. Built on an older base (glibc 2.31) + for broad distro support. Run `OpenXcoop-x86_64.AppImage` from the extracted + folder, next to the data folders (see the bundled `HOW_TO_RUN.txt`).