181 lines
4.8 KiB
Python
Executable File
181 lines
4.8 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""
|
|
Build script for bundling GlowPath backend with PyInstaller.
|
|
This script handles the entire build process including dependency installation and cleanup.
|
|
"""
|
|
|
|
import os
|
|
import sys
|
|
import subprocess
|
|
import shutil
|
|
import argparse
|
|
from pathlib import Path
|
|
|
|
|
|
def run_command(cmd, cwd=None, check=True):
|
|
"""Run a command and return the result."""
|
|
print(f"Running: {' '.join(cmd)}")
|
|
result = subprocess.run(cmd, cwd=cwd, check=check, capture_output=True, text=True)
|
|
if result.stdout:
|
|
print(result.stdout)
|
|
if result.stderr:
|
|
print(result.stderr, file=sys.stderr)
|
|
return result
|
|
|
|
|
|
def clean_build():
|
|
"""Clean previous build artifacts."""
|
|
print("Cleaning previous build artifacts...")
|
|
dirs_to_clean = ["build", "dist", "__pycache__"]
|
|
files_to_clean = ["*.pyc"]
|
|
|
|
for dir_name in dirs_to_clean:
|
|
if os.path.exists(dir_name):
|
|
print(f"Removing {dir_name}")
|
|
shutil.rmtree(dir_name)
|
|
|
|
# Clean .pyc files
|
|
for root, dirs, files in os.walk("."):
|
|
for file in files:
|
|
if file.endswith(".pyc"):
|
|
os.remove(os.path.join(root, file))
|
|
|
|
|
|
def install_dependencies():
|
|
"""Install build dependencies."""
|
|
print("Installing build dependencies...")
|
|
try:
|
|
# Try uv first (faster) - sync dev dependencies
|
|
run_command(["uv", "sync", "--group", "dev"])
|
|
except (subprocess.CalledProcessError, FileNotFoundError):
|
|
# Fallback to pip
|
|
run_command(
|
|
[
|
|
sys.executable,
|
|
"-m",
|
|
"pip",
|
|
"install",
|
|
"pyinstaller",
|
|
"pyinstaller-hooks-contrib",
|
|
]
|
|
)
|
|
|
|
|
|
def build_executable(debug=False):
|
|
"""Build the executable using PyInstaller."""
|
|
print("Building executable with PyInstaller...")
|
|
|
|
cmd = ["pyinstaller"]
|
|
|
|
if debug:
|
|
cmd.extend(["--debug", "all"])
|
|
|
|
cmd.extend(["--clean", "--noconfirm", "glowpath-backend.spec"])
|
|
|
|
run_command(cmd)
|
|
|
|
|
|
def create_distribution():
|
|
"""Create a distribution package."""
|
|
print("Creating distribution package...")
|
|
|
|
dist_dir = Path("dist")
|
|
if not dist_dir.exists():
|
|
print("No dist directory found. Build may have failed.")
|
|
return False
|
|
|
|
# Create a distribution folder with all necessary files
|
|
app_name = "glowpath-backend"
|
|
bundle_dir = dist_dir / f"{app_name}-bundle"
|
|
|
|
if bundle_dir.exists():
|
|
shutil.rmtree(bundle_dir)
|
|
|
|
bundle_dir.mkdir()
|
|
|
|
# Copy the executable
|
|
exe_path = dist_dir / app_name
|
|
if exe_path.exists():
|
|
if sys.platform == "win32":
|
|
exe_path = exe_path.with_suffix(".exe")
|
|
shutil.copy2(exe_path, bundle_dir)
|
|
|
|
# Create a simple startup script
|
|
if sys.platform != "win32":
|
|
startup_script = bundle_dir / "start.sh"
|
|
startup_script.write_text(
|
|
f"""#!/bin/bash
|
|
cd "$(dirname "$0")"
|
|
./{app_name} "$@"
|
|
"""
|
|
)
|
|
startup_script.chmod(0o755)
|
|
else:
|
|
startup_script = bundle_dir / "start.bat"
|
|
startup_script.write_text(
|
|
f"""@echo off
|
|
cd /d "%~dp0"
|
|
{app_name}.exe %*
|
|
"""
|
|
)
|
|
|
|
# Create README
|
|
readme = bundle_dir / "README.txt"
|
|
readme.write_text(
|
|
f"""GlowPath Backend
|
|
===============
|
|
|
|
This is a bundled version of the GlowPath backend application.
|
|
|
|
To run the application:
|
|
- On Unix/Linux/macOS: ./start.sh
|
|
- On Windows: start.bat
|
|
- Or run the executable directly: ./{app_name}{"" if sys.platform != "win32" else ".exe"}
|
|
|
|
The application will start a web server. Check the console output for the URL to access the interface.
|
|
"""
|
|
)
|
|
|
|
print(f"Distribution package created in: {bundle_dir}")
|
|
return True
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description="Build GlowPath backend executable")
|
|
parser.add_argument("--debug", action="store_true", help="Build in debug mode")
|
|
parser.add_argument(
|
|
"--no-clean", action="store_true", help="Skip cleaning build artifacts"
|
|
)
|
|
parser.add_argument(
|
|
"--no-deps", action="store_true", help="Skip installing dependencies"
|
|
)
|
|
|
|
args = parser.parse_args()
|
|
|
|
try:
|
|
if not args.no_clean:
|
|
clean_build()
|
|
|
|
if not args.no_deps:
|
|
install_dependencies()
|
|
|
|
build_executable(debug=args.debug)
|
|
|
|
if create_distribution():
|
|
print("\n✅ Build completed successfully!")
|
|
print(f"Executable and bundle available in the 'dist' directory")
|
|
else:
|
|
print("\n❌ Build failed!")
|
|
sys.exit(1)
|
|
|
|
except subprocess.CalledProcessError as e:
|
|
print(f"\n❌ Build failed with error: {e}")
|
|
sys.exit(1)
|
|
except KeyboardInterrupt:
|
|
print("\n⚠️ Build interrupted by user")
|
|
sys.exit(1)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|