feat: Implement configuration bundling for customer-specific builds and enhance build scripts

This commit is contained in:
claudi 2026-01-30 11:09:19 +01:00
parent 4e5deab7e9
commit a355c13c82
5 changed files with 750 additions and 22 deletions

View file

@ -9,13 +9,21 @@ Requirements:
- For MSI: WiX Toolset (optional, requires separate installation)
Usage:
python build_windows.py [--msi] [--code-sign]
python build_windows.py [--msi] [--code-sign] [--env-file PATH]
Options:
--msi Create MSI installer (requires WiX Toolset)
--code-sign Sign executable (requires certificate)
--env-file PATH Use custom .env file (default: project root .env)
If not provided, uses .env from project root
Build fails if .env doesn't exist
"""
import sys
import subprocess
import os
import shutil
import argparse
from pathlib import Path
from datetime import datetime
@ -33,14 +41,35 @@ if sys.platform == "win32":
class WindowsBuilder:
"""Build Windows installer using PyInstaller."""
def __init__(self):
"""Initialize builder paths."""
def __init__(self, env_file: Path | None = None):
"""Initialize builder paths.
Args:
env_file: Path to .env file to bundle. If None, uses project root .env.
If that doesn't exist, raises error.
"""
self.project_root = Path(__file__).parent.parent.parent
self.build_dir = self.project_root / "build"
self.dist_dir = self.build_dir / "dist" / "windows"
self.temp_dir = self.build_dir / "temp" / "windows"
self.spec_file = self.build_dir / "webdrop_bridge.spec"
self.version = get_current_version()
# Validate and set env file
if env_file is None:
env_file = self.project_root / ".env"
else:
env_file = Path(env_file).resolve()
if not env_file.exists():
raise FileNotFoundError(
f"Configuration file not found: {env_file}\n"
f"Please provide a .env file using --env-file parameter\n"
f"or ensure .env exists in project root"
)
self.env_file = env_file
print(f"📋 Using configuration: {self.env_file}")
def _get_version(self) -> str:
"""Get version from __init__.py.
@ -66,6 +95,7 @@ class WindowsBuilder:
self.temp_dir.mkdir(parents=True, exist_ok=True)
# PyInstaller command using spec file
# Pass env_file path as environment variable for spec to pick up
cmd = [
sys.executable,
"-m",
@ -78,11 +108,17 @@ class WindowsBuilder:
]
print(f" Command: {' '.join(cmd)}")
# Set environment variable for spec file to use
env = os.environ.copy()
env["WEBDROP_ENV_FILE"] = str(self.env_file)
result = subprocess.run(
cmd,
cwd=str(self.project_root),
encoding="utf-8",
errors="replace"
errors="replace",
env=env
)
if result.returncode != 0:
@ -341,28 +377,40 @@ class WindowsBuilder:
return True
def sync_version() -> None:
"""Sync version from __init__.py to all dependent files."""
script_path = Path(__file__).parent.parent.parent / "scripts" / "sync_version.py"
result = subprocess.run(
[sys.executable, str(script_path)],
capture_output=True,
text=True,
encoding="utf-8",
)
if result.returncode != 0:
print(f"❌ Version sync failed: {result.stderr}")
sys.exit(1)
print(result.stdout)
def main() -> int:
"""Build Windows MSI installer."""
parser = argparse.ArgumentParser(
description="Build WebDrop Bridge Windows installer"
)
parser.add_argument(
"--msi",
action="store_true",
help="Create MSI installer (requires WiX Toolset)",
)
parser.add_argument(
"--code-sign",
action="store_true",
help="Sign executable (requires certificate in CODE_SIGN_CERT env var)",
)
parser.add_argument(
"--env-file",
type=str,
default=None,
help="Path to .env file to bundle (default: project root .env)",
)
args = parser.parse_args()
print("🔄 Syncing version...")
sync_version()
builder = WindowsBuilder()
success = builder.build(create_msi=True, sign=False)
try:
builder = WindowsBuilder(env_file=args.env_file)
except FileNotFoundError as e:
print(f"❌ Build failed: {e}")
return 1
success = builder.build(create_msi=args.msi, sign=args.code_sign)
return 0 if success else 1