mirror of
https://github.com/Architeuthis-Flux/JumperlessV5.git
synced 2025-09-07 11:17:58 +00:00
777 lines
23 KiB
Python
Executable File
777 lines
23 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""
|
|
JumperlessAppPackager.py - Universal Multi-Platform Packager for Jumperless Bridge
|
|
Creates AppImages, executables, and portable packages for Linux, Windows, and macOS
|
|
"""
|
|
|
|
import os
|
|
import sys
|
|
import shutil
|
|
import pathlib
|
|
import subprocess
|
|
import time
|
|
import zipfile
|
|
import tarfile
|
|
import json
|
|
from pathlib import Path
|
|
|
|
def cleanup_intermediate_files():
|
|
"""Clean up intermediate files created during packaging"""
|
|
print("🧹 Cleaning up intermediate files...")
|
|
|
|
# List of files and directories to clean up
|
|
cleanup_patterns = [
|
|
"dist/",
|
|
"build/",
|
|
"*.spec",
|
|
"*.AppDir/",
|
|
"__pycache__/",
|
|
"*.pyc",
|
|
"*.pyo",
|
|
".pytest_cache/",
|
|
"*.egg-info/",
|
|
".DS_Store",
|
|
"Thumbs.db",
|
|
"desktop.ini",
|
|
"Jumperless_Linux/",
|
|
"Jumperless_Windows/",
|
|
"Jumperless_macOS/",
|
|
"tools/appimagetool-*",
|
|
"JumperlessFiles/",
|
|
"venv/",
|
|
".venv/",
|
|
"env/",
|
|
".env/",
|
|
".coverage",
|
|
"htmlcov/",
|
|
".mypy_cache/",
|
|
".tox/",
|
|
"*.log",
|
|
"*.tmp",
|
|
"*.bak",
|
|
"*.swp",
|
|
"*.swo",
|
|
"*~",
|
|
".*.swp",
|
|
".*.swo"
|
|
]
|
|
|
|
# Clean up files and directories
|
|
for pattern in cleanup_patterns:
|
|
if "/" in pattern:
|
|
# Directory cleanup
|
|
dir_path = pattern.rstrip("/")
|
|
if os.path.exists(dir_path):
|
|
try:
|
|
shutil.rmtree(dir_path)
|
|
print(f" 🗑️ Removed directory: {dir_path}")
|
|
except Exception as e:
|
|
print(f" ⚠️ Could not remove {dir_path}: {e}")
|
|
else:
|
|
# File pattern cleanup
|
|
import glob
|
|
for file_path in glob.glob(pattern):
|
|
try:
|
|
if os.path.isfile(file_path):
|
|
os.remove(file_path)
|
|
print(f" 🗑️ Removed file: {file_path}")
|
|
elif os.path.isdir(file_path):
|
|
shutil.rmtree(file_path)
|
|
print(f" 🗑️ Removed directory: {file_path}")
|
|
except Exception as e:
|
|
print(f" ⚠️ Could not remove {file_path}: {e}")
|
|
|
|
print("✅ Cleanup complete!")
|
|
|
|
def package_linux_appimage():
|
|
"""Package for Linux as AppImage"""
|
|
print("=== Packaging Linux AppImage ===")
|
|
|
|
# Ensure output directory exists
|
|
appimage_dir = pathlib.Path("builds/linux/appimage")
|
|
appimage_dir.mkdir(parents=True, exist_ok=True)
|
|
|
|
# Clean up old AppImages to avoid confusion
|
|
print("🧹 Cleaning up old AppImages...")
|
|
old_appimages = list(appimage_dir.glob("Jumperless-*.AppImage"))
|
|
for old_appimage in old_appimages:
|
|
try:
|
|
old_appimage.unlink()
|
|
print(f" 🗑️ Removed old AppImage: {old_appimage.name}")
|
|
except Exception as e:
|
|
print(f" ⚠️ Could not remove {old_appimage.name}: {e}")
|
|
|
|
if old_appimages:
|
|
print(f"✅ Cleaned up {len(old_appimages)} old AppImage(s)")
|
|
else:
|
|
print("✅ No old AppImages to clean up")
|
|
|
|
# Architecture configurations
|
|
architectures = [
|
|
("x86_64", "dist/JumperlessWokwiBridge_x86_64", "JumperlessWokwiBridge.AppDir", "Jumperless-x86_64.AppImage", "JumperlessWokwiBridge"),
|
|
("aarch64", "dist/JumperlessWokwiBridge_aarch64", "JumperlessWokwiBridge_aarch64.AppDir", "Jumperless-aarch64.AppImage", "JumperlessWokwiBridge")
|
|
]
|
|
|
|
success_count = 0
|
|
arm64_success = False
|
|
|
|
for arch_name, dist_path, appdir_path, appimage_path, executable_name in architectures:
|
|
print(f"\n📦 Creating {arch_name} AppImage...")
|
|
|
|
# Create PyInstaller executable for this architecture
|
|
pyinstaller_cmd = [
|
|
sys.executable, "-m", "PyInstaller",
|
|
"--clean", # Clear cache for each build
|
|
"--onefile",
|
|
"--console",
|
|
"--name", executable_name,
|
|
"--icon", "assets/icons/icon.png",
|
|
"--distpath", dist_path,
|
|
"--specpath", f"build/spec_{arch_name}", # Unique spec path per architecture
|
|
"JumperlessWokwiBridge.py"
|
|
]
|
|
|
|
print(f"Running PyInstaller for {arch_name}...")
|
|
result = subprocess.run(pyinstaller_cmd)
|
|
|
|
if result.returncode != 0:
|
|
print(f"❌ PyInstaller failed for {arch_name}")
|
|
continue
|
|
|
|
# Create AppImage
|
|
if create_appimage_for_arch(arch_name, dist_path, appdir_path, appimage_path, executable_name):
|
|
success_count += 1
|
|
if arch_name == "aarch64":
|
|
arm64_success = True
|
|
|
|
# Create launcher scripts for AppImage directory
|
|
create_appimage_launchers(appimage_dir)
|
|
|
|
# Copy installation script to AppImage directory
|
|
try:
|
|
install_script_source = pathlib.Path("builds/linux/appimage/install_jumperless.sh")
|
|
if install_script_source.exists():
|
|
print("✅ Installation script ready")
|
|
else:
|
|
print("⚠️ Installation script not found")
|
|
except Exception as e:
|
|
print(f"⚠️ Could not check installation script: {e}")
|
|
|
|
print(f"\n🎉 Linux AppImage packaging complete! Created {success_count} architecture packages.")
|
|
|
|
# Create tar.gz archive
|
|
create_distribution_archive(appimage_dir)
|
|
|
|
# Clean up intermediate files
|
|
cleanup_intermediate_files()
|
|
|
|
return success_count > 0
|
|
|
|
def create_appimage_for_arch(arch_name, dist_path, appdir_path, appimage_path, executable_name):
|
|
"""Create AppImage for specific architecture"""
|
|
try:
|
|
# Create AppDir structure
|
|
appdir = pathlib.Path(appdir_path)
|
|
if appdir.exists():
|
|
shutil.rmtree(appdir)
|
|
appdir.mkdir(parents=True, exist_ok=True)
|
|
|
|
# Copy executable
|
|
executable_source = pathlib.Path(dist_path) / executable_name
|
|
executable_dest = appdir / "AppRun"
|
|
|
|
if not executable_source.exists():
|
|
print(f"❌ Executable not found: {executable_source}")
|
|
return False
|
|
|
|
shutil.copy2(executable_source, executable_dest)
|
|
os.chmod(executable_dest, 0o755)
|
|
|
|
# Create desktop file
|
|
desktop_content = '''[Desktop Entry]
|
|
Type=Application
|
|
Name=Jumperless Wokwi Bridge
|
|
Exec=AppRun
|
|
Icon=icon
|
|
Comment=Jumperless Wokwi Bridge - Electronics Prototyping Tool
|
|
Categories=Development;Electronics;Education;
|
|
Terminal=true
|
|
StartupNotify=false
|
|
'''
|
|
|
|
desktop_file = appdir / "Jumperless.desktop"
|
|
with open(desktop_file, 'w') as f:
|
|
f.write(desktop_content)
|
|
|
|
# Copy icon if available
|
|
icon_source = pathlib.Path("assets/icons/icon.png")
|
|
if icon_source.exists():
|
|
shutil.copy2(icon_source, appdir / "icon.png")
|
|
|
|
# Download appimagetool if needed
|
|
appimagetool_path = download_appimagetool()
|
|
if not appimagetool_path:
|
|
print("❌ Could not download appimagetool")
|
|
return False
|
|
|
|
# Create AppImage
|
|
appimage_output = pathlib.Path(f"builds/linux/appimage/{appimage_path}")
|
|
appimage_cmd = [str(appimagetool_path), str(appdir), str(appimage_output)]
|
|
|
|
print(f"Creating AppImage: {appimage_output}")
|
|
result = subprocess.run(appimage_cmd, capture_output=True, text=True)
|
|
|
|
if result.returncode == 0 and appimage_output.exists():
|
|
# Make executable
|
|
os.chmod(appimage_output, 0o755)
|
|
|
|
# Get file size
|
|
appimage_size = appimage_output.stat().st_size / (1024*1024)
|
|
print(f"✅ Created {arch_name} AppImage: {appimage_output} ({appimage_size:.1f} MB)")
|
|
|
|
# Clean up AppDir
|
|
if appdir.exists():
|
|
shutil.rmtree(appdir)
|
|
|
|
return True
|
|
else:
|
|
print(f"❌ AppImage creation failed for {arch_name}")
|
|
print(f"Error: {result.stderr}")
|
|
return False
|
|
|
|
except Exception as e:
|
|
print(f"❌ Error creating AppImage for {arch_name}: {e}")
|
|
return False
|
|
|
|
def download_appimagetool():
|
|
"""Download appimagetool if not already available"""
|
|
tools_dir = pathlib.Path("tools")
|
|
tools_dir.mkdir(exist_ok=True)
|
|
|
|
# Check current architecture
|
|
arch = os.uname().machine
|
|
if arch == "x86_64":
|
|
appimagetool_name = "appimagetool-x86_64.AppImage"
|
|
elif arch in ["aarch64", "arm64"]:
|
|
appimagetool_name = "appimagetool-aarch64.AppImage"
|
|
else:
|
|
print(f"❌ Unsupported architecture for appimagetool: {arch}")
|
|
return None
|
|
|
|
appimagetool_path = tools_dir / appimagetool_name
|
|
|
|
if appimagetool_path.exists():
|
|
os.chmod(appimagetool_path, 0o755)
|
|
return appimagetool_path
|
|
|
|
print(f"📥 Downloading {appimagetool_name}...")
|
|
download_url = f"https://github.com/AppImage/AppImageKit/releases/download/continuous/{appimagetool_name}"
|
|
|
|
try:
|
|
import urllib.request
|
|
urllib.request.urlretrieve(download_url, appimagetool_path)
|
|
os.chmod(appimagetool_path, 0o755)
|
|
print(f"✅ Downloaded {appimagetool_name}")
|
|
return appimagetool_path
|
|
except Exception as e:
|
|
print(f"❌ Failed to download appimagetool: {e}")
|
|
return None
|
|
|
|
def create_appimage_launchers(appimage_dir):
|
|
"""Create launcher scripts for AppImage directory"""
|
|
|
|
# Smart launcher script
|
|
smart_launcher = '''#!/bin/bash
|
|
|
|
# Simple Jumperless AppImage Launcher
|
|
# Detects architecture and runs the correct AppImage
|
|
|
|
echo "Starting Jumperless..."
|
|
|
|
# Detect architecture
|
|
ARCH=$(uname -m)
|
|
echo "Architecture: $ARCH"
|
|
|
|
# Set AppImage name based on architecture
|
|
case "$ARCH" in
|
|
x86_64)
|
|
APPIMAGE="Jumperless-x86_64.AppImage"
|
|
;;
|
|
aarch64|arm64)
|
|
APPIMAGE="Jumperless-aarch64.AppImage"
|
|
;;
|
|
*)
|
|
echo "Unsupported architecture: $ARCH"
|
|
echo "Supported: x86_64, aarch64/arm64"
|
|
echo "Trying x86_64 AppImage..."
|
|
APPIMAGE="Jumperless-x86_64.AppImage"
|
|
;;
|
|
esac
|
|
|
|
# Look for AppImage in current directory
|
|
if [ -f "$APPIMAGE" ]; then
|
|
APPIMAGE_PATH="./$APPIMAGE"
|
|
else
|
|
echo "AppImage not found: $APPIMAGE"
|
|
echo "Make sure you're running this script from the AppImage directory"
|
|
echo "To create AppImages, run: python3 JumperlessAppPackager.py"
|
|
exit 1
|
|
fi
|
|
|
|
# Make executable if not already
|
|
if [ ! -x "$APPIMAGE_PATH" ]; then
|
|
echo "Making AppImage executable..."
|
|
chmod +x "$APPIMAGE_PATH"
|
|
fi
|
|
|
|
# Run the AppImage
|
|
echo "Running: $APPIMAGE_PATH"
|
|
exec "$APPIMAGE_PATH" "$@"
|
|
'''
|
|
|
|
|
|
|
|
# Desktop launcher script
|
|
desktop_launcher = '''#!/bin/bash
|
|
|
|
# Desktop Launcher for Jumperless
|
|
# This script finds its own location and runs Jumperless from there
|
|
|
|
# Get the directory where this script is located
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
|
|
# Change to that directory and run the launcher
|
|
cd "$SCRIPT_DIR"
|
|
|
|
echo "Jumperless Desktop Launcher"
|
|
echo "Running from: $SCRIPT_DIR"
|
|
echo "=========================="
|
|
|
|
# Run the main launcher
|
|
./run_jumperless.sh "$@"
|
|
|
|
# Keep terminal open on exit
|
|
echo ""
|
|
echo "Jumperless has exited."
|
|
echo "Press Enter to close this window..."
|
|
read
|
|
'''
|
|
|
|
# Write launcher scripts
|
|
smart_launcher_path = appimage_dir / "run_jumperless.sh"
|
|
with open(smart_launcher_path, 'w', encoding='utf-8') as f:
|
|
f.write(smart_launcher)
|
|
os.chmod(smart_launcher_path, 0o755)
|
|
print(f"✅ Created run_jumperless.sh")
|
|
|
|
|
|
|
|
desktop_launcher_path = appimage_dir / "desktop_launcher.sh"
|
|
with open(desktop_launcher_path, 'w', encoding='utf-8') as f:
|
|
f.write(desktop_launcher)
|
|
os.chmod(desktop_launcher_path, 0o755)
|
|
print(f"✅ Created desktop_launcher.sh")
|
|
|
|
# Create desktop file
|
|
desktop_content = '''[Desktop Entry]
|
|
Version=1.0
|
|
Type=Application
|
|
Name=Jumperless
|
|
Exec=sh -c "\\\\$HOME/.local/bin/jumperless/desktop_launcher.sh"
|
|
Icon=jumperless
|
|
Comment=Jumperless Wokwi Bridge - Electronics Prototyping Tool
|
|
Categories=Development;
|
|
Terminal=true
|
|
StartupNotify=true
|
|
Path=/usr/share/applications/
|
|
'''
|
|
|
|
desktop_file_path = appimage_dir / "Jumperless.desktop"
|
|
with open(desktop_file_path, 'w', encoding='utf-8') as f:
|
|
f.write(desktop_content)
|
|
print(f"✅ Created Jumperless.desktop")
|
|
|
|
# Create uninstaller script
|
|
uninstaller_content = '''#!/bin/bash
|
|
|
|
# Jumperless Linux Uninstaller Script
|
|
# Completely removes Jumperless from standard Linux directories
|
|
|
|
set -e # Exit on any error
|
|
|
|
echo "🗑️ Jumperless Uninstaller"
|
|
echo "========================="
|
|
echo ""
|
|
|
|
# Define installation directories
|
|
INSTALL_DIR="$HOME/.local/bin/jumperless"
|
|
DESKTOP_DIR="$HOME/.local/share/applications"
|
|
ICONS_DIR="$HOME/.local/share/icons/hicolor/256x256/apps"
|
|
|
|
echo "This will completely remove Jumperless from your system:"
|
|
echo "📁 $INSTALL_DIR"
|
|
echo "📄 $DESKTOP_DIR/Jumperless.desktop"
|
|
echo "🖼️ $ICONS_DIR/jumperless.png"
|
|
echo ""
|
|
|
|
# Check if installation exists
|
|
if [ ! -d "$INSTALL_DIR" ] && [ ! -f "$DESKTOP_DIR/Jumperless.desktop" ]; then
|
|
echo "✅ Jumperless is not installed (nothing to remove)"
|
|
exit 0
|
|
fi
|
|
|
|
# Confirmation prompt
|
|
read -p "Are you sure you want to uninstall Jumperless? (y/N): " -n 1 -r
|
|
echo
|
|
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
|
echo "❌ Uninstall cancelled"
|
|
exit 0
|
|
fi
|
|
|
|
echo ""
|
|
echo "🗑️ Removing Jumperless..."
|
|
|
|
# Remove main installation directory
|
|
if [ -d "$INSTALL_DIR" ]; then
|
|
rm -rf "$INSTALL_DIR"
|
|
echo " ✅ Removed installation directory"
|
|
else
|
|
echo " ⚠️ Installation directory not found"
|
|
fi
|
|
|
|
# Remove desktop file
|
|
if [ -f "$DESKTOP_DIR/Jumperless.desktop" ]; then
|
|
rm -f "$DESKTOP_DIR/Jumperless.desktop"
|
|
echo " ✅ Removed desktop file"
|
|
else
|
|
echo " ⚠️ Desktop file not found"
|
|
fi
|
|
|
|
# Remove icon
|
|
if [ -f "$ICONS_DIR/jumperless.png" ]; then
|
|
rm -f "$ICONS_DIR/jumperless.png"
|
|
echo " ✅ Removed system icon"
|
|
else
|
|
echo " ⚠️ System icon not found"
|
|
fi
|
|
|
|
# Update desktop database
|
|
echo "🔄 Updating desktop database..."
|
|
if command -v update-desktop-database &> /dev/null; then
|
|
update-desktop-database "$DESKTOP_DIR" 2>/dev/null || true
|
|
echo " ✅ Desktop database updated"
|
|
fi
|
|
|
|
# Update icon cache
|
|
echo "🖼️ Updating icon cache..."
|
|
if command -v gtk-update-icon-cache &> /dev/null; then
|
|
gtk-update-icon-cache "$HOME/.local/share/icons/hicolor" 2>/dev/null || true
|
|
echo " ✅ Icon cache updated"
|
|
fi
|
|
|
|
echo ""
|
|
echo "✅ Jumperless has been completely uninstalled!"
|
|
echo ""
|
|
echo "🙋 If you want to reinstall later, just run:"
|
|
echo " ./install_jumperless.sh"
|
|
echo ""
|
|
echo "Thanks for using Jumperless! 👋"
|
|
'''
|
|
|
|
uninstaller_path = appimage_dir / "uninstall_jumperless.sh"
|
|
with open(uninstaller_path, 'w', encoding='utf-8') as f:
|
|
f.write(uninstaller_content)
|
|
os.chmod(uninstaller_path, 0o755)
|
|
print(f"✅ Created uninstall_jumperless.sh")
|
|
|
|
# Copy icon if available
|
|
icon_source = pathlib.Path("assets/icons/icon.png")
|
|
if icon_source.exists():
|
|
shutil.copy2(icon_source, appimage_dir / "icon.png")
|
|
print(f"✅ Copied icon.png")
|
|
|
|
# Create unified README.md
|
|
readme_content = '''# Jumperless Linux Distribution
|
|
|
|
Jumperless App distribution for Linux.
|
|
|
|
## Quick Start
|
|
|
|
### 1. Install with Desktop Integration (Recommended)
|
|
|
|
The simplest way to get Jumperless running with desktop integration:
|
|
|
|
```bash
|
|
./install_jumperless.sh
|
|
```
|
|
|
|
Benefits:
|
|
- Desktop integration (appears in Applications Menu)
|
|
- System icon support
|
|
- Follows Linux standards (XDG directories)
|
|
- Easy updates and uninstall
|
|
- Automatic architecture detection
|
|
|
|
After installation, launch from:
|
|
- Applications Menu: Search for "Jumperless"
|
|
- Terminal: `~/.local/bin/jumperless/desktop_launcher.sh`
|
|
|
|
### 2. Python Script (Auto-Updates Available)
|
|
|
|
For automatic updates from GitHub:
|
|
|
|
```bash
|
|
# Install Python dependencies first
|
|
pip install -r python/requirements.txt
|
|
|
|
# Run from source
|
|
python3 python/JumperlessWokwiBridge.py
|
|
```
|
|
|
|
When running as a Python script, Jumperless can automatically check for and download updates from GitHub, keeping you current with new features and bug fixes.
|
|
|
|
### 3. Launcher Scripts
|
|
|
|
Quick portable usage without installation:
|
|
|
|
```bash
|
|
# Smart launcher with architecture detection
|
|
./run_jumperless.sh
|
|
|
|
# Desktop-friendly launcher
|
|
./desktop_launcher.sh
|
|
```
|
|
|
|
### 4. Direct AppImage Execution
|
|
|
|
Manual execution:
|
|
|
|
```bash
|
|
# Make executable (first time only)
|
|
chmod +x Jumperless-*.AppImage
|
|
|
|
# Run directly
|
|
./Jumperless-x86_64.AppImage # Intel/AMD systems
|
|
./Jumperless-aarch64.AppImage # ARM64 systems (Raspberry Pi, etc.)
|
|
```
|
|
|
|
## What's Included
|
|
|
|
### AppImage Files
|
|
- `Jumperless-aarch64.AppImage` - ARM64 systems (Raspberry Pi 4+, Apple Silicon)
|
|
- `Jumperless-x86_64.AppImage` - Intel/AMD 64-bit systems (most PCs)
|
|
|
|
### Installation & Management
|
|
- `install_jumperless.sh` - Linux installer
|
|
- `uninstall_jumperless.sh` - Complete removal tool
|
|
- `Jumperless.desktop` - Desktop integration file
|
|
|
|
### Launcher Scripts
|
|
- `run_jumperless.sh` - Smart launcher with architecture detection
|
|
- `desktop_launcher.sh` - Desktop environment launcher
|
|
|
|
### Source Code & Dependencies
|
|
- `python/JumperlessWokwiBridge.py` - Main application source
|
|
- `python/requirements.txt` - Python dependencies
|
|
|
|
### Assets
|
|
- `icon.png` - Application icon
|
|
- `JumperlessFiles/` - Application data directory
|
|
|
|
## Method Comparison
|
|
|
|
| Method | Installation | Auto-Updates | Desktop Integration | Startup Speed |
|
|
|--------|-------------|--------------|-------------------|---------------|
|
|
| Desktop Install | One-time | Manual | Full | Fast |
|
|
| Python Script | Dependencies only | Automatic | Manual | Medium |
|
|
| Launcher Scripts | None | Manual | None | Fast |
|
|
| Direct AppImage | None | Manual | None | Fast |
|
|
|
|
## System Requirements
|
|
|
|
- OS: Linux (any distribution)
|
|
- Architecture: x86_64 or ARM64/aarch64
|
|
- Python: 3.10+ (for Python script method)
|
|
- Desktop: Any modern Linux desktop environment
|
|
|
|
## Architecture Detection
|
|
|
|
All launchers automatically detect your system:
|
|
- x86_64/amd64: Intel/AMD 64-bit processors
|
|
- aarch64/arm64: ARM 64-bit processors (Raspberry Pi, ARM servers)
|
|
|
|
## Troubleshooting
|
|
|
|
### Permission Issues
|
|
```bash
|
|
chmod +x *.sh *.AppImage
|
|
```
|
|
|
|
### Python Dependencies
|
|
```bash
|
|
cd python/
|
|
pip install -r requirements.txt
|
|
```
|
|
|
|
### Desktop Integration Not Working
|
|
```bash
|
|
./install_jumperless.sh
|
|
```
|
|
|
|
### AppImage Won't Start
|
|
```bash
|
|
./run_jumperless.sh
|
|
```
|
|
|
|
## Updates
|
|
|
|
### Automatic Updates (Python Script Only)
|
|
When running `python3 python/JumperlessWokwiBridge.py`, the app automatically:
|
|
- Checks GitHub for new versions
|
|
- Downloads updates in the background
|
|
- Keeps you current with latest features
|
|
|
|
### Manual Updates (Other Methods)
|
|
1. Download new AppImage distribution
|
|
2. Extract and replace files
|
|
3. Run installer again (if using desktop installation)
|
|
|
|
## Uninstallation
|
|
|
|
### Desktop Installation
|
|
```bash
|
|
./uninstall_jumperless.sh
|
|
```
|
|
|
|
### Manual Cleanup
|
|
```bash
|
|
rm -rf ~/.local/bin/jumperless/
|
|
rm ~/.local/share/applications/Jumperless.desktop
|
|
rm ~/.local/share/icons/hicolor/256x256/apps/jumperless.png
|
|
```
|
|
|
|
## Usage Tips
|
|
|
|
- For daily use: Use the desktop installer for best experience
|
|
- For development: Use Python script method for auto-updates
|
|
- For portability: Use launcher scripts on shared systems
|
|
- For minimal setup: Use direct AppImage execution
|
|
|
|
## Support & Documentation
|
|
|
|
- GitHub Repository: https://github.com/Architeuthis-Flux/JumperlessV5
|
|
- Support: https://discord.gg/TcjM5uEgb4
|
|
- Documentation: https://jumperless-docs.readthedocs.io/en/latest/
|
|
|
|
|
|
---
|
|
|
|
Choose the method that best fits your workflow.
|
|
'''
|
|
|
|
readme_path = appimage_dir / "README.md"
|
|
with open(readme_path, 'w', encoding='utf-8') as f:
|
|
f.write(readme_content)
|
|
print(f"✅ Created unified README.md")
|
|
|
|
# Create organized folder structure
|
|
create_organized_structure(appimage_dir)
|
|
|
|
def create_organized_structure(appimage_dir):
|
|
"""Create organized folder structure with python/ and docs/ folders"""
|
|
|
|
# Create python folder and copy source files
|
|
python_dir = appimage_dir / "python"
|
|
python_dir.mkdir(exist_ok=True)
|
|
|
|
# Copy main application file
|
|
main_app_source = pathlib.Path("JumperlessWokwiBridge.py")
|
|
if main_app_source.exists():
|
|
shutil.copy2(main_app_source, python_dir / "JumperlessWokwiBridge.py")
|
|
print(f"✅ Copied JumperlessWokwiBridge.py to python/")
|
|
|
|
# Copy requirements file
|
|
requirements_source = pathlib.Path("requirements.txt")
|
|
if requirements_source.exists():
|
|
shutil.copy2(requirements_source, python_dir / "requirements.txt")
|
|
print(f"✅ Copied requirements.txt to python/")
|
|
|
|
# Remove any old README files since we now use a single README.md
|
|
old_readme_files = [
|
|
"README_LAUNCHERS.md",
|
|
"README_AppImages.md",
|
|
"README_STANDARD_INSTALLATION.md"
|
|
]
|
|
|
|
for readme_file in old_readme_files:
|
|
readme_path = appimage_dir / readme_file
|
|
if readme_path.exists():
|
|
readme_path.unlink()
|
|
print(f"✅ Removed old {readme_file}")
|
|
|
|
print(f"✅ Created organized structure with python/ folder")
|
|
|
|
def create_distribution_archive(appimage_dir):
|
|
"""Create tar.gz archive of the AppImage directory"""
|
|
|
|
print("\n📦 Creating distribution archive...")
|
|
|
|
# Create archive path
|
|
linux_dir = pathlib.Path("builds/linux")
|
|
archive_path = linux_dir / "Jumperless-Linux.tar.gz"
|
|
|
|
# Remove any existing archive
|
|
if archive_path.exists():
|
|
archive_path.unlink()
|
|
|
|
try:
|
|
import tarfile
|
|
import tempfile
|
|
|
|
# Create a temporary clean directory
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
temp_path = pathlib.Path(temp_dir)
|
|
clean_appimage_dir = temp_path / "jumperless"
|
|
|
|
# Copy everything except fuse_hidden files
|
|
shutil.copytree(appimage_dir, clean_appimage_dir,
|
|
ignore=lambda dir, files: [f for f in files if f.startswith('.fuse_hidden')])
|
|
|
|
# Create tar.gz archive
|
|
with tarfile.open(archive_path, "w:gz") as tar:
|
|
tar.add(clean_appimage_dir, arcname="jumperless")
|
|
|
|
# Get archive size
|
|
archive_size = archive_path.stat().st_size / 1024 # KB
|
|
print(f"✅ Created distribution archive: {archive_path} ({archive_size:.0f}KB)")
|
|
|
|
except Exception as e:
|
|
print(f"❌ Failed to create archive: {e}")
|
|
|
|
def main():
|
|
"""Main function"""
|
|
print("🚀 Jumperless Universal Multi-Platform Packager")
|
|
print("=" * 50)
|
|
|
|
if not os.path.exists("JumperlessWokwiBridge.py"):
|
|
print("❌ JumperlessWokwiBridge.py not found!")
|
|
print("Make sure you're running this script from the project root directory.")
|
|
return 1
|
|
|
|
# Default to Linux AppImage packaging
|
|
success = package_linux_appimage()
|
|
|
|
if success:
|
|
print("\n🎉 Packaging completed successfully!")
|
|
print("\n📦 Your packages are ready in the builds/ directory:")
|
|
print(" builds/linux/appimage/")
|
|
print("\n🚀 To run:")
|
|
print(" cd builds/linux/appimage/")
|
|
print(" ./run_jumperless.sh")
|
|
return 0
|
|
else:
|
|
print("\n❌ Packaging failed!")
|
|
return 1
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main()) |