Skip to content

1.10.2b3

1.10.2b3 #1837

Workflow file for this run

on: [push]
env:
ProjectName: Chataigne
PackagesVersion: 1.2.10
jobs:
windows:
name: Windows
# if: false # always skip job
runs-on: windows-2022
strategy:
matrix:
include:
- arch: win-x64
buildFolder: "VisualStudio2022_CI"
installerName: install
- arch: win7-x64
buildFolder: "VisualStudio2022_Win7CI"
installerName: installWin7
fail-fast: false
steps:
- name: Checkout repo
uses: actions/checkout@v2
with:
submodules: 'recursive'
- name: Checkout JUCE
uses: actions/checkout@v2
with:
repository: benkuper/JUCE
ref: develop-local
path: JUCE
- name: Set Variables
id: set_variables
uses: ./.github/actions/set-suffix
with:
os: ${{ matrix.arch }}
- name: Add msbuild to PATH
uses: microsoft/setup-msbuild@v1.0.2
- name: Force 64-bit Linker
shell: powershell
run: |
cmd.exe /c "call `"C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat`" && set > %temp%\vcvars.txt"
Get-Content "$env:temp\vcvars.txt" | Foreach-Object { if ($_ -match "^(.*?)=(.*)$") { Set-Content "env:\$($matches[1])" $matches[2] }}
- name: Build
run: msbuild "Builds/${{ matrix.buildFolder }}/${{ env.ProjectName }}.sln" /p:PreferredToolArchitecture=x64 /m /verbosity:normal /p:Configuration=${{ steps.set_variables.outputs.config }}
- name: Create Package
id: create_package
shell: powershell
run: |
Set-Variable -Name "PKGNAME" -Value "${{ env.ProjectName }}-${{ steps.set_variables.outputs.suffix }}"
Invoke-WebRequest "${{ secrets.DEPDIRURL }}${{ env.ProjectName }}-win-x64-${{ steps.set_variables.outputs.dep }}-dependencies.zip" -OutFile ./deps.zip
7z e deps.zip -aoa
&"C:/Program Files (x86)/Inno Setup 6/ISCC.exe" "${{ github.workspace }}/${{ matrix.installerName }}.iss" /O. /F$PKGNAME
echo "pkg-name=$PKGNAME.exe" | Out-File -FilePath $Env:GITHUB_OUTPUT -Encoding utf8 -Append
echo "pdb-name=$PKGNAME.pdb" | Out-File -FilePath $Env:GITHUB_OUTPUT -Encoding utf8 -Append
working-directory: ./Binaries/CI/App
- name: Upload
uses: ./.github/actions/upload
with:
pkg-name: ./Binaries/CI/App/${{ steps.create_package.outputs.pkg-name }}
url: ${{ secrets.UPLOADURL }}
pass: ${{ secrets.UPLOADPASS }}
- name: Rename PDB
if: ${{ steps.set_variables.outputs.config == 'Release' && matrix.arch == 'win-x64' }}
id: rename_pdb
shell: powershell
run: |
Rename-Item -Path "./${{ env.ProjectName }}.pdb" -NewName "${{ steps.create_package.outputs.pdb-name }}"
working-directory: ./Builds/${{ matrix.buildFolder }}/x64/${{ steps.set_variables.outputs.config }}/App
- name: Upload PDB
if: ${{ steps.set_variables.outputs.config == 'Release' && matrix.arch == 'win-x64' }}
uses: ./.github/actions/upload
with:
pkg-name: ./Builds/${{ matrix.buildFolder }}/x64/${{ steps.set_variables.outputs.config }}/App/${{ steps.create_package.outputs.pdb-name }}
url: ${{ secrets.PDBUPLOADURL }}
pass: ${{ secrets.UPLOADPASS }}
osx:
# if: false # tmp disable
name: OSX
runs-on: macos-latest
strategy:
matrix:
include:
- arch: x86_64
suffix: intel
config: Release
- arch: arm64
suffix: silicon
config: ReleaseSilicon
fail-fast: false
steps:
- name: Checkout repo
uses: actions/checkout@v2
with:
submodules: 'recursive'
- name: Checkout JUCE
uses: actions/checkout@v2
with:
repository: benkuper/JUCE
ref: develop-local
path: JUCE
- name: Set Suffix
id: set_variables
uses: ./.github/actions/set-suffix
with:
os: 'osx-${{ matrix.suffix }}'
- name: Download Packages
run: |
curl -L -o Packages.dmg 'http://s.sudre.free.fr/Software/files/Packages.dmg'
hdiutil mount Packages.dmg
sudo installer -pkg "/Volumes/Packages ${{ env.PackagesVersion }}/Install Packages.pkg" -target /
hdiutil detach "/Volumes/Packages ${{ env.PackagesVersion }}/"
- name: Setup XCode
uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: '16.4.0'
- name: Build
uses: sersoft-gmbh/xcodebuild-action@v2.0.1
with:
project: Builds/MacOSX_CI/${{ env.ProjectName }}.xcodeproj
destination: platform=macOS
jobs: 2
action: build
arch: ${{ matrix.arch }}
configuration: ${{ matrix.config }}
use-xcpretty: true
- name: Bundle Libraries (Silicon Only)
if: ${{ matrix.arch == 'arm64' }}
run: |
# 1. Define Path (Matches .pkgproj expectation)
APP_PATH="Release/${{ env.ProjectName }}.app"
# 2. Verification
if [ ! -d "$APP_PATH" ]; then
echo "::error::App not found at '$APP_PATH'. Please verify your Projucer Post-Build script actually moves the app to this folder."
echo "Listing root directory for debugging:"
ls -F
exit 1
fi
echo "Found App at $APP_PATH. Proceeding with bundling."
FRAMEWORKS_DIR="$APP_PATH/Contents/Frameworks"
EXECUTABLE="$APP_PATH/Contents/MacOS/${{ env.ProjectName }}"
# 3. Create Frameworks directory
mkdir -p "$FRAMEWORKS_DIR"
# 4. Copy Dylibs
cp External/mosquitto/lib/osx/libmosquitto.dylib "$FRAMEWORKS_DIR/"
cp External/mosquitto/lib/osx/libmosquittopp.dylib "$FRAMEWORKS_DIR/"
cp External/mosquitto/lib/osx/libssl.dylib "$FRAMEWORKS_DIR/"
cp External/mosquitto/lib/osx/libcrypto.dylib "$FRAMEWORKS_DIR/"
# 5. Fix Library IDs
install_name_tool -id @rpath/libmosquitto.dylib "$FRAMEWORKS_DIR/libmosquitto.dylib"
install_name_tool -id @rpath/libmosquittopp.dylib "$FRAMEWORKS_DIR/libmosquittopp.dylib"
install_name_tool -id @rpath/libssl.dylib "$FRAMEWORKS_DIR/libssl.dylib"
install_name_tool -id @rpath/libcrypto.dylib "$FRAMEWORKS_DIR/libcrypto.dylib"
# 6. Fix dependency path in libmosquittopp
install_name_tool -change /usr/local/lib/libmosquitto.dylib @rpath/libmosquitto.dylib "$FRAMEWORKS_DIR/libmosquittopp.dylib" || true
install_name_tool -change libmosquitto.dylib @rpath/libmosquitto.dylib "$FRAMEWORKS_DIR/libmosquittopp.dylib" || true
install_name_tool -change libssl.dylib @rpath/libssl.dylib "$FRAMEWORKS_DIR/libmosquittopp.dylib" || true
install_name_tool -change libcrypto.dylib @rpath/libcrypto.dylib "$FRAMEWORKS_DIR/libmosquittopp.dylib" || true
# 7. Add RPATH to Executable
install_name_tool -add_rpath @executable_path/../Frameworks "$EXECUTABLE" || true
# 8. Re-sign (Mandatory after modifying the bundle)
codesign --force --deep --sign - "$APP_PATH"
- name: Create Package
id: create_package
run: |
packagesbuild ${{ env.ProjectName }}.pkgproj
PKGNAME=${{ env.ProjectName }}-${{ steps.set_variables.outputs.suffix }}.pkg
mv ${{ env.ProjectName }}.pkg $PKGNAME
echo "pkg-name=$PKGNAME" >> $GITHUB_OUTPUT
working-directory: ./Package
- name: Upload
uses: ./.github/actions/upload
with:
pkg-name: ./Package/${{ steps.create_package.outputs.pkg-name }}
url: ${{ secrets.UPLOADURL }}
pass: ${{ secrets.UPLOADPASS }}
linux:
name: Linux (x64)
runs-on: ubuntu-22.04
steps:
- name: Checkout repo
uses: actions/checkout@v4
with:
submodules: 'recursive'
- name: Checkout JUCE
uses: actions/checkout@v4
with:
repository: benkuper/JUCE
ref: develop-local
path: JUCE
- name: Set Suffix
id: set_variables
uses: ./.github/actions/set-suffix
with:
os: linux-x64
- name: Installing dependencies
run: |
sudo apt-get update
sudo apt-get install -y \
libasound2-dev libjack-jackd2-dev ladspa-sdk \
libfreetype6-dev libx11-dev libxcomposite-dev \
libxcursor-dev libxinerama-dev libxext-dev \
libxrandr-dev libxrender-dev libwebkit2gtk-4.0-dev \
libglu1-mesa-dev mesa-common-dev libcurl4-openssl-dev \
libssl-dev libmosquitto-dev libmosquittopp-dev libbluetooth-dev \
libusb-1.0-0-dev libhidapi-dev libsdl2-dev
- name: Build
run: |
# Clean blobs to force system linking
rm -rf External/mosquitto/lib/linux/
rm -rf External/sdl/lib/linux/
cd Builds/LinuxMakefile
make -j$(nproc) CONFIG=Release
- name: ABI Baseline Audit (glibc/OpenSSL)
run: |
set -euo pipefail
BIN=Builds/LinuxMakefile/build/${{ env.ProjectName }}
if [ ! -f "$BIN" ]; then
echo "::error::Binary not found at $BIN"
exit 1
fi
MAX_GLIBC=$(objdump -T "$BIN" | grep -o 'GLIBC_[0-9.]\+' | sed 's/GLIBC_//' | sort -V | tail -1)
echo "Detected max GLIBC symbol: $MAX_GLIBC"
if dpkg --compare-versions "$MAX_GLIBC" gt "2.35"; then
echo "::error::GLIBC baseline too new ($MAX_GLIBC). Must stay <= 2.35 for Ubuntu 22.04 baseline compatibility."
exit 1
fi
NEEDED=$(readelf -d "$BIN" | awk '/NEEDED/ {print $5}' | tr -d '[]')
echo "Detected NEEDED libs:"
echo "$NEEDED"
if echo "$NEEDED" | grep -E 'libssl\.so\.1\.1|libcrypto\.so\.1\.1' >/dev/null; then
echo "::error::Binary links against OpenSSL 1.1, which is not acceptable for Bookworm/Trixie/Ubuntu 24.04 targets."
exit 1
fi
- name: Validate ABI Across Target Distros
run: |
set -euo pipefail
for IMAGE in ubuntu:22.04 ubuntu:24.04 debian:bookworm debian:trixie; do
echo "\n=== ABI check in $IMAGE ==="
docker run --rm -v "${GITHUB_WORKSPACE}:/ws" "$IMAGE" bash -lc '
set -euo pipefail
apt-get update >/dev/null
apt-get install -y --no-install-recommends binutils >/dev/null
BIN=/ws/Builds/LinuxMakefile/build/${{ env.ProjectName }}
MAX_GLIBC=$(objdump -T "$BIN" | grep -o "GLIBC_[0-9.]\\+" | sed "s/GLIBC_//" | sort -V | tail -1)
echo "Max GLIBC symbol: $MAX_GLIBC"
dpkg --compare-versions "$MAX_GLIBC" le "2.35"
NEEDED=$(readelf -d "$BIN" | sed -n "s/.*Shared library: \[\(.*\)\]/\1/p")
if echo "$NEEDED" | grep -E "libssl\\.so\\.1\\.1|libcrypto\\.so\\.1\\.1" >/dev/null; then
echo "OpenSSL 1.1 dependency detected"
exit 1
fi
'
done
- name: Prepare AppImage Tool
run: |
wget "https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-x86_64.AppImage"
chmod a+x appimagetool-x86_64.AppImage
APPDIR=./Builds/LinuxMakefile/${{ env.ProjectName }}.AppDir
mkdir -p "$APPDIR/usr/bin" "$APPDIR/usr/lib"
# Keep template metadata/resources, but reset dynamic libs and stale binary.
find "$APPDIR/usr/lib" -mindepth 1 ! -name 'exec_wrapper.so' -delete
rm -f "$APPDIR/usr/bin/${{ env.ProjectName }}"
# Ensure expected AppImage metadata files are present.
test -f "$APPDIR/AppRun"
test -f "$APPDIR/chataigne.desktop"
test -f "$APPDIR/chataigne.png"
- name: Create AppImage
id: create_package
run: |
set -euo pipefail
APPDIR=./Builds/LinuxMakefile/${{ env.ProjectName }}.AppDir
BIN=$APPDIR/usr/bin/${{ env.ProjectName }}
# 1. Copy Executable
cp Builds/LinuxMakefile/build/${{ env.ProjectName }} "$BIN"
# 2. Copy app-specific libs (seed set)
LIB_PATH=/usr/lib/x86_64-linux-gnu
copy_glob() {
if compgen -G "$1" > /dev/null; then
cp -d $1 "$APPDIR/usr/lib/"
fi
}
copy_glob "$LIB_PATH/libmosquitto.so*"
copy_glob "$LIB_PATH/libmosquittopp.so*"
copy_glob "$LIB_PATH/libbluetooth.so*"
copy_glob "$LIB_PATH/libusb-1.0.so*"
copy_glob "$LIB_PATH/libhidapi-hidraw.so*"
copy_glob "$LIB_PATH/libSDL2-2.0.so*"
copy_glob "External/servus/lib/linux/libServus.so*"
# Some bundles only ship libServus.so while the binary expects libServus.so.6.
if compgen -G "$APPDIR/usr/lib/libServus.so" > /dev/null && ! compgen -G "$APPDIR/usr/lib/libServus.so.6" > /dev/null; then
ln -sf libServus.so "$APPDIR/usr/lib/libServus.so.6"
fi
# 3. Auto-bundle runtime deps from direct NEEDED entries, excluding forbidden core libs.
SEARCH_DIRS=("$LIB_PATH" "/lib/x86_64-linux-gnu" "/usr/lib/x86_64-linux-gnu")
is_forbidden_lib() {
case "$1" in
libc.so*|libm.so*|libpthread.so*|librt.so*|libdl.so*|libgcc_s.so*|libstdc++.so*|libssl.so*|libcrypto.so*|libcurl.so*|libpulse.so*|libpulsecommon*.so*|ld-linux*.so*|libresolv.so*|libnss_*.so*|libanl.so*|libutil.so*)
return 0
;;
*)
return 1
;;
esac
}
find_matching_so_path() {
local SONAME="$1"
local DIR
for DIR in "${SEARCH_DIRS[@]}"; do
if [ -e "$DIR/$SONAME" ]; then
echo "$DIR/$SONAME"
return 0
fi
done
for DIR in "${SEARCH_DIRS[@]}"; do
local CANDIDATE
CANDIDATE=$(find "$DIR" -maxdepth 1 -name "$SONAME*" 2>/dev/null | head -n 1 || true)
if [ -n "$CANDIDATE" ]; then
echo "$CANDIDATE"
return 0
fi
done
return 1
}
copy_needed_from_target() {
local TARGET="$1"
readelf -d "$TARGET" | awk '/NEEDED/ {print $5}' | tr -d '[]' | while read -r SONAME; do
[ -n "$SONAME" ] || continue
if is_forbidden_lib "$SONAME"; then
continue
fi
if compgen -G "$APPDIR/usr/lib/$SONAME*" > /dev/null; then
continue
fi
local RESOLVED
RESOLVED=$(find_matching_so_path "$SONAME" || true)
if [ -n "$RESOLVED" ]; then
copy_glob "$(dirname "$RESOLVED")/$SONAME*"
else
echo "::warning::Could not resolve dependency $SONAME from $TARGET"
fi
done
}
# Multiple passes resolve transitive dependencies of bundled libraries only.
for _ in 1 2 3; do
copy_needed_from_target "$BIN"
while read -r LIBFILE; do
copy_needed_from_target "$LIBFILE"
done < <(find "$APPDIR/usr/lib" -maxdepth 1 \( -type f -o -type l \))
done
# Never bundle glibc/libstdc++/OpenSSL/libcurl/libpulse
for FORBIDDEN in libc.so libm.so libpthread.so librt.so libdl.so libgcc_s.so libstdc++.so libssl.so libcrypto.so libcurl.so libpulse.so libpulsecommon; do
if compgen -G "$APPDIR/usr/lib/${FORBIDDEN}*" > /dev/null; then
echo "::error::Forbidden bundled runtime detected: ${FORBIDDEN}"
exit 1
fi
done
# 4. Generate AppImage
PKGNAME=${{ env.ProjectName }}-${{ steps.set_variables.outputs.suffix }}.AppImage
echo "pkg-name=$PKGNAME" >> $GITHUB_OUTPUT
./appimagetool-x86_64.AppImage --no-appstream $APPDIR $PKGNAME
working-directory: ${{ github.workspace }}
- name: Smoke Test Linux AppImage
run: |
set -euo pipefail
PKG=${{ steps.create_package.outputs.pkg-name }}
for IMAGE in ubuntu:22.04 ubuntu:24.04 debian:bookworm debian:trixie; do
echo "\n=== AppImage smoke test in $IMAGE ==="
docker run --rm -v "${GITHUB_WORKSPACE}:/ws" "$IMAGE" bash -lc '
set -euo pipefail
export DEBIAN_FRONTEND=noninteractive
apt-get update >/dev/null
apt-get install -y --no-install-recommends xvfb xauth file >/dev/null
# AppImage payload intentionally relies on host-provided libcurl/libpulse.
install_any_pkg() {
for PKG in "$@"; do
if apt-cache show "$PKG" >/dev/null 2>&1; then
apt-get install -y --no-install-recommends "$PKG" >/dev/null
return 0
fi
done
echo "::error::None of the package alternatives are available: $*"
exit 1
}
install_any_pkg libcurl4 libcurl4t64
install_any_pkg libpulse0 libpulse0t64
install_any_pkg libgcrypt20 libgcrypt20t64
cd /ws
chmod +x '"$PKG"'
rm -rf squashfs-root
./'"$PKG"' --appimage-extract >/dev/null
APPDIR=/ws/squashfs-root
APP="$APPDIR/usr/bin/${{ env.ProjectName }}"
export LD_LIBRARY_PATH="$APPDIR/usr/lib"
# Dependency check for launch-critical binary with AppDir runtime path
if ldd "$APP" | grep -q "not found"; then
echo "Missing runtime deps detected in AppImage binary"
ldd "$APP"
exit 1
fi
# Startup smoke test (timeout means app started and stayed alive)
set +e
timeout 20s xvfb-run -a "$APP" --help >/tmp/chataigne-smoke.log 2>&1
RC=$?
set -e
if [ "$RC" -ne 0 ] && [ "$RC" -ne 124 ]; then
echo "App startup failed (rc=$RC)"
cat /tmp/chataigne-smoke.log
exit 1
fi
'
done
- name: Upload
uses: ./.github/actions/upload
with:
pkg-name: ${{ steps.create_package.outputs.pkg-name }}
url: ${{ secrets.UPLOADURL }}
pass: ${{ secrets.UPLOADPASS }}
raspberrypi:
name: Raspberry Pi (Cross-Compile)
runs-on: ubuntu-22.04
strategy:
fail-fast: false
matrix:
include:
- name: "RPi 64-bit"
arch_name: aarch64
deb_arch: arm64
gnu_type: aarch64-linux-gnu
buildFolder: "Raspberry64"
buildConfig: "Release"
arch_flags: "-march=armv8-a"
- name: "RPi 32-bit"
arch_name: armhf
deb_arch: armhf
gnu_type: arm-linux-gnueabihf
buildFolder: "Raspberry"
buildConfig: "Release"
arch_flags: "-march=armv7-a -mfpu=neon-vfpv4 -mfloat-abi=hard"
steps:
- name: Checkout repo
uses: actions/checkout@v4
with:
submodules: 'recursive'
- name: Checkout JUCE
uses: actions/checkout@v4
with:
repository: benkuper/JUCE
ref: develop-local
path: JUCE
- name: Setup Multi-Arch Apt
run: |
sudo dpkg --add-architecture ${{ matrix.deb_arch }}
sudo cp /etc/apt/sources.list /etc/apt/sources.list.bak
echo "deb [arch=amd64] http://archive.ubuntu.com/ubuntu jammy main universe restricted multiverse" | sudo tee /etc/apt/sources.list
echo "deb [arch=amd64] http://archive.ubuntu.com/ubuntu jammy-updates main universe restricted multiverse" | sudo tee -a /etc/apt/sources.list
echo "deb [arch=amd64] http://archive.ubuntu.com/ubuntu jammy-security main universe restricted multiverse" | sudo tee -a /etc/apt/sources.list
echo "deb [arch=${{ matrix.deb_arch }}] http://ports.ubuntu.com/ubuntu-ports jammy main universe restricted multiverse" | sudo tee -a /etc/apt/sources.list
echo "deb [arch=${{ matrix.deb_arch }}] http://ports.ubuntu.com/ubuntu-ports jammy-updates main universe restricted multiverse" | sudo tee -a /etc/apt/sources.list
echo "deb [arch=${{ matrix.deb_arch }}] http://ports.ubuntu.com/ubuntu-ports jammy-security main universe restricted multiverse" | sudo tee -a /etc/apt/sources.list
sudo apt-get update
- name: Install Cross-Compiler and Libraries
run: |
sudo apt-get install -y crossbuild-essential-${{ matrix.deb_arch }}
sudo apt-get install -y \
libasound2-dev:${{ matrix.deb_arch }} \
libjack-jackd2-dev:${{ matrix.deb_arch }} \
ladspa-sdk:${{ matrix.deb_arch }} \
libfreetype6-dev:${{ matrix.deb_arch }} \
libx11-dev:${{ matrix.deb_arch }} \
libxcomposite-dev:${{ matrix.deb_arch }} \
libxcursor-dev:${{ matrix.deb_arch }} \
libxinerama-dev:${{ matrix.deb_arch }} \
libxext-dev:${{ matrix.deb_arch }} \
libxrandr-dev:${{ matrix.deb_arch }} \
libxrender-dev:${{ matrix.deb_arch }} \
libwebkit2gtk-4.0-dev:${{ matrix.deb_arch }} \
libglu1-mesa-dev:${{ matrix.deb_arch }} \
mesa-common-dev:${{ matrix.deb_arch }} \
libcurl4-openssl-dev:${{ matrix.deb_arch }} \
libssl-dev:${{ matrix.deb_arch }} \
libmosquitto-dev:${{ matrix.deb_arch }} \
libmosquittopp-dev:${{ matrix.deb_arch }} \
libbluetooth-dev:${{ matrix.deb_arch }} \
libusb-1.0-0-dev:${{ matrix.deb_arch }} \
libhidapi-dev:${{ matrix.deb_arch }} \
libsdl2-dev:${{ matrix.deb_arch }}
- name: Set Suffix
id: set_variables
uses: ./.github/actions/set-suffix
with:
os: ${{ matrix.arch_name }}
- name: Build
run: |
# Clean incompatible blobs
rm -rf External/mosquitto/lib/linux/
rm -rf External/mosquitto/lib/rpi/
rm -rf External/sdl/lib/raspberry64/
rm -rf External/sdl/lib/raspberry/
rm -rf Modules/juce_simpleweb/libs/Linux/x86_64/
cd Builds/${{ matrix.buildFolder }}
export PKG_CONFIG_PATH=/usr/lib/${{ matrix.gnu_type }}/pkgconfig
make -j$(nproc) \
CONFIG=${{ matrix.buildConfig }} \
CC=${{ matrix.gnu_type }}-gcc \
CXX=${{ matrix.gnu_type }}-g++ \
AR=${{ matrix.gnu_type }}-ar \
STRIP=${{ matrix.gnu_type }}-strip \
TARGET_ARCH="${{ matrix.arch_flags }}"
- name: ABI Baseline Audit (RPi ${{ matrix.arch_name }})
run: |
set -euo pipefail
BIN=Builds/${{ matrix.buildFolder }}/build/${{ env.ProjectName }}
if [ ! -f "$BIN" ]; then
echo "::error::Binary not found at $BIN"
exit 1
fi
MAX_GLIBC=$(${{ matrix.gnu_type }}-objdump -T "$BIN" | grep -o 'GLIBC_[0-9.]\+' | sed 's/GLIBC_//' | sort -V | tail -1)
echo "Detected max GLIBC symbol: $MAX_GLIBC"
if dpkg --compare-versions "$MAX_GLIBC" gt "2.35"; then
echo "::error::RPi GLIBC baseline too new ($MAX_GLIBC). Must stay <= 2.35."
exit 1
fi
NEEDED=$(${{ matrix.gnu_type }}-readelf -d "$BIN" | awk '/NEEDED/ {print $5}' | tr -d '[]')
echo "Detected NEEDED libs:"
echo "$NEEDED"
if echo "$NEEDED" | grep -E 'libssl\.so\.1\.1|libcrypto\.so\.1\.1' >/dev/null; then
echo "::error::RPi binary links against OpenSSL 1.1, which is not acceptable for Bookworm/Trixie generation systems."
exit 1
fi
- name: Prepare AppImage Tool
run: |
wget "https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-x86_64.AppImage"
chmod a+x appimagetool-x86_64.AppImage
APPDIR=./Builds/${{ matrix.buildFolder }}/${{ env.ProjectName }}.AppDir
mkdir -p "$APPDIR/usr/bin" "$APPDIR/usr/lib"
# Keep template metadata/resources, but reset dynamic libs and stale binary.
find "$APPDIR/usr/lib" -mindepth 1 -delete
rm -f "$APPDIR/usr/bin/${{ env.ProjectName }}"
# Ensure expected AppImage metadata files are present.
test -f "$APPDIR/AppRun"
test -f "$APPDIR/chataigne.desktop"
test -f "$APPDIR/chataigne.png"
- name: Create AppImage
id: create_package
run: |
set -euo pipefail
APPDIR=./Builds/${{ matrix.buildFolder }}/${{ env.ProjectName }}.AppDir
BIN=$APPDIR/usr/bin/${{ env.ProjectName }}
# 1. Copy Executable
cp Builds/${{ matrix.buildFolder }}/build/${{ env.ProjectName }} "$BIN"
# 2. Copy app-specific libs only
LIB_PATH=/usr/lib/${{ matrix.gnu_type }}
copy_glob() {
if compgen -G "$1" > /dev/null; then
cp -d $1 "$APPDIR/usr/lib/"
fi
}
copy_glob "$LIB_PATH/libmosquitto.so*"
copy_glob "$LIB_PATH/libmosquittopp.so*"
copy_glob "$LIB_PATH/libbluetooth.so*"
copy_glob "$LIB_PATH/libusb-1.0.so*"
copy_glob "$LIB_PATH/libhidapi-hidraw.so*"
copy_glob "$LIB_PATH/libSDL2-2.0.so*"
if [ "${{ matrix.arch_name }}" = "aarch64" ]; then
copy_glob "External/servus/lib/raspberry64/libServus.so*"
else
copy_glob "External/servus/lib/raspberry/libServus.so*"
fi
# Some bundles only ship libServus.so while the binary expects libServus.so.6.
if compgen -G "$APPDIR/usr/lib/libServus.so" > /dev/null && ! compgen -G "$APPDIR/usr/lib/libServus.so.6" > /dev/null; then
ln -sf libServus.so "$APPDIR/usr/lib/libServus.so.6"
fi
# 3. Auto-bundle direct+transitive runtime deps using cross-readelf (works for foreign arch binaries).
SEARCH_DIRS=("$LIB_PATH" "/lib/${{ matrix.gnu_type }}" "/usr/lib/${{ matrix.gnu_type }}")
is_forbidden_lib() {
case "$1" in
libc.so*|libm.so*|libpthread.so*|librt.so*|libdl.so*|libgcc_s.so*|libstdc++.so*|libssl.so*|libcrypto.so*|libcurl.so*|libpulse.so*|libpulsecommon*.so*|ld-linux*.so*|libresolv.so*|libnss_*.so*|libanl.so*|libutil.so*)
return 0
;;
*)
return 1
;;
esac
}
find_matching_so_path() {
local SONAME="$1"
local DIR
for DIR in "${SEARCH_DIRS[@]}"; do
if [ -e "$DIR/$SONAME" ]; then
echo "$DIR/$SONAME"
return 0
fi
done
for DIR in "${SEARCH_DIRS[@]}"; do
local CANDIDATE
CANDIDATE=$(find "$DIR" -maxdepth 1 -name "$SONAME*" 2>/dev/null | head -n 1 || true)
if [ -n "$CANDIDATE" ]; then
echo "$CANDIDATE"
return 0
fi
done
return 1
}
copy_needed_from_target() {
local TARGET="$1"
${{ matrix.gnu_type }}-readelf -d "$TARGET" | awk '/NEEDED/ {print $5}' | tr -d '[]' | while read -r SONAME; do
[ -n "$SONAME" ] || continue
if is_forbidden_lib "$SONAME"; then
continue
fi
if compgen -G "$APPDIR/usr/lib/$SONAME*" > /dev/null; then
continue
fi
local RESOLVED
RESOLVED=$(find_matching_so_path "$SONAME" || true)
if [ -n "$RESOLVED" ]; then
copy_glob "$(dirname "$RESOLVED")/$SONAME*"
else
echo "::warning::Could not resolve dependency $SONAME from $TARGET"
fi
done
}
# Multiple passes to resolve transitive dependencies.
for _ in 1 2 3; do
copy_needed_from_target "$BIN"
find "$APPDIR/usr/lib" \( -type f -o -type l \) | while read -r LIBFILE; do
copy_needed_from_target "$LIBFILE"
done
done
# 4. Verify dependency closure for bundled payload (excluding intentionally forbidden system libs).
verify_target_needed() {
local TARGET="$1"
local MISSING=0
while read -r SONAME; do
[ -n "$SONAME" ] || continue
if is_forbidden_lib "$SONAME"; then
continue
fi
if compgen -G "$APPDIR/usr/lib/$SONAME*" > /dev/null; then
continue
fi
if ! find_matching_so_path "$SONAME" >/dev/null; then
echo "::error::Unresolved dependency for $(basename "$TARGET"): $SONAME"
MISSING=1
fi
done < <(${{ matrix.gnu_type }}-readelf -d "$TARGET" | awk '/NEEDED/ {print $5}' | tr -d '[]')
return $MISSING
}
verify_target_needed "$BIN"
while read -r LIBFILE; do
verify_target_needed "$LIBFILE"
done < <(find "$APPDIR/usr/lib" \( -type f -o -type l \))
# Never bundle glibc/libstdc++/OpenSSL/libcurl/libpulse
for FORBIDDEN in libc.so libm.so libpthread.so librt.so libdl.so libgcc_s.so libstdc++.so libssl.so libcrypto.so libcurl.so libpulse.so libpulsecommon; do
if compgen -G "$APPDIR/usr/lib/${FORBIDDEN}*" > /dev/null; then
echo "::error::Forbidden bundled runtime detected: ${FORBIDDEN}"
exit 1
fi
done
# 5. Generate AppImage
PKGNAME=${{ env.ProjectName }}-linux-${{ steps.set_variables.outputs.suffix }}.AppImage
echo "pkg-name=$PKGNAME" >> $GITHUB_OUTPUT
export ARCH=${{ matrix.arch_name }}
./appimagetool-x86_64.AppImage --no-appstream $APPDIR $PKGNAME
working-directory: ${{ github.workspace }}
- name: Smoke Test Raspberry Payload
run: |
set -euo pipefail
APPDIR=./Builds/${{ matrix.buildFolder }}/${{ env.ProjectName }}.AppDir
BIN=$APPDIR/usr/bin/${{ env.ProjectName }}
SEARCH_DIRS=("$APPDIR/usr/lib" "/lib/${{ matrix.gnu_type }}" "/usr/lib/${{ matrix.gnu_type }}")
if [ ! -f "$BIN" ]; then
echo "::error::Payload binary missing: $BIN"
exit 1
fi
is_forbidden_lib() {
case "$1" in
libc.so*|libm.so*|libpthread.so*|librt.so*|libdl.so*|libgcc_s.so*|libstdc++.so*|libssl.so*|libcrypto.so*|libcurl.so*|libpulse.so*|libpulsecommon*.so*|ld-linux*.so*|libresolv.so*|libnss_*.so*|libanl.so*|libutil.so*)
return 0
;;
*)
return 1
;;
esac
}
has_dep() {
local SONAME="$1"
local DIR
for DIR in "${SEARCH_DIRS[@]}"; do
if compgen -G "$DIR/$SONAME*" > /dev/null; then
return 0
fi
done
return 1
}
verify_target_needed() {
local TARGET="$1"
local MISSING=0
while read -r SONAME; do
[ -n "$SONAME" ] || continue
if is_forbidden_lib "$SONAME"; then
continue
fi
if ! has_dep "$SONAME"; then
echo "::error::Smoke test unresolved dependency for $(basename "$TARGET"): $SONAME"
MISSING=1
fi
done < <(${{ matrix.gnu_type }}-readelf -d "$TARGET" | awk '/NEEDED/ {print $5}' | tr -d '[]')
return $MISSING
}
verify_target_needed "$BIN"
while read -r LIBFILE; do
verify_target_needed "$LIBFILE"
done < <(find "$APPDIR/usr/lib" \( -type f -o -type l \))
echo "Raspberry payload smoke test passed for ${{ matrix.arch_name }}"
- name: Upload
uses: ./.github/actions/upload
with:
pkg-name: ${{ steps.create_package.outputs.pkg-name }}
url: ${{ secrets.UPLOADURL }}
pass: ${{ secrets.UPLOADPASS }}