From 6a01e103686646b43d6936e2d93f5196188227c1 Mon Sep 17 00:00:00 2001 From: moonpower Date: Tue, 7 Apr 2026 09:41:34 +0200 Subject: [PATCH] Add musl CI builds and fix apt sources --- .gitea/workflows/build.yml | 157 ++++++++++++++- scripts/ci/build-linux-musl-target.sh | 231 ++++++++++++++++++++++ scripts/ci/configure-linux-apt-sources.sh | 37 ++++ 3 files changed, 419 insertions(+), 6 deletions(-) create mode 100755 scripts/ci/build-linux-musl-target.sh create mode 100755 scripts/ci/configure-linux-apt-sources.sh diff --git a/.gitea/workflows/build.yml b/.gitea/workflows/build.yml index 1c7e298..b3f65ac 100644 --- a/.gitea/workflows/build.yml +++ b/.gitea/workflows/build.yml @@ -23,15 +23,19 @@ jobs: include: - target: x64 extra_arch: "" + apt_source_arch: "" packages: "build-essential binutils cmake file git libgl1-mesa-dev libsdl2-dev make pkg-config python3 python3-jinja2 zip" - target: x32 extra_arch: "i386" + apt_source_arch: "" packages: "binutils cmake file gcc-multilib g++-multilib git libc6-dev-i386 libgl1-mesa-dev:i386 libsdl2-dev:i386 make pkg-config python3 python3-jinja2 zip" - target: arm64 extra_arch: "arm64" + apt_source_arch: "arm64" packages: "binutils cmake file gcc-aarch64-linux-gnu g++-aarch64-linux-gnu git libgl1-mesa-dev:arm64 libsdl2-dev:arm64 make pkg-config python3 python3-jinja2 zip" - target: arm32 extra_arch: "armhf" + apt_source_arch: "armhf" packages: "binutils cmake file gcc-arm-linux-gnueabihf g++-arm-linux-gnueabihf git libgl1-mesa-dev:armhf libsdl2-dev:armhf make pkg-config python3 python3-jinja2 zip" steps: - uses: actions/checkout@v4 @@ -62,12 +66,9 @@ jobs: set -euo pipefail if [ -n "${{ matrix.extra_arch }}" ]; then dpkg --add-architecture "${{ matrix.extra_arch }}" - printf '%s\n' \ - "deb [arch=${{ matrix.extra_arch }}] http://ports.ubuntu.com/ubuntu-ports noble main restricted universe multiverse" \ - "deb [arch=${{ matrix.extra_arch }}] http://ports.ubuntu.com/ubuntu-ports noble-updates main restricted universe multiverse" \ - "deb [arch=${{ matrix.extra_arch }}] http://ports.ubuntu.com/ubuntu-ports noble-security main restricted universe multiverse" \ - "deb [arch=${{ matrix.extra_arch }}] http://ports.ubuntu.com/ubuntu-ports noble-backports main restricted universe multiverse" \ - > "/etc/apt/sources.list.d/ubuntu-ports-${{ matrix.extra_arch }}.list" + fi + if [ -n "${{ matrix.apt_source_arch }}" ]; then + ./scripts/ci/configure-linux-apt-sources.sh "${{ matrix.apt_source_arch }}" fi apt-get update DEBIAN_FRONTEND=noninteractive apt-get install -y ${{ matrix.packages }} @@ -81,6 +82,150 @@ jobs: name: armsx-linux-${{ matrix.target }} path: artifacts/linux/${{ matrix.target }}/armsx-linux-${{ matrix.target }}.zip + macos-ventura: + name: macOS Ventura x64 + runs-on: macos-ventura-x64 + steps: + - uses: actions/checkout@v4 + with: + submodules: false + + - name: Fetch submodules with private repo token + shell: bash + env: + GIT_TOKEN: ${{ secrets.GIT_TOKEN }} + FSUI_SUBMODULE_TOKEN: ${{ secrets.FSUI_SUBMODULE_TOKEN }} + run: | + set -euo pipefail + token="${GIT_TOKEN:-${FSUI_SUBMODULE_TOKEN:-}}" + if [ -z "$token" ]; then + echo "GIT_TOKEN (or FSUI_SUBMODULE_TOKEN) is required to fetch the private fsui-lib submodule." >&2 + exit 1 + fi + + auth="$(printf 'x-access-token:%s' "$token" | base64 | tr -d '\n')" + git config --global http.https://git.nanodata.cloud/.extraheader "AUTHORIZATION: basic $auth" + git submodule sync --recursive + git submodule update --init --recursive --depth=1 + + - name: Validate Ventura runner + shell: bash + run: | + set -euo pipefail + if [ "$(uname -s)" != "Darwin" ]; then + echo "macOS Ventura runner required." >&2 + exit 1 + fi + if [ "$(uname -m)" != "x86_64" ]; then + echo "macOS Ventura x64 runner required." >&2 + exit 1 + fi + major="$(sw_vers -productVersion | cut -d. -f1)" + if [ "${major:-0}" -lt 13 ]; then + echo "macOS Ventura 13.x or newer required." >&2 + exit 1 + fi + + - name: Install macOS dependencies + shell: bash + run: | + set -euo pipefail + export PATH="/opt/homebrew/bin:/usr/local/bin:$PATH" + if ! command -v brew >/dev/null 2>&1; then + echo "Homebrew is required on the macOS Ventura runner." >&2 + exit 1 + fi + brew list cmake >/dev/null 2>&1 || brew install cmake + brew list sdl2 >/dev/null 2>&1 || brew install sdl2 + brew list dylibbundler >/dev/null 2>&1 || brew install dylibbundler + if ! command -v python3 >/dev/null 2>&1; then + brew install python@3 + fi + if ! python3 -c 'import importlib.util; raise SystemExit(0 if importlib.util.find_spec("jinja2") else 1)'; then + python3 -m pip install --user jinja2 + fi + + - name: Build macOS Ventura package + shell: bash + run: | + set -euo pipefail + ./build.sh macosapp + mkdir -p artifacts/macos/ventura + ( + cd . + zip -r artifacts/macos/ventura/armsx-macos-ventura-x64.zip armsx.app + ) + + - uses: actions/upload-artifact@v3 + with: + name: armsx-macos-ventura-x64 + path: artifacts/macos/ventura/armsx-macos-ventura-x64.zip + + linux-musl: + name: Linux musl ${{ matrix.target }} + runs-on: ubuntu-latest + container: + image: catthehacker/ubuntu:act-24.04 + strategy: + fail-fast: false + matrix: + include: + - target: x64 + - target: x32 + - target: arm64 + - target: arm32 + steps: + - uses: actions/checkout@v4 + with: + submodules: false + + - name: Fetch submodules with private repo token + shell: bash + env: + GIT_TOKEN: ${{ secrets.GIT_TOKEN }} + FSUI_SUBMODULE_TOKEN: ${{ secrets.FSUI_SUBMODULE_TOKEN }} + run: | + set -euo pipefail + token="${GIT_TOKEN:-${FSUI_SUBMODULE_TOKEN:-}}" + if [ -z "$token" ]; then + echo "GIT_TOKEN (or FSUI_SUBMODULE_TOKEN) is required to fetch the private fsui-lib submodule." >&2 + exit 1 + fi + + auth="$(printf 'x-access-token:%s' "$token" | base64 | tr -d '\n')" + git config --global http.https://git.nanodata.cloud/.extraheader "AUTHORIZATION: basic $auth" + git submodule sync --recursive + git submodule update --init --recursive --depth=1 + + - name: Install musl build dependencies + shell: bash + run: | + set -euo pipefail + apt-get update + DEBIAN_FRONTEND=noninteractive apt-get install -y \ + ca-certificates \ + cmake \ + curl \ + file \ + git \ + make \ + pkg-config \ + python3 \ + python3-jinja2 \ + tar \ + unzip \ + xz-utils \ + zip + + - name: Build musl package + shell: bash + run: ./scripts/ci/build-linux-musl-target.sh "${{ matrix.target }}" + + - uses: actions/upload-artifact@v3 + with: + name: armsx-linux-musl-${{ matrix.target }} + path: artifacts/linux-musl/${{ matrix.target }}/armsx-linux-musl-${{ matrix.target }}.zip + windows: name: Windows ${{ matrix.target }} runs-on: ubuntu-latest diff --git a/scripts/ci/build-linux-musl-target.sh b/scripts/ci/build-linux-musl-target.sh new file mode 100755 index 0000000..d10f361 --- /dev/null +++ b/scripts/ci/build-linux-musl-target.sh @@ -0,0 +1,231 @@ +#!/usr/bin/env bash + +set -euo pipefail + +target="${1:-}" +build_jobs="${BUILD_JOBS:-4}" +alpine_branch="${ALPINE_BRANCH:-latest-stable}" + +if [ -z "$target" ]; then + echo "Usage: $0 " >&2 + exit 1 +fi + +repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" +tool_root="$repo_root/build/ci-tools" +apk_root="$tool_root/apk" +toolchain_root="$tool_root/musl" +sysroot_root="$tool_root/sysroots" +fsui_build_dir="$repo_root/build/fsui/musl-$target" +artifact_root="$repo_root/artifacts/linux-musl/$target" +package_root="$artifact_root/armsx-linux-musl-$target" +package_zip="$artifact_root/armsx-linux-musl-$target.zip" + +mkdir -p "$tool_root" "$apk_root" "$toolchain_root" "$sysroot_root" + +download_apk_static() { + local apk_static="$apk_root/apk.static" + if [ -x "$apk_static" ]; then + printf '%s\n' "$apk_static" + return 0 + fi + + local listing filename package_url package_path + listing="$(curl -fsSL "https://dl-cdn.alpinelinux.org/alpine/${alpine_branch}/main/x86_64/")" + filename="$( + printf '%s\n' "$listing" \ + | sed -n 's/.*href="\([^"]*apk-tools-static[^"]*\.apk\)".*/\1/p' \ + | head -n1 + )" + if [ -z "$filename" ]; then + filename="$( + printf '%s\n' "$listing" \ + | grep -oE 'apk-tools-static-[^"<>[:space:]]+\.apk' \ + | head -n1 + )" + fi + + if [ -z "$filename" ]; then + echo "Unable to locate apk-tools-static package on Alpine ${alpine_branch}." >&2 + exit 1 + fi + + package_url="https://dl-cdn.alpinelinux.org/alpine/${alpine_branch}/main/x86_64/$filename" + package_path="$apk_root/$filename" + curl -fsSL "$package_url" -o "$package_path" + tar -xzf "$package_path" -C "$apk_root" sbin/apk.static + mv "$apk_root/sbin/apk.static" "$apk_static" + chmod +x "$apk_static" + rm -rf "$apk_root/sbin" "$package_path" + + printf '%s\n' "$apk_static" +} + +download_toolchain() { + local toolchain_name="$1" + local toolchain_dir="$toolchain_root/$toolchain_name-cross" + local toolchain_archive="$toolchain_root/$toolchain_name-cross.tgz" + if [ -x "$toolchain_dir/bin/${toolchain_name}-gcc" ]; then + printf '%s\n' "$toolchain_dir" + return 0 + fi + + rm -rf "$toolchain_dir" "$toolchain_archive" + curl -fsSL "https://musl.cc/${toolchain_name}-cross.tgz" -o "$toolchain_archive" + tar -xzf "$toolchain_archive" -C "$toolchain_root" + if [ ! -d "$toolchain_dir" ]; then + echo "Unable to extract musl toolchain ${toolchain_name}." >&2 + exit 1 + fi + + printf '%s\n' "$toolchain_dir" +} + +configure_target() { + case "$target" in + x64) + apk_arch="x86_64" + toolchain_name="x86_64-linux-musl" + cmake_processor="x86_64" + sysroot_packages="sdl2-dev mesa-dev" + ;; + x32) + apk_arch="x86" + toolchain_name="i686-linux-musl" + cmake_processor="i686" + sysroot_packages="sdl2-dev mesa-dev" + ;; + arm64) + apk_arch="aarch64" + toolchain_name="aarch64-linux-musl" + cmake_processor="aarch64" + sysroot_packages="sdl2-dev mesa-dev" + ;; + arm32) + apk_arch="armv7" + toolchain_name="arm-linux-musleabihf" + cmake_processor="arm" + sysroot_packages="sdl2-dev mesa-dev" + ;; + *) + echo "Unsupported musl target '$target'." >&2 + exit 1 + ;; + esac +} + +configure_target + +toolchain_dir="$(download_toolchain "$toolchain_name")" +apk_static="$(download_apk_static)" + +sysroot_dir="$sysroot_root/$target" +repo_file="$sysroot_dir/etc/apk/repositories" +rm -rf "$sysroot_dir" +mkdir -p "$sysroot_dir/etc/apk" "$sysroot_dir/var/cache/apk" "$sysroot_dir/var/lib/apk" + +cat > "$repo_file" <&2 + exit 1 +fi +sdl2_config_dir="$(dirname "$config_path")" + +toolchain_file="$tool_root/musl-$target.cmake" +cat > "$toolchain_file" < "$package_root/armsx" <<'EOF' +#!/usr/bin/env sh +set -eu +app_dir="$(cd "$(dirname "$0")" && pwd)" +export LD_LIBRARY_PATH="$app_dir/lib${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}" +loader="" +for candidate in "$app_dir"/lib/ld-musl-*.so.1; do + if [ -x "$candidate" ]; then + loader="$candidate" + break + fi +done +if [ -n "$loader" ]; then + exec "$loader" "$app_dir/armsx.bin" "$@" +fi +exec "$app_dir/armsx.bin" "$@" +EOF + +chmod +x "$package_root/armsx" + +( + cd "$artifact_root" + zip -r "$(basename "$package_zip")" "$(basename "$package_root")" +) diff --git a/scripts/ci/configure-linux-apt-sources.sh b/scripts/ci/configure-linux-apt-sources.sh new file mode 100755 index 0000000..c9d96d2 --- /dev/null +++ b/scripts/ci/configure-linux-apt-sources.sh @@ -0,0 +1,37 @@ +#!/usr/bin/env bash + +set -euo pipefail + +target_arch="${1:-}" + +case "$target_arch" in + arm64|armhf) + ;; + *) + exit 0 + ;; +esac + +backup_dir="/etc/apt/armsx-sources-backup" +mkdir -p "$backup_dir" + +for source in /etc/apt/sources.list /etc/apt/sources.list.d/*; do + [ -e "$source" ] || continue + if grep -Eq 'archive\.ubuntu\.com/ubuntu|security\.ubuntu\.com/ubuntu|ports\.ubuntu\.com/ubuntu-ports' "$source" 2>/dev/null; then + mv "$source" "$backup_dir/$(basename "$source")" + fi +done + +cat > "/etc/apt/sources.list.d/armsx-ubuntu-amd64.list" <<'EOF' +deb [arch=amd64] http://archive.ubuntu.com/ubuntu noble main restricted universe multiverse +deb [arch=amd64] http://archive.ubuntu.com/ubuntu noble-updates main restricted universe multiverse +deb [arch=amd64] http://archive.ubuntu.com/ubuntu noble-backports main restricted universe multiverse +deb [arch=amd64] http://security.ubuntu.com/ubuntu noble-security main restricted universe multiverse +EOF + +cat > "/etc/apt/sources.list.d/armsx-ubuntu-${target_arch}.list" <