#!/usr/bin/env bash # Baut HeronVPN.app via py2app (Menuebar-Only, kein Dock via LSUIElement), # generiert ein "H"-Icon, duennt aus, installiert nach ~/Applications und legt # einen LaunchAgent fuer optionalen Autostart bereit. set -euo pipefail PY="/Library/Developer/CommandLineTools/usr/bin/python3" APPS="$HOME/apps" APP="$HOME/Applications/HeronVPN.app" BID="com.heron.vpntray" LSREG=/System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support/lsregister cd "$APPS" # --- 0. Icon erzeugen (H-Badge -> icon_heron.icns) --- TMP="$(mktemp -d)" QT_QPA_PLATFORM=offscreen "$PY" - "$APPS/heron_mask.png" "$TMP/h.png" <<'PYEOF' import sys from PyQt6.QtGui import QImage, QPainter, QColor, qAlpha, qRgba from PyQt6.QtCore import Qt # Reiher-Maske gruen einfaerben und mittig auf quadratische Flaeche setzen. mask = QImage(sys.argv[1]).convertToFormat(QImage.Format.Format_ARGB32) tgt = QColor("#2e7d32") for y in range(mask.height()): for x in range(mask.width()): a = qAlpha(mask.pixel(x, y)) if a: mask.setPixel(x, y, qRgba(tgt.red(), tgt.green(), tgt.blue(), a)) S = 1024 inner = int(S * 0.80) scaled = mask.scaled(inner, inner, Qt.AspectRatioMode.KeepAspectRatio, Qt.TransformationMode.SmoothTransformation) canvas = QImage(S, S, QImage.Format.Format_ARGB32) canvas.fill(Qt.GlobalColor.transparent) p = QPainter(canvas) p.drawImage((S - scaled.width()) // 2, (S - scaled.height()) // 2, scaled) p.end() canvas.save(sys.argv[2]) PYEOF ISET="$TMP/icon.iconset"; mkdir -p "$ISET" for s in 16 32 128 256 512; do sips -z $s $s "$TMP/h.png" --out "$ISET/icon_${s}x${s}.png" >/dev/null sips -z $((s*2)) $((s*2)) "$TMP/h.png" --out "$ISET/icon_${s}x${s}@2x.png" >/dev/null done iconutil -c icns "$ISET" -o "$APPS/icon_heron.icns" rm -rf "$TMP" # --- 1. Bauen --- rm -rf build dist "$PY" setup_heron.py py2app >/tmp/heron_py2app.log 2>&1 || { tail -20 /tmp/heron_py2app.log; exit 1; } # --- 2. Nach ~/Applications uebernehmen --- pkill -f "HeronVPN.app/Contents/MacOS/HeronVPN" 2>/dev/null || true rm -rf "$APP" cp -R "$APPS/dist/HeronVPN.app" "$APP" # --- 2b. Ausduennen (wie Wuerth) --- QT="$APP/Contents/Resources/lib/python3.9/PyQt6/Qt6" KEEP="QtCore QtGui QtWidgets QtDBus QtNetwork QtPrintSupport QtSvg QtSvgWidgets" for fw in "$QT/lib"/*.framework; do name="$(basename "$fw" .framework)" echo " $KEEP " | grep -q " $name " || rm -rf "$fw" done rm -rf "$QT/qml" "$QT/translations" "$QT/qsci" if [ -d "$QT/plugins" ]; then for d in "$QT/plugins"/*/; do case "$(basename "$d")" in platforms|styles|imageformats|iconengines) ;; *) rm -rf "$d";; esac done fi PYQT="$APP/Contents/Resources/lib/python3.9/PyQt6" for m in QtQml QtQuick QtQuick3D QtQuickWidgets QtPdf QtPdfWidgets QtDesigner \ QtMultimedia QtMultimediaWidgets Qt3DCore QtCharts QtDataVisualization \ QtSql QtTest QtBluetooth QtPositioning QtSensors QtWebChannel \ QtWebSockets QtNfc QtSpatialAudio QtShaderTools QtOpenGL QtOpenGLWidgets; do find "$PYQT" -maxdepth 1 -name "${m}.*so" -delete 2>/dev/null || true done while IFS= read -r f; do archs="$(lipo -archs "$f" 2>/dev/null || true)" if [[ "$archs" == *x86_64* && "$archs" == *arm64* ]]; then lipo "$f" -thin arm64 -output "$f" 2>/dev/null || true fi done < <(find "$APP" -type f \( -name "*.so" -o -name "*.dylib" -o -perm -u+x \)) find "$APP" -type f \( -name "*.so" -o -name "*.dylib" \) -exec strip -x {} \; 2>/dev/null || true codesign --force --deep -s - "$APP" >/dev/null 2>&1 # --- 3. Build-Ausgabe entfernen --- "$LSREG" -u "$APPS/dist/HeronVPN.app" 2>/dev/null || true rm -rf build dist # --- 4. registrieren --- "$LSREG" -f "$APP" # --- 5. LaunchAgent --- LA="$HOME/Library/LaunchAgents/$BID.plist" mkdir -p "$HOME/Library/LaunchAgents" cat >"$LA" < Label$BID ProgramArguments /usr/bin/open-a$APP RunAtLoad KeepAlive AGENT echo "Fertig: $APP" echo "Autostart aktivieren: launchctl load $LA" echo "Autostart deaktivieren: launchctl unload $LA"