glowpath/backend/build.py
I. A. Naval 3218c10063
Add bundled open-webui
Use pyinstaller to compile to a single bundle
2025-06-28 15:09:35 -04:00

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()