Enhance build process and GUI for Path Juggler. Updated build script to use Nuitka for creating a standalone macOS app. Added .DS_Store and build artifacts to .gitignore. Refactored GUI to utilize PySide6, improving layout and styling. Updated logging mechanism for thread-safe operations and enhanced status indicators.
This commit is contained in:
178
build_app.sh
Normal file → Executable file
178
build_app.sh
Normal file → Executable file
@@ -1,7 +1,8 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Build script for Path Juggler
|
||||
# Creates a standalone macOS .app bundle with ad-hoc code signing
|
||||
# Creates a standalone macOS .app bundle using Nuitka
|
||||
# Uses PySide6 (Qt) for GUI - fully bundleable, no system dependencies
|
||||
#
|
||||
|
||||
set -e
|
||||
@@ -13,146 +14,107 @@ VENV_DIR="$SCRIPT_DIR/.build_venv"
|
||||
DIST_DIR="$SCRIPT_DIR/dist"
|
||||
|
||||
echo "=== Path Juggler - Build Script ==="
|
||||
echo "Using Nuitka + PySide6 for a fully self-contained build"
|
||||
echo ""
|
||||
|
||||
# Check for Python 3
|
||||
if ! command -v python3 &> /dev/null; then
|
||||
echo "Error: python3 is required but not found"
|
||||
# Check for uv
|
||||
if ! command -v uv &> /dev/null; then
|
||||
echo "Error: uv is required but not found"
|
||||
echo ""
|
||||
echo "Install uv with:"
|
||||
echo " curl -LsSf https://astral.sh/uv/install.sh | sh"
|
||||
echo " or: brew install uv"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
PYTHON_VERSION=$(python3 -c 'import sys; print(f"{sys.version_info.major}.{sys.version_info.minor}")')
|
||||
echo "Using Python $PYTHON_VERSION"
|
||||
|
||||
# Create virtual environment
|
||||
echo "Using uv version: $(uv --version)"
|
||||
echo ""
|
||||
|
||||
# Create virtual environment with Python 3.11 (good compatibility)
|
||||
echo "=== Creating virtual environment ==="
|
||||
if [ -d "$VENV_DIR" ]; then
|
||||
echo "Removing existing build environment..."
|
||||
rm -rf "$VENV_DIR"
|
||||
fi
|
||||
|
||||
python3 -m venv "$VENV_DIR"
|
||||
uv venv "$VENV_DIR" --python 3.11
|
||||
source "$VENV_DIR/bin/activate"
|
||||
|
||||
# Upgrade pip
|
||||
PYTHON_VERSION=$(python -c 'import sys; print(f"{sys.version_info.major}.{sys.version_info.minor}")')
|
||||
echo "Python version: $PYTHON_VERSION"
|
||||
|
||||
# Install build dependencies
|
||||
echo ""
|
||||
echo "=== Installing build dependencies ==="
|
||||
pip install --upgrade pip --quiet
|
||||
pip install pyinstaller watchdog --quiet
|
||||
uv pip install --quiet PySide6 watchdog nuitka ordered-set zstandard
|
||||
|
||||
echo "Installed:"
|
||||
pip list | grep -E "(pyinstaller|watchdog)"
|
||||
echo "Installed packages:"
|
||||
uv pip list | grep -iE "(pyside6|watchdog|nuitka)" || true
|
||||
|
||||
# Create PyInstaller spec file for more control
|
||||
# Build with Nuitka
|
||||
echo ""
|
||||
echo "=== Creating build configuration ==="
|
||||
echo "=== Building application with Nuitka ==="
|
||||
|
||||
cat > "$SCRIPT_DIR/path_juggler.spec" << 'SPEC'
|
||||
# -*- mode: python ; coding: utf-8 -*-
|
||||
# Clean previous build
|
||||
rm -rf "$DIST_DIR/$APP_NAME.app" 2>/dev/null || true
|
||||
rm -rf "$DIST_DIR"/*.build 2>/dev/null || true
|
||||
rm -rf "$DIST_DIR"/*.dist 2>/dev/null || true
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
python -m nuitka \
|
||||
--standalone \
|
||||
--macos-create-app-bundle \
|
||||
--macos-app-name="$APP_NAME" \
|
||||
--product-name="$APP_NAME" \
|
||||
--product-version="1.0.0" \
|
||||
--company-name="PathJuggler" \
|
||||
--file-description="File organization utility" \
|
||||
--enable-plugin=pyside6 \
|
||||
--include-module=watchdog.observers \
|
||||
--include-module=watchdog.observers.fsevents \
|
||||
--include-module=watchdog.events \
|
||||
--output-dir="$DIST_DIR" \
|
||||
--remove-output \
|
||||
--assume-yes-for-downloads \
|
||||
path_juggler_gui.py
|
||||
|
||||
block_cipher = None
|
||||
|
||||
a = Analysis(
|
||||
['path_juggler_gui.py'],
|
||||
pathex=[],
|
||||
binaries=[],
|
||||
datas=[],
|
||||
hiddenimports=[
|
||||
'watchdog.observers',
|
||||
'watchdog.observers.fsevents',
|
||||
'watchdog.events',
|
||||
],
|
||||
hookspath=[],
|
||||
hooksconfig={},
|
||||
runtime_hooks=[],
|
||||
excludes=[],
|
||||
win_no_prefer_redirects=False,
|
||||
win_private_assemblies=False,
|
||||
cipher=block_cipher,
|
||||
noarchive=False,
|
||||
)
|
||||
|
||||
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
|
||||
|
||||
exe = EXE(
|
||||
pyz,
|
||||
a.scripts,
|
||||
[],
|
||||
exclude_binaries=True,
|
||||
name='Path Juggler',
|
||||
debug=False,
|
||||
bootloader_ignore_signals=False,
|
||||
strip=False,
|
||||
upx=True,
|
||||
console=False, # No terminal window
|
||||
disable_windowed_traceback=False,
|
||||
argv_emulation=True, # Important for macOS
|
||||
target_arch=None,
|
||||
codesign_identity=None,
|
||||
entitlements_file=None,
|
||||
)
|
||||
|
||||
coll = COLLECT(
|
||||
exe,
|
||||
a.binaries,
|
||||
a.zipfiles,
|
||||
a.datas,
|
||||
strip=False,
|
||||
upx=True,
|
||||
upx_exclude=[],
|
||||
name='Path Juggler',
|
||||
)
|
||||
|
||||
app = BUNDLE(
|
||||
coll,
|
||||
name='Path Juggler.app',
|
||||
icon=None,
|
||||
bundle_identifier='com.pathjuggler.app',
|
||||
info_plist={
|
||||
'CFBundleName': 'Path Juggler',
|
||||
'CFBundleDisplayName': 'Path Juggler',
|
||||
'CFBundleVersion': '1.0.0',
|
||||
'CFBundleShortVersionString': '1.0.0',
|
||||
'NSHighResolutionCapable': True,
|
||||
'LSMinimumSystemVersion': '10.13.0',
|
||||
'NSAppleEventsUsageDescription': 'Path Juggler needs to access files.',
|
||||
},
|
||||
)
|
||||
SPEC
|
||||
|
||||
# Build the app
|
||||
echo ""
|
||||
echo "=== Building application ==="
|
||||
pyinstaller --clean --noconfirm path_juggler.spec
|
||||
|
||||
# Sign the app with ad-hoc signature
|
||||
echo ""
|
||||
echo "=== Signing application (ad-hoc) ==="
|
||||
APP_PATH="$DIST_DIR/Path Juggler.app"
|
||||
# Find the generated app
|
||||
APP_PATH="$DIST_DIR/path_juggler_gui.app"
|
||||
FINAL_APP_PATH="$DIST_DIR/$APP_NAME.app"
|
||||
|
||||
if [ -d "$APP_PATH" ]; then
|
||||
# Remove any existing signatures
|
||||
codesign --remove-signature "$APP_PATH" 2>/dev/null || true
|
||||
# Rename to proper name
|
||||
rm -rf "$FINAL_APP_PATH" 2>/dev/null || true
|
||||
mv "$APP_PATH" "$FINAL_APP_PATH"
|
||||
|
||||
# Sign with ad-hoc signature (dash means ad-hoc)
|
||||
codesign --force --deep --sign - "$APP_PATH"
|
||||
# Update Info.plist with proper bundle ID
|
||||
PLIST="$FINAL_APP_PATH/Contents/Info.plist"
|
||||
if [ -f "$PLIST" ]; then
|
||||
/usr/libexec/PlistBuddy -c "Set :CFBundleIdentifier $BUNDLE_ID" "$PLIST" 2>/dev/null || true
|
||||
/usr/libexec/PlistBuddy -c "Set :CFBundleName $APP_NAME" "$PLIST" 2>/dev/null || true
|
||||
/usr/libexec/PlistBuddy -c "Set :CFBundleDisplayName $APP_NAME" "$PLIST" 2>/dev/null || true
|
||||
fi
|
||||
|
||||
# Sign the app
|
||||
echo ""
|
||||
echo "=== Signing application (ad-hoc) ==="
|
||||
codesign --remove-signature "$FINAL_APP_PATH" 2>/dev/null || true
|
||||
codesign --force --deep --sign - "$FINAL_APP_PATH"
|
||||
|
||||
echo "Signature applied"
|
||||
codesign -dv "$APP_PATH" 2>&1 | head -5
|
||||
codesign -dv "$FINAL_APP_PATH" 2>&1 | head -5
|
||||
else
|
||||
echo "Error: App bundle not found at $APP_PATH"
|
||||
echo "Error: App bundle not found"
|
||||
echo "Looking for: $APP_PATH"
|
||||
ls -la "$DIST_DIR/"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Clean up
|
||||
echo ""
|
||||
echo "=== Cleaning up ==="
|
||||
rm -rf "$SCRIPT_DIR/build"
|
||||
rm -f "$SCRIPT_DIR/path_juggler.spec"
|
||||
rm -rf "$DIST_DIR"/*.build 2>/dev/null || true
|
||||
rm -rf "$DIST_DIR"/*.dist 2>/dev/null || true
|
||||
rm -rf "$DIST_DIR"/*.onefile-build 2>/dev/null || true
|
||||
deactivate
|
||||
rm -rf "$VENV_DIR"
|
||||
|
||||
@@ -160,7 +122,7 @@ echo ""
|
||||
echo "=== Build complete ==="
|
||||
echo ""
|
||||
echo "Application created at:"
|
||||
echo " $APP_PATH"
|
||||
echo " $FINAL_APP_PATH"
|
||||
echo ""
|
||||
echo "To install, drag the app to your Applications folder:"
|
||||
echo " open \"$DIST_DIR\""
|
||||
|
||||
Reference in New Issue
Block a user