#!/usr/bin/env bash # build_appimage.sh — builds GizerBridge.AppImage # # Requires: bash, wget, x86_64 Linux # Downloads appimagetool and a self-contained Python 3.11 AppImage (first run # only; both are cached in .build_appimage/). No system Python is used. # # Usage: # bash build_appimage.sh # builds GizerBridge.AppImage here # bash build_appimage.sh --clean # wipe cache and rebuild set -euo pipefail APP="GizerBridge" PY_VER="3.11" PY_FULL="3.11.14" # exact release from niess/python-appimage PY_TAG="cp311" MANYLINUX="manylinux_2_28" ARCH="x86_64" SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" SRC="$SCRIPT_DIR/gizerbridge" BUILD="$SCRIPT_DIR/.build_appimage" APPDIR="$BUILD/AppDir" PYTHON_AI_NAME="python${PY_FULL}-${PY_TAG}-${PY_TAG}-${MANYLINUX}_${ARCH}.AppImage" PYTHON_AI_URL="https://github.com/niess/python-appimage/releases/download/python${PY_VER}/${PYTHON_AI_NAME}" APPIMAGETOOL_URL="https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-${ARCH}.AppImage" # ── Args ────────────────────────────────────────────────────────────────────── if [[ "${1:-}" == "--clean" ]]; then echo "Cleaning build cache..." rm -rf "$BUILD" fi mkdir -p "$BUILD" # ── 1. appimagetool ─────────────────────────────────────────────────────────── APPIMAGETOOL="$BUILD/appimagetool" if [[ ! -f "$APPIMAGETOOL" ]]; then echo "[1/5] Downloading appimagetool..." wget -q --show-progress "$APPIMAGETOOL_URL" -O "$APPIMAGETOOL" chmod +x "$APPIMAGETOOL" else echo "[1/5] appimagetool already cached." fi # ── 2. Python AppImage ──────────────────────────────────────────────────────── PYTHON_AI="$BUILD/$PYTHON_AI_NAME" if [[ ! -f "$PYTHON_AI" ]]; then echo "[2/5] Downloading Python ${PY_VER} AppImage..." wget -q --show-progress "$PYTHON_AI_URL" -O "$PYTHON_AI" chmod +x "$PYTHON_AI" else echo "[2/5] Python AppImage already cached." fi # ── 3. Extract and install dependencies ────────────────────────────────────── echo "[3/5] Extracting Python ${PY_VER} AppImage..." rm -rf "$APPDIR" "$BUILD/squashfs-root" # --appimage-extract unpacks squashfs directly without FUSE (cd "$BUILD" && "./$PYTHON_AI_NAME" --appimage-extract) mv "$BUILD/squashfs-root" "$APPDIR" PYTHON="$APPDIR/opt/python${PY_VER}/bin/python${PY_VER}" echo " Ensuring pip is available..." "$PYTHON" -m ensurepip --upgrade --quiet 2>/dev/null || true "$PYTHON" -m pip install --quiet --upgrade pip echo " Installing: requests Pillow pystray..." "$PYTHON" -m pip install --quiet --no-warn-script-location \ "requests>=2.28.0" "Pillow>=9.0.0" "pystray>=0.19.0" # ── 4. Assemble AppDir ──────────────────────────────────────────────────────── echo "[4/5] Assembling AppDir..." # App source — placed at the AppDir root so main.py's sys.path.insert works APP_PKG="$APPDIR/gizerbridge" mkdir -p "$APP_PKG" cp "$SRC"/*.py "$APP_PKG/" cp "$SRC"/icon.png "$APP_PKG/" cp "$SRC"/icon_full.png "$APP_PKG/" # AppRun — replaces the Python base AppRun. # The Python base image ships AppRun as a symlink (→ usr/bin/python3.11). # Remove it before writing so we create a regular file, not overwrite the target. rm -f "$APPDIR/AppRun" # Use $APPDIR (set by AppImage runtime for both FUSE and extract-and-run modes) cat > "$APPDIR/AppRun" << 'APPRUN_EOF' #!/bin/bash # $APPDIR is always set by the AppImage runtime export PYTHONHOME="${APPDIR}/opt/python3.11" export LD_LIBRARY_PATH="${APPDIR}/usr/lib:${APPDIR}/opt/python3.11/lib${LD_LIBRARY_PATH:+:${LD_LIBRARY_PATH}}" # Point Tcl/Tk at the bundled scripts (avoids system version conflicts) export TCL_LIBRARY="${APPDIR}/usr/share/tcltk/tcl8.6" export TK_LIBRARY="${APPDIR}/usr/share/tcltk/tk8.6" exec "${APPDIR}/opt/python3.11/bin/python3.11" "${APPDIR}/gizerbridge/main.py" "$@" APPRUN_EOF chmod +x "$APPDIR/AppRun" # .desktop — remove any .desktop files from the Python base image first rm -f "$APPDIR"/*.desktop cat > "$APPDIR/${APP}.desktop" << DESKTOP_EOF [Desktop Entry] Name=GizerBridge Comment=Sierra Chart → Tralgo Analytics Exec=AppRun Icon=gizerbridge Type=Application Categories=Office;Finance; Terminal=false StartupNotify=false StartupWMClass=gizerbridge DESKTOP_EOF # Icon (must be at AppDir root, named .png) cp "$SRC/icon.png" "$APPDIR/gizerbridge.png" # .DirIcon — used by Dolphin and other file managers as the AppImage file icon # The Python base image ships .DirIcon as a symlink; remove it first. rm -f "$APPDIR/.DirIcon" cp "$SRC/icon.png" "$APPDIR/.DirIcon" # Remove the usr/bin Python wrappers from the base image — they confuse the # AppImage runtime when it tries to locate $0 and end up with the wrong $HERE. rm -f "$APPDIR/usr/bin/python" \ "$APPDIR/usr/bin/python3" \ "$APPDIR/usr/bin/python3.11" # ── 5. Build AppImage ───────────────────────────────────────────────────────── echo "[5/5] Building AppImage..." OUTPUT="$SCRIPT_DIR/${APP}.AppImage" rm -f "$OUTPUT" # APPIMAGE_EXTRACT_AND_RUN=1 lets appimagetool run without FUSE on the host ARCH=x86_64 APPIMAGE_EXTRACT_AND_RUN=1 \ "$APPIMAGETOOL" --comp gzip "$APPDIR" "$OUTPUT" 2>&1 echo "" echo "✓ Built: $OUTPUT" echo " Size : $(du -sh "$OUTPUT" | cut -f1)" echo "" echo "Run with: ./${APP}.AppImage"