1. Install required tools
sudo apt update sudo apt install git patchelf desktop-file-utils appstream file nodejs npm libfuse2t64 curl wget https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage wget https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-x86_64.AppImage Make both appimages executable
chmod +x linuxdeploy-x86_64.AppImage appimagetool-x86_64.AppImage 2. Wheel
uv build --wheel mkdir -p build/wheels curl -L -o build/wheels/proxy_tools-0.1.0.tar.gz \ https://pypi.org/packages/source/p/proxy-tools/proxy_tools-0.1.0.tar.gz ls -l build/wheels/proxy_tools-0.1.0.tar.gz file build/wheels/proxy_tools-0.1.0.tar.gz proxy-tools has no wheel on PyPI; build one from sdist so --no-build later works
uv build --wheel --out-dir build/wheels build/wheels/proxy_tools-0.1.0.tar.gz Verify static dir is present inside wheel
unzip -l dist/*.whl | grep static Export Locked requirements
mkdir -p build uv export --frozen --no-dev --no-emit-project --extra desktop --no-hashes -o build/requirements.txt 3. uild the bundled runtime
Assemble the AppDir Layout
APP_ID="io.github.viraj_sh.uniclare_client" mkdir -p AppDir/usr/share/applications mkdir -p AppDir/usr/share/icons/hicolor/scalable/apps mkdir -p AppDir/usr/share/metainfo Copy .desktop .metainfo.xml and .svg in the AppDir
# .desktop → root AND usr/share/applications/ cp "packaging/appimage/$APP_ID.desktop" "AppDir/$APP_ID.desktop" cp "packaging/appimage/$APP_ID.desktop" "AppDir/usr/share/applications/$APP_ID.desktop" # .svg → root AND usr/share/icons/hicolor/scalable/apps/ cp "packaging/appimage/$APP_ID.svg" "AppDir/$APP_ID.svg" cp "packaging/appimage/$APP_ID.svg" "AppDir/usr/share/icons/hicolor/scalable/apps/$APP_ID.svg" # metainfo → usr/share/metainfo/ only cp "packaging/appimage/$APP_ID.metainfo.xml" "AppDir/usr/share/metainfo/$APP_ID.metainfo.xml" Create AppRun
cat > AppDir/AppRun <<'EOF' #!/bin/sh HERE="$(dirname "$(readlink -f "$0")")" U="$HERE/usr/lib/unidesk" unset PYTHONHOME PYTHONSTARTUP export PYTHONPATH="$U/site-packages" export PYTHONNOUSERSITE=1 PYTHONDONTWRITEBYTECODE=1 PYTHONSAFEPATH=1 export LD_LIBRARY_PATH="$HERE/usr/lib:$HERE/usr/lib/x86_64-linux-gnu:${LD_LIBRARY_PATH}" export PYWEBVIEW_GUI=qt QT_API=pyqt6 QTWEBENGINE_DISABLE_SANDBOX=1 export QTWEBENGINEPROCESS_PATH="$U/site-packages/PyQt6/Qt6/libexec/QtWebEngineProcess" export QTWEBENGINE_RESOURCES_PATH="$U/site-packages/PyQt6/Qt6/resources" exec "$U/python/bin/python3" -m uniclare_client.interfaces.desktop "$@" EOF chmod +x AppDir/AppRun Copy interpreter
PY_ROOT=$(dirname $(dirname $(readlink -f $(uv python find 3.12 --no-project)))) echo "PY_ROOT=$PY_ROOT" test -x "$PY_ROOT/bin/python3" && echo "OK: interpreter exists" mkdir -p AppDir/usr/lib/unidesk/python cp -a "$PY_ROOT"/. AppDir/usr/lib/unidesk/python/ uv pip install --python AppDir/usr/lib/unidesk/python/bin/python3 \ --target AppDir/usr/lib/unidesk/site-packages \ --no-deps --no-build \ --find-links build/wheels \ -r build/requirements.txt dist/uniclare_client-3.0.0-py3-none-any.whl 4. Fix QtWebEngineProcess permissions (required, otherwise white screen)
find AppDir -name "QtWebEngineProcess" -exec chmod +x {} \; 5. Package AppImage
./appimagetool-x86_64.AppImage AppDir uniclare_client.AppImage 