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