From 9b4e0f5b1f743a2f4111cb11983fedfe44037ef6 Mon Sep 17 00:00:00 2001 From: SRHTNIN Date: Tue, 13 Oct 2026 18:47:24 +0200 Subject: [PATCH] Bundle libsecret for systems without it Co-Authored-By: Claude Opus 5.5 --- Packaging/BuildAppImage.sh | 8 +++++ Packaging/RestoreLibraryPath.py | 40 +++++++++++++++++++++++-- Packaging/SaraSuite/Templates/Launch.sh | 4 +++ 3 files changed, 50 insertions(+), 2 deletions(-) diff --git a/Packaging/BuildAppImage.sh b/Packaging/BuildAppImage.sh index 30f95fd..8d087f2 100755 --- a/Packaging/BuildAppImage.sh +++ b/Packaging/BuildAppImage.sh @@ -17,9 +17,17 @@ podman run --rm --security-opt label=disable -v "$PWD:/src" -w /src \ quay.io/pypa/manylinux_2_28_x86_64 bash -euo pipefail -c ' uv venv --quiet --allow-existing --managed-python --python 3.14 build/Container/venv uv pip install --quiet --python build/Container/venv -r requirements.txt pyinstaller + # The Flet window needs libsecret, which some systems lack: bundled as a + # fallback (RestoreLibraryPath.py uses only what the system lacks). + dnf -y -q install libsecret >/dev/null + mkdir -p build/FallbackLibs + for Library in libsecret-1.so.0 libgcrypt.so.20 libgpg-error.so.0; do cp -L /usr/lib64/$Library build/FallbackLibs/; done + # Each finds the others beside it, wherever they are put (a client lib folder). + patchelf --set-rpath "\$ORIGIN" build/FallbackLibs/libsecret-1.so.0 build/FallbackLibs/libgcrypt.so.20 build/Container/venv/bin/pyinstaller --noconfirm --clean --log-level WARN \ --onefile --windowed --name SaraSort --collect-data flet \ --runtime-hook "$PWD/Packaging/RestoreLibraryPath.py" \ + --add-data "$PWD/build/FallbackLibs:FallbackLibs" \ --add-data "$PWD/assets:assets" \ --distpath build/PyInstaller/dist --workpath build/PyInstaller/work --specpath build/PyInstaller \ "$PWD/Main.py"' diff --git a/Packaging/RestoreLibraryPath.py b/Packaging/RestoreLibraryPath.py index 446798a..3be7522 100644 --- a/Packaging/RestoreLibraryPath.py +++ b/Packaging/RestoreLibraryPath.py @@ -3,9 +3,45 @@ # desktop client then loads the bundled (older) libstdc++ instead of the # system's and won't start ("GLIBCXX_3.4.29 not found"). Give them the # original value back; the app itself has already loaded what it needs. +# +# Flet's window also needs libsecret, which isn't part of GTK and some +# systems don't install ("libsecret-1.so.0: cannot open shared object file"). +# The build bundles it in FallbackLibs, with libgcrypt and libgpg-error, which +# it needs; the ones this system lacks (only those) are offered to what the +# app starts, after the original path. import os +import subprocess +import sys -if "LD_LIBRARY_PATH_ORIG" in os.environ: - os.environ["LD_LIBRARY_PATH"] = os.environ["LD_LIBRARY_PATH_ORIG"] + +def SystemHas(Name): + """Whether the system itself has a library: asked of its linker cache + and standard folders, not dlopen, which would also find this bundle's + own copies (PyInstaller adds what bundled libraries need).""" + for Folder in ("/usr/lib64", "/usr/lib", "/lib64", "/lib", "/usr/lib/x86_64-linux-gnu", "/lib/x86_64-linux-gnu"): + if os.path.exists(os.path.join(Folder, Name)): + return True + try: + Cache = subprocess.run(["ldconfig", "-p"], capture_output=True, text=True, timeout=10, + env={"PATH": "/usr/sbin:/usr/bin:/sbin:/bin"}).stdout + except (OSError, subprocess.SubprocessError): + return False + return any(Line.strip().startswith(Name + " ") for Line in Cache.splitlines()) + +Paths = [os.environ["LD_LIBRARY_PATH_ORIG"]] if os.environ.get("LD_LIBRARY_PATH_ORIG") else [] + +Bundle = getattr(sys, "_MEIPASS", "") +Fallback = os.path.join(Bundle, "FallbackLibs") +if sys.platform.startswith("linux") and os.path.isdir(Fallback): + InUse = os.path.join(Bundle, "FallbackLibsInUse") + os.makedirs(InUse, exist_ok=True) + for Name in ("libgpg-error.so.0", "libgcrypt.so.20", "libsecret-1.so.0"): + if not SystemHas(Name) and not os.path.lexists(os.path.join(InUse, Name)): + os.symlink(os.path.join(Fallback, Name), os.path.join(InUse, Name)) + if os.listdir(InUse): + Paths.append(InUse) + +if Paths: + os.environ["LD_LIBRARY_PATH"] = os.pathsep.join(Paths) else: os.environ.pop("LD_LIBRARY_PATH", None) diff --git a/Packaging/SaraSuite/Templates/Launch.sh b/Packaging/SaraSuite/Templates/Launch.sh index 70e1413..72b2ae9 100755 --- a/Packaging/SaraSuite/Templates/Launch.sh +++ b/Packaging/SaraSuite/Templates/Launch.sh @@ -4,5 +4,9 @@ # matches the window to {Name}.desktop (StartupWMClass) instead of showing it # as "com.appveyor.flet". Empty if that fails: Flet then uses its own client. export FLET_VIEW_PATH="$("{Python}" "{ProgramDir}/FletIdentity.py" "srhtnin.{Name}")" +# Libraries Flet's window needs that this system lacks (libsecret), from +# SaraSuite: FletIdentity.py puts them beside the client copy. +Fallback="$FLET_VIEW_PATH/../fallback-libs" +[ -n "$FLET_VIEW_PATH" ] && [ -d "$Fallback" ] && export LD_LIBRARY_PATH="${LD_LIBRARY_PATH:+$LD_LIBRARY_PATH:}$Fallback" cd "{AppDir}" || exit 1 exec "{Python}" Main.py "$@"