diff --git a/.gitignore b/.gitignore index 6c7cbe6..3c5d84a 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ __pycache__/ *.AppImage __pycache__/ *.pyc +*.appimage diff --git a/build_appimage.sh b/build_appimage.sh new file mode 100755 index 0000000..9887d52 --- /dev/null +++ b/build_appimage.sh @@ -0,0 +1,142 @@ +#!/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" diff --git a/icon_full-640.png b/icon_full-640.png new file mode 100644 index 0000000..5febcb3 Binary files /dev/null and b/icon_full-640.png differ