From b8c219a474470e1a2322da674aac92e0ebab5226 Mon Sep 17 00:00:00 2001 From: Dicov Date: Fri, 11 Sep 2026 23:28:12 -0400 Subject: [PATCH] feat: empaquetado AppImage AnyLinux (sharun, portable total) Sustituye Ubuntu+linuxdeploy por el flujo certificado de Anylinux-AppImages: build en Arch, deploy con quick-sharun (glibc y ld-linux propios, DwarFS + uruntime), paquetes mesa debloated y test con xvfb. El AppImage resultante corre en cualquier distro, musl y NixOS sin FHS-wrapper. - trinity-appimage.sh: receta autocontenida (engine via secrets.MCPE_NX, tapk-extract, Trinity; install en /usr; deploy; datos linux-bin; sidecar 32-bit aislado en lib32/; make-appimage + test). Aplica patches/libc-shim-fcntl-getlk.patch al engine tras clonarlo. - .github/workflows/anylinux-appimage.yml: matrix x86_64 + aarch64 en ghcr.io/pkgforge-dev/archlinux:latest, publica en latest. - patches/libc-shim-fcntl-getlk.patch: el shim necesita fcntl GETLK(5)/SETLKW(7) porque el libsqliteX del juego los sondea al iniciar (si no: SIGABRT). Los workflows linuxdeploy existentes quedan intactos. --- .github/workflows/anylinux-appimage.yml | 57 ++++++ patches/libc-shim-fcntl-getlk.patch | 66 +++++++ trinity-appimage.sh | 228 ++++++++++++++++++++++++ 3 files changed, 351 insertions(+) create mode 100644 .github/workflows/anylinux-appimage.yml create mode 100644 patches/libc-shim-fcntl-getlk.patch create mode 100755 trinity-appimage.sh diff --git a/.github/workflows/anylinux-appimage.yml b/.github/workflows/anylinux-appimage.yml new file mode 100644 index 00000000..585d81ca --- /dev/null +++ b/.github/workflows/anylinux-appimage.yml @@ -0,0 +1,57 @@ +name: AnyLinux AppImage (sharun, portable total) + +on: + workflow_dispatch: + +jobs: + build: + name: "Trinity AnyLinux (${{ matrix.arch }})" + runs-on: ${{ matrix.runs-on }} + strategy: + fail-fast: false + matrix: + include: + - arch: x86_64 + runs-on: ubuntu-24.04 + - arch: aarch64 + runs-on: ubuntu-24.04-arm + # Regla anylinux: compilar SOLO en Arch Linux (Fedora/Ubuntu rompen el deploy) + container: ghcr.io/pkgforge-dev/archlinux:latest + + steps: + - name: Clonar repositorio Trinity + uses: actions/checkout@v4 + + - name: Clonar mcpe-nx + run: | + git clone ${{ secrets.MCPE_NX }} mcpe-nx > /dev/null 2>&1 + + - name: Construir AppImage AnyLinux + run: | + sleep 10 # el runner a veces monta el checkout del commit anterior + sh ./trinity-appimage.sh + + - name: Subir artefacto + uses: actions/upload-artifact@v4 + with: + name: Trinity_Launcher-${{ matrix.arch }}-anylinux + path: dist + + release: + if: ${{ needs.build.result == 'success' }} + needs: [build] + permissions: write-all + runs-on: ubuntu-latest + steps: + - uses: actions/download-artifact@v4 + with: + pattern: Trinity_Launcher-*-anylinux + merge-multiple: true + + - name: Subir a Release (latest) + uses: softprops/action-gh-release@v2 + with: + tag_name: latest + files: | + Trinity_Launcher-*.AppImage + *.zsync diff --git a/patches/libc-shim-fcntl-getlk.patch b/patches/libc-shim-fcntl-getlk.patch new file mode 100644 index 00000000..5726e29b --- /dev/null +++ b/patches/libc-shim-fcntl-getlk.patch @@ -0,0 +1,66 @@ +From 3531d78b40d63cf99543a7a38456adfd2be6e4ba Mon Sep 17 00:00:00 2001 +From: Dicov +Date: Fri, 11 Sep 2026 20:53:54 -0400 +Subject: [PATCH] fix: libc-shim implementa fcntl GETLK(5) y SETLKW(7) + +libsqliteX (telemetria del juego) sondea F_GETLK al iniciar y el shim +lanzaba runtime_error -> SIGABRT. Se traduce struct flock igual que +SETLK y en GETLK se devuelven los campos al llamante. +--- + libc-shim/src/file_misc.cpp | 17 +++++++++++++++-- + libc-shim/src/file_misc.h | 4 +++- + 2 files changed, 18 insertions(+), 3 deletions(-) + +diff --git a/libc-shim/src/file_misc.cpp b/libc-shim/src/file_misc.cpp +index 37e55b8..eaf92d2 100644 +--- a/libc-shim/src/file_misc.cpp ++++ b/libc-shim/src/file_misc.cpp +@@ -175,7 +175,9 @@ int shim::fcntl(int fd, bionic::fcntl_index cmd, void *arg) { + return (int)bionic::from_host_file_status_flags(::fcntl(fd, F_GETFL)); + case bionic::fcntl_index::SETFL: + return ::fcntl(fd, F_SETFL, to_host_file_status_flags((bionic::file_status_flags) (size_t) arg)); +- case bionic::fcntl_index::SETLK: { ++ case bionic::fcntl_index::GETLK: ++ case bionic::fcntl_index::SETLK: ++ case bionic::fcntl_index::SETLKW: { + auto afl = (bionic::flock *) arg; + struct flock fl {}; + fl.l_type = afl->l_type; +@@ -183,7 +185,18 @@ int shim::fcntl(int fd, bionic::fcntl_index cmd, void *arg) { + fl.l_start = afl->l_start; + fl.l_len = afl->l_len; + fl.l_pid = afl->l_pid; +- return ::fcntl(fd, F_SETLK, &fl); ++ int host_cmd = (cmd == bionic::fcntl_index::GETLK) ? F_GETLK ++ : (cmd == bionic::fcntl_index::SETLKW) ? F_SETLKW : F_SETLK; ++ int ret = ::fcntl(fd, host_cmd, &fl); ++ if (ret != -1 && cmd == bionic::fcntl_index::GETLK) { ++ // GETLK devuelve el bloqueo conflictivo en la misma estructura ++ afl->l_type = fl.l_type; ++ afl->l_whence = fl.l_whence; ++ afl->l_start = fl.l_start; ++ afl->l_len = fl.l_len; ++ afl->l_pid = fl.l_pid; ++ } ++ return ret; + } + default: + handle_runtime_error("Unsupported fcntl %d", (int)cmd); +diff --git a/libc-shim/src/file_misc.h b/libc-shim/src/file_misc.h +index 48ebded..5a602cd 100644 +--- a/libc-shim/src/file_misc.h ++++ b/libc-shim/src/file_misc.h +@@ -38,7 +38,9 @@ namespace shim { + SETFD = 2, + GETFL = 3, + SETFL = 4, +- SETLK = 6 ++ GETLK = 5, ++ SETLK = 6, ++ SETLKW = 7 + }; + + struct flock { +-- +2.55.0 + diff --git a/trinity-appimage.sh b/trinity-appimage.sh new file mode 100755 index 00000000..dbc761af --- /dev/null +++ b/trinity-appimage.sh @@ -0,0 +1,228 @@ +#!/bin/sh +# Trinity Launcher — AnyLinux AppImage (metodo quick-sharun/sharun). +# +# Construye un AppImage 100% portable (glibc + ld-linux propios, DwarFS + +# uruntime): corre en cualquier distro, musl y NixOS sin FHS-wrapper. +# +# REGLAS (https://github.com/pkgforge-dev/Anylinux-AppImages): +# - Compilar SOLO en Arch Linux. Nunca Fedora ni Ubuntu. +# - La app debe estar instalada en /usr ANTES de empaquetar. +# - Nunca copiar .so/binarios a mano al AppDir: se pasan como args a +# quick-sharun y el hace el deploy (ldd + strace de dlopens + sharun). +# - Ignorar guias externas (linuxdeploy, appimage-builder, docs.appimage). +# +# Uso local (en Arch): sh ./trinity-appimage.sh +# En CI lo llama .github/workflows/anylinux-appimage.yml dentro del +# contenedor ghcr.io/pkgforge-dev/archlinux:latest. +# +# Env opcionales: +# MCPE_NX URL/repo del engine (por defecto manifest publico, rama qt6) +# UPINFO update-information para el hook self-updater +# OUTPATH destino del AppImage (por defecto ./dist) + +set -eux + +ARCH="$(uname -m)" +ROOT="$PWD" +OUTPATH="${OUTPATH:-$ROOT/dist}" +OUTNAME="Trinity_Launcher-$ARCH.AppImage" +SHARUN_URL="https://raw.githubusercontent.com/pkgforge-dev/Anylinux-AppImages/refs/heads/main/useful-tools/quick-sharun.sh" +DEBLOAT_URL="https://raw.githubusercontent.com/pkgforge-dev/Anylinux-AppImages/refs/heads/main/useful-tools/get-debloated-pkgs.sh" +ZIG_VER="0.16.0" + +if [ "$(id -u)" -eq 0 ]; then + SUDO="" +else + SUDO="sudo" +fi + +echo "=== 1/7 Dependencias del sistema (Arch) ===" +$SUDO pacman -Syu --noconfirm \ + base-devel git curl wget cmake clang ninja patchelf zsync \ + xorg-server-xvfb pciutils hwdata dbus \ + qt6-base qt6-declarative qt6-webengine qt6-svg qt6-tools qt6-translations \ + libzip libpng libpulse alsa-lib pipewire jack2 sndio \ + libx11 libxi libxext libxfixes libxcursor libxrandr libxss libxtst \ + libxcb libxkbcommon libxkbcommon-x11 xcb-util-wm \ + mesa vulkan-headers vulkan-validation-layers libdrm \ + libevdev libusb bluez-libs ibus libunwind libdecor wayland \ + libcups openssl curl + +echo "=== 2/7 Paquetes debloated (mesa sin LLVM completo, icu/qt/gtk minis) ===" +wget --retry-connrefused --tries=30 "$DEBLOAT_URL" -O ./get-debloated-pkgs.sh +chmod +x ./get-debloated-pkgs.sh +./get-debloated-pkgs.sh --add-common --prefer-nano + +echo "=== 3/7 Fuentes del engine + datos ===" +if [ ! -d mcpe-nx ]; then + # MCPE_NX trae la URL privada (secreto del CI, igual que en los + # workflows simple-appimage/nightly); sin el se usa el manifest + # publico en su rama qt6 (ver docs/BUILD.md). + ENGINE_URL="${MCPE_NX:-https://github.com/minecraft-linux/mcpelauncher-manifest.git}" + # Silenciado a proposito: la URL puede llevar credenciales y este + # script corre con xtrace (set -x). + set +x + git clone "$ENGINE_URL" mcpe-nx > /dev/null 2>&1 + if [ -z "${MCPE_NX:-}" ]; then + git -C mcpe-nx checkout qt6 > /dev/null 2>&1 || true + git -C mcpe-nx submodule update --init --recursive > /dev/null 2>&1 || true + fi + set -x +fi +# Parche del engine: libc-shim necesita fcntl GETLK(5)/SETLKW(7) porque el +# libsqliteX del juego los sondea al iniciar (si no: SIGABRT). Se omite en +# silencio si el engine ya lo trae. +if [ -f patches/libc-shim-fcntl-getlk.patch ] && [ -d mcpe-nx/libc-shim ]; then + (cd mcpe-nx && patch -p1 --forward < ../patches/libc-shim-fcntl-getlk.patch) || true +fi +if [ ! -d tapk-extract ]; then + git clone https://gitlab.com/javiercplus/tapk-extract.git tapk-extract +fi +if [ ! -d linux-bin ] && [ ! -d mcpe-nx/mcpelauncher-linux-bin ]; then + git clone https://github.com/minecraft-linux/mcpelauncher-linux-bin.git linux-bin +fi +# El engine trae sdl3/ y mcpelauncher-linux-bin/ vendoreados y no usa +# submodulos: un clone plano basta. +if [ "$ARCH" = "x86_64" ] && [ ! -d 32bitmcpe ]; then + # HuggingFace limita por IP (429): reintentos con espera + for i in 1 2 3 4 5 6 7 8 9 10; do + if wget --retry-connrefused --tries=5 \ + https://huggingface.co/datasets/ccoffee20/PEPE/resolve/main/mcpe32bit.tar \ + -O mcpe32bit.tar; then + break + fi + echo "Descarga 429/fallida (intento $i/10), esperando 60s..." + rm -f mcpe32bit.tar + sleep 60 + done + tar -xf mcpe32bit.tar +fi + +echo "=== 4/7 Compilar engine (sin GUI, SDL3) + extractor (Zig) + Trinity ===" +export CC=clang +export CXX=clang++ +cmake -S mcpe-nx -B mcpe-nx/build -G Ninja \ + -DCMAKE_BUILD_TYPE=Release \ + -DBUILD_WEBVIEW=OFF \ + -DGAMEWINDOW_SYSTEM=SDL3 \ + -DBUILD_UI=OFF \ + -DENABLE_DEV_PATHS=OFF \ + -DCMAKE_POLICY_VERSION_MINIMUM=3.5 \ + -Wno-dev +cmake --build mcpe-nx/build --parallel "$(nproc)" + +ZIG_TAR="zig-$ARCH-linux-$ZIG_VER.tar.xz" +if ! command -v zig >/dev/null 2>&1; then + wget -q "https://ziglang.org/download/$ZIG_VER/$ZIG_TAR" + tar -xf "$ZIG_TAR" + export PATH="$ROOT/zig-$ARCH-linux-$ZIG_VER:$PATH" +fi +(cd tapk-extract && zig build --release=fast -Dtarget=native -Dcpu=baseline) + +if [ "$ARCH" != "x86_64" ]; then + # Sin flags x86 en ARM (igual que el workflow appimage-arm) + find . -name "CMakeLists.txt" -exec sed -i 's/-msse3\b//g;s/-msse4[^ ]*//g;s/-mavx[^ ]*//g' {} \; + sed -i 's/-msse3\b//g;s/-msse4[^ ]*//g;s/-mavx[^ ]*//g' build.sh || true +fi +cmake -S . -B build -G Ninja \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_C_COMPILER=clang \ + -DCMAKE_CXX_COMPILER=clang++ \ + -Wno-dev +cmake --build build --parallel "$(nproc)" + +echo "=== 5/7 Instalar todo en /usr (requisito de quick-sharun) ===" +$SUDO install -Dm755 build/app/trinity /usr/bin/trinity +$SUDO install -Dm755 mcpe-nx/build/mcpelauncher-client/mcpelauncher-client /usr/bin/mcpelauncher-client +$SUDO install -Dm755 tapk-extract/zig-out/bin/tapk-extract /usr/bin/mcpelauncher-extract +for helper in msa-daemon mcpelauncher-error; do + found="$(find mcpe-nx/build -type f -name "$helper" -print | head -n 1)" || true + if [ -n "$found" ]; then + $SUDO install -Dm755 "$found" "/usr/bin/$helper" + fi +done +$SUDO install -Dm644 resources/shortcuts/com.trench.trinity.launcher.desktop \ + /usr/share/applications/com.trench.trinity.launcher.desktop +$SUDO install -Dm644 resources/branding/com.trench.trinity.launcher.svg \ + /usr/share/icons/hicolor/scalable/apps/com.trench.trinity.launcher.svg +$SUDO mkdir -p /usr/share/mcpelauncher +if [ -d mcpe-nx/mcpelauncher-linux-bin ]; then + $SUDO cp -r mcpe-nx/mcpelauncher-linux-bin/. /usr/share/mcpelauncher/ +else + $SUDO cp -r linux-bin/. /usr/share/mcpelauncher/ +fi + +echo "=== 6/7 Deploy con quick-sharun (jamas copiar .so a mano) ===" +wget --retry-connrefused --tries=30 "$SHARUN_URL" -O ./quick-sharun +chmod +x ./quick-sharun + +export ICON=/usr/share/icons/hicolor/scalable/apps/com.trench.trinity.launcher.svg +export DESKTOP=/usr/share/applications/com.trench.trinity.launcher.desktop +export MAIN_BIN=trinity +export OUTPATH OUTNAME +export ADD_HOOKS="self-updater.hook:fix-namespaces.hook" +export DEPLOY_OPENGL=1 DEPLOY_VULKAN=1 DEPLOY_SDL=1 + +BINS="/usr/bin/trinity /usr/bin/mcpelauncher-client /usr/bin/mcpelauncher-extract /usr/bin/lspci" +for helper in msa-daemon mcpelauncher-error; do + if [ -x "/usr/bin/$helper" ]; then + BINS="$BINS /usr/bin/$helper" + fi +done +# shellcheck disable=SC2086 +./quick-sharun $BINS + +echo "=== 6b/7 Datos extra + sidecar 32-bit (x86_64) ===" +# linux-bin: datos del engine, no son ELF asi que van directo a share/ +mkdir -p AppDir/share/mcpelauncher +cp -r /usr/share/mcpelauncher/. AppDir/share/mcpelauncher/ +# pci.ids para lspci (deteccion de GPU del gestor de contenido) +if [ -f /usr/share/hwdata/pci.ids ]; then + mkdir -p AppDir/share/hwdata + cp -f /usr/share/hwdata/pci.ids AppDir/share/hwdata/ +fi +# Vars de runtime que el AppRun/sharun expande al lanzar (sin expandir aqui) +echo 'MCPELAUNCHER_DATA_DIR=${SHARUN_DIR}/share/mcpelauncher' >> AppDir/.env +echo 'PCI_IDS=${SHARUN_DIR}/share/hwdata/pci.ids' >> AppDir/.env +# Aislamiento anti-crash en distros con userland viejo (Void/musl, etc): +# - fusion: el host puede traer QT_QPA_PLATFORMTHEME=gtk3 y el plugin +# libqgtk3 empaquetado contra un GTK3 ajeno al del host -> mezcla y SIGSEGV +# - GIO_MODULE_DIR/GIO_USE_VFS: impide que GIO sondee los modulos gio/gvfs +# del host (incompatibles con el glib empaquetado -> SIGSEGV) +echo 'QT_QPA_PLATFORMTHEME=fusion' >> AppDir/.env +echo 'GIO_MODULE_DIR=${SHARUN_DIR}/lib/gio/modules' >> AppDir/.env +echo 'GIO_USE_VFS=local' >> AppDir/.env + +if [ "$ARCH" = "x86_64" ] && [ -f 32bitmcpe/bin/mcpelauncher-client86 ]; then + # El helper de 32-bit no puede mezclarse en lib/ (colisionaria con los + # .so de 64-bit con el mismo nombre), asi que viaja autocontenido en + # lib32/ con su propio ld-linux --library-path: el mismo mecanismo + # certificado de HOW-TO-MAKE-THESE.md, sin LD_LIBRARY_PATH. + mkdir -p AppDir/lib32 AppDir/bin + cp -f 32bitmcpe/bin/mcpelauncher-client86 AppDir/bin/.mcpelauncher-client86.real + chmod +x AppDir/bin/.mcpelauncher-client86.real + cp -f 32bitmcpe/lib32/*.so* AppDir/lib32/ 2>/dev/null || true + if [ -f 32bitmcpe/lib32/ld-linux.so.2 ]; then + cp -f 32bitmcpe/lib32/ld-linux.so.2 AppDir/lib32/ + elif [ -f /usr/lib32/ld-linux.so.2 ]; then + cp -f /usr/lib32/ld-linux.so.2 AppDir/lib32/ + fi + # Limpieza heredada del workflow clasico: esos GL/X de 32-bit pisan + # los del host y rompen el arranque en GPUs modernas. + rm -f AppDir/lib32/libGLdispatch.so* AppDir/lib32/libX*.so* AppDir/lib32/libxcb*.so* + cat > AppDir/bin/mcpelauncher-client86 <<'EOF' +#!/bin/sh +# Sidecar 32-bit: loader propio, sin tocar el entorno del proceso padre. +HERE="$(dirname "$(readlink -f "$0")")/.." +HERE="$(readlink -f "$HERE")" +exec "$HERE"/lib32/ld-linux.so.2 --library-path "$HERE"/lib32 "$HERE"/bin/.mcpelauncher-client86.real "$@" +EOF + chmod +x AppDir/bin/mcpelauncher-client86 +fi + +echo "=== 7/7 Empaquetar (DwarFS + uruntime) y test ===" +export OUTPATH OUTNAME +./quick-sharun --make-appimage +./quick-sharun --test ./dist/*.AppImage + +echo "Listo: $OUTPATH/$OUTNAME"