feat: Implement brand-aware release creation for Agravity
Some checks failed
Tests & Quality Checks / Test on Python 3.11 (push) Has been cancelled
Tests & Quality Checks / Test on Python 3.12 (push) Has been cancelled
Tests & Quality Checks / Test on Python 3.11-1 (push) Has been cancelled
Tests & Quality Checks / Test on Python 3.12-1 (push) Has been cancelled
Tests & Quality Checks / Test on Python 3.10 (push) Has been cancelled
Tests & Quality Checks / Test on Python 3.11-2 (push) Has been cancelled
Tests & Quality Checks / Test on Python 3.12-2 (push) Has been cancelled
Tests & Quality Checks / Build Artifacts (push) Has been cancelled
Tests & Quality Checks / Build Artifacts-1 (push) Has been cancelled
Some checks failed
Tests & Quality Checks / Test on Python 3.11 (push) Has been cancelled
Tests & Quality Checks / Test on Python 3.12 (push) Has been cancelled
Tests & Quality Checks / Test on Python 3.11-1 (push) Has been cancelled
Tests & Quality Checks / Test on Python 3.12-1 (push) Has been cancelled
Tests & Quality Checks / Test on Python 3.10 (push) Has been cancelled
Tests & Quality Checks / Test on Python 3.11-2 (push) Has been cancelled
Tests & Quality Checks / Test on Python 3.12-2 (push) Has been cancelled
Tests & Quality Checks / Build Artifacts (push) Has been cancelled
Tests & Quality Checks / Build Artifacts-1 (push) Has been cancelled
- Added support for multiple brands in release scripts, allowing for branded artifacts. - Introduced brand configuration management with JSON files for each brand. - Created a new `brand_config.py` script to handle brand-specific logic and asset resolution. - Updated `create_release.ps1` and `create_release.sh` scripts to utilize brand configurations and generate release manifests. - Added unit tests for brand configuration loading and release manifest generation. - Introduced `agravity` brand with its specific configuration in `agravity.json`.
This commit is contained in:
parent
b988532aaa
commit
fd69996c53
8 changed files with 552 additions and 409 deletions
|
|
@ -39,13 +39,14 @@ from pathlib import Path
|
|||
from datetime import datetime
|
||||
|
||||
# Import shared version utilities
|
||||
from brand_config import load_brand_config
|
||||
from sync_version import get_current_version, do_sync_version
|
||||
|
||||
|
||||
class WindowsBuilder:
|
||||
"""Build Windows installer using PyInstaller."""
|
||||
|
||||
def __init__(self, env_file: Path | None = None):
|
||||
def __init__(self, env_file: Path | None = None, brand: str | None = None):
|
||||
"""Initialize builder paths.
|
||||
|
||||
Args:
|
||||
|
|
@ -53,10 +54,12 @@ class WindowsBuilder:
|
|||
If that doesn't exist, raises error.
|
||||
"""
|
||||
self.project_root = Path(__file__).parent.parent.parent
|
||||
self.brand = load_brand_config(brand, root=self.project_root)
|
||||
self.build_dir = self.project_root / "build"
|
||||
self.dist_dir = self.build_dir / "dist" / "windows"
|
||||
self.temp_dir = self.build_dir / "temp" / "windows"
|
||||
self.dist_dir = self.build_dir / "dist" / "windows" / self.brand.brand_id
|
||||
self.temp_dir = self.build_dir / "temp" / "windows" / self.brand.brand_id
|
||||
self.spec_file = self.build_dir / "webdrop_bridge.spec"
|
||||
self.wix_template = self.build_dir / "WebDropBridge.wxs"
|
||||
self.version = get_current_version()
|
||||
|
||||
# Validate and set env file
|
||||
|
|
@ -74,6 +77,7 @@ class WindowsBuilder:
|
|||
|
||||
self.env_file = env_file
|
||||
print(f"📋 Using configuration: {self.env_file}")
|
||||
print(f"🏷️ Building brand: {self.brand.display_name} ({self.brand.brand_id})")
|
||||
|
||||
def _get_version(self) -> str:
|
||||
"""Get version from __init__.py.
|
||||
|
|
@ -116,6 +120,15 @@ class WindowsBuilder:
|
|||
# Set environment variable for spec file to use
|
||||
env = os.environ.copy()
|
||||
env["WEBDROP_ENV_FILE"] = str(self.env_file)
|
||||
env["WEBDROP_BRAND_ID"] = self.brand.brand_id
|
||||
env["WEBDROP_APP_DISPLAY_NAME"] = self.brand.display_name
|
||||
env["WEBDROP_ASSET_PREFIX"] = self.brand.asset_prefix
|
||||
env["WEBDROP_EXE_NAME"] = self.brand.exe_name
|
||||
env["WEBDROP_BUNDLE_ID"] = self.brand.bundle_identifier
|
||||
env["WEBDROP_CONFIG_DIR_NAME"] = self.brand.config_dir_name
|
||||
env["WEBDROP_ICON_ICO"] = str(self.brand.icon_ico)
|
||||
env["WEBDROP_ICON_ICNS"] = str(self.brand.icon_icns)
|
||||
env["WEBDROP_VERSION"] = self.version
|
||||
|
||||
result = subprocess.run(cmd, cwd=str(self.project_root), text=True, env=env)
|
||||
|
||||
|
|
@ -123,8 +136,8 @@ class WindowsBuilder:
|
|||
print("❌ PyInstaller build failed")
|
||||
return False
|
||||
|
||||
# Check if executable exists (now in WebDropBridge/WebDropBridge.exe due to COLLECT)
|
||||
exe_path = self.dist_dir / "WebDropBridge" / "WebDropBridge.exe"
|
||||
# Check if executable exists (inside the COLLECT directory)
|
||||
exe_path = self.dist_dir / self.brand.exe_name / f"{self.brand.exe_name}.exe"
|
||||
if not exe_path.exists():
|
||||
print(f"❌ Executable not found at {exe_path}")
|
||||
return False
|
||||
|
|
@ -134,7 +147,9 @@ class WindowsBuilder:
|
|||
|
||||
# Calculate total dist size
|
||||
total_size = sum(
|
||||
f.stat().st_size for f in self.dist_dir.glob("WebDropBridge/**/*") if f.is_file()
|
||||
f.stat().st_size
|
||||
for f in self.dist_dir.glob(f"{self.brand.exe_name}/**/*")
|
||||
if f.is_file()
|
||||
)
|
||||
if total_size > 0:
|
||||
print(f" Total size: {total_size / 1024 / 1024:.1f} MB")
|
||||
|
|
@ -249,7 +264,7 @@ class WindowsBuilder:
|
|||
|
||||
# Harvest application files using Heat
|
||||
print(f" Harvesting application files...")
|
||||
dist_folder = self.dist_dir / "WebDropBridge"
|
||||
dist_folder = self.dist_dir / self.brand.exe_name
|
||||
if not dist_folder.exists():
|
||||
print(f"❌ Distribution folder not found: {dist_folder}")
|
||||
return False
|
||||
|
|
@ -291,7 +306,7 @@ class WindowsBuilder:
|
|||
# Compile both WiX files
|
||||
wix_obj = self.build_dir / "WebDropBridge.wixobj"
|
||||
wix_files_obj = self.build_dir / "WebDropBridge_Files.wixobj"
|
||||
msi_output = self.dist_dir / f"WebDropBridge-{self.version}-Setup.msi"
|
||||
msi_output = self.dist_dir / self.brand.windows_installer_name(self.version)
|
||||
|
||||
# Run candle compiler - make sure to use correct source directory
|
||||
candle_cmd = [
|
||||
|
|
@ -301,11 +316,11 @@ class WindowsBuilder:
|
|||
"-ext",
|
||||
"WixUtilExtension",
|
||||
f"-dDistDir={self.dist_dir}",
|
||||
f"-dSourceDir={self.dist_dir}\\WebDropBridge", # Set SourceDir for Heat-generated files
|
||||
f"-dSourceDir={self.dist_dir}\{self.brand.exe_name}", # Set SourceDir for Heat-generated files
|
||||
f"-dResourcesDir={self.project_root}\\resources", # Set ResourcesDir for branding assets
|
||||
"-o",
|
||||
str(self.build_dir) + "\\",
|
||||
str(self.build_dir / "WebDropBridge.wxs"),
|
||||
str(self.build_dir / "WebDropBridge.generated.wxs"),
|
||||
]
|
||||
|
||||
if harvest_file.exists():
|
||||
|
|
@ -325,7 +340,7 @@ class WindowsBuilder:
|
|||
"-ext",
|
||||
"WixUtilExtension",
|
||||
"-b",
|
||||
str(self.dist_dir / "WebDropBridge"), # Base path for source files
|
||||
str(self.dist_dir / self.brand.exe_name), # Base path for source files
|
||||
"-o",
|
||||
str(msi_output),
|
||||
str(wix_obj),
|
||||
|
|
@ -353,6 +368,7 @@ class WindowsBuilder:
|
|||
print("✅ MSI installer created successfully")
|
||||
print(f"📦 Output: {msi_output}")
|
||||
print(f" Size: {msi_output.stat().st_size / 1024 / 1024:.1f} MB")
|
||||
self.generate_checksum(msi_output)
|
||||
|
||||
return True
|
||||
|
||||
|
|
@ -363,7 +379,7 @@ class WindowsBuilder:
|
|||
even if a previous PyInstaller run omitted them.
|
||||
"""
|
||||
src_icons_dir = self.project_root / "resources" / "icons"
|
||||
bundle_icons_dir = self.dist_dir / "WebDropBridge" / "_internal" / "resources" / "icons"
|
||||
bundle_icons_dir = self.dist_dir / self.brand.exe_name / "_internal" / "resources" / "icons"
|
||||
required_icons = ["home.ico", "reload.ico", "open.ico", "openwith.ico"]
|
||||
|
||||
try:
|
||||
|
|
@ -392,97 +408,23 @@ class WindowsBuilder:
|
|||
Creates per-machine installation (Program Files).
|
||||
Installation requires admin rights, but the app does not.
|
||||
"""
|
||||
wix_content = f"""<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi"
|
||||
xmlns:ui="http://schemas.microsoft.com/wix/2010/ui"
|
||||
xmlns:util="http://schemas.microsoft.com/wix/UtilExtension">
|
||||
<Product Id="*" Name="WebDrop Bridge" Language="1033" Version="{self.version}"
|
||||
Manufacturer="HIM-Tools"
|
||||
UpgradeCode="12345678-1234-1234-1234-123456789012">
|
||||
wix_template = self.wix_template.read_text(encoding="utf-8")
|
||||
wix_content = wix_template.format(
|
||||
product_name=self.brand.display_name,
|
||||
version=self.version,
|
||||
manufacturer=self.brand.manufacturer,
|
||||
upgrade_code=self.brand.msi_upgrade_code,
|
||||
asset_prefix=self.brand.asset_prefix,
|
||||
icon_ico=str(self.brand.icon_ico),
|
||||
dialog_bmp=str(self.brand.dialog_bmp),
|
||||
banner_bmp=str(self.brand.banner_bmp),
|
||||
license_rtf=str(self.brand.license_rtf),
|
||||
exe_name=self.brand.exe_name,
|
||||
install_dir_name=self.brand.install_dir_name,
|
||||
shortcut_description=self.brand.shortcut_description,
|
||||
)
|
||||
|
||||
<Package InstallerVersion="200" Compressed="yes" InstallScope="perMachine" Platform="x64" />
|
||||
<Media Id="1" Cabinet="WebDropBridge.cab" EmbedCab="yes" />
|
||||
|
||||
<!-- Required property for WixUI_InstallDir dialog set -->
|
||||
<Property Id="WIXUI_INSTALLDIR" Value="INSTALLFOLDER" />
|
||||
|
||||
<!-- Application Icon -->
|
||||
<Icon Id="AppIcon.ico" SourceFile="$(var.ResourcesDir)\\icons\\app.ico" />
|
||||
|
||||
<!-- Custom branding for InstallDir dialog set -->
|
||||
<WixVariable Id="WixUIDialogBmp" Value="$(var.ResourcesDir)\\icons\\background.bmp" />
|
||||
<WixVariable Id="WixUIBannerBmp" Value="$(var.ResourcesDir)\\icons\\banner.bmp" />
|
||||
<WixVariable Id="WixUILicenseRtf" Value="$(var.ResourcesDir)\\license.rtf" />
|
||||
|
||||
<!-- Installation UI dialogs -->
|
||||
<UIRef Id="WixUI_InstallDir" />
|
||||
<UIRef Id="WixUI_ErrorProgressText" />
|
||||
|
||||
<!-- Close running application before installation -->
|
||||
<util:CloseApplication
|
||||
Target="WebDropBridge.exe"
|
||||
CloseMessage="yes"
|
||||
RebootPrompt="no"
|
||||
ElevatedCloseMessage="no" />
|
||||
|
||||
<Feature Id="ProductFeature" Title="WebDrop Bridge" Level="1">
|
||||
<ComponentGroupRef Id="AppFiles" />
|
||||
<ComponentRef Id="ProgramMenuShortcut" />
|
||||
<ComponentRef Id="DesktopShortcut" />
|
||||
</Feature>
|
||||
|
||||
<Directory Id="TARGETDIR" Name="SourceDir">
|
||||
<Directory Id="ProgramFiles64Folder">
|
||||
<Directory Id="INSTALLFOLDER" Name="WebDrop Bridge" />
|
||||
</Directory>
|
||||
<Directory Id="ProgramMenuFolder">
|
||||
<Directory Id="ApplicationProgramsFolder" Name="WebDrop Bridge"/>
|
||||
</Directory>
|
||||
<Directory Id="DesktopFolder" />
|
||||
</Directory>
|
||||
|
||||
<DirectoryRef Id="ApplicationProgramsFolder">
|
||||
<Component Id="ProgramMenuShortcut" Guid="*">
|
||||
<Shortcut Id="ApplicationStartMenuShortcut"
|
||||
Name="WebDrop Bridge"
|
||||
Description="Web Drag-and-Drop Bridge"
|
||||
Target="[INSTALLFOLDER]WebDropBridge.exe"
|
||||
Icon="AppIcon.ico"
|
||||
IconIndex="0"
|
||||
WorkingDirectory="INSTALLFOLDER" />
|
||||
<RemoveFolder Id="ApplicationProgramsFolderRemove"
|
||||
On="uninstall" />
|
||||
<RegistryValue Root="HKCU"
|
||||
Key="Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\WebDropBridge"
|
||||
Name="installed"
|
||||
Type="integer"
|
||||
Value="1"
|
||||
KeyPath="yes" />
|
||||
</Component>
|
||||
</DirectoryRef>
|
||||
|
||||
<DirectoryRef Id="DesktopFolder">
|
||||
<Component Id="DesktopShortcut" Guid="*">
|
||||
<Shortcut Id="DesktopApplicationShortcut"
|
||||
Name="WebDrop Bridge"
|
||||
Description="Web Drag-and-Drop Bridge"
|
||||
Target="[INSTALLFOLDER]WebDropBridge.exe"
|
||||
Icon="AppIcon.ico"
|
||||
IconIndex="0"
|
||||
WorkingDirectory="INSTALLFOLDER" />
|
||||
<RegistryValue Root="HKCU"
|
||||
Key="Software\\WebDropBridge"
|
||||
Name="DesktopShortcut"
|
||||
Type="integer"
|
||||
Value="1"
|
||||
KeyPath="yes" />
|
||||
</Component>
|
||||
</DirectoryRef>
|
||||
</Product>
|
||||
</Wix>
|
||||
"""
|
||||
|
||||
wix_file = self.build_dir / "WebDropBridge.wxs"
|
||||
wix_file = self.build_dir / "WebDropBridge.generated.wxs"
|
||||
wix_file.write_text(wix_content)
|
||||
print(f" Created WiX source: {wix_file}")
|
||||
return True
|
||||
|
|
@ -573,7 +515,7 @@ class WindowsBuilder:
|
|||
print(" Skipping code signing")
|
||||
return True
|
||||
|
||||
exe_path = self.dist_dir / "WebDropBridge.exe"
|
||||
exe_path = self.dist_dir / self.brand.exe_name / f"{self.brand.exe_name}.exe"
|
||||
cmd = [
|
||||
signtool,
|
||||
"sign",
|
||||
|
|
@ -606,7 +548,7 @@ class WindowsBuilder:
|
|||
"""
|
||||
start_time = datetime.now()
|
||||
print("=" * 60)
|
||||
print("🚀 WebDrop Bridge Windows Build")
|
||||
print(f"🚀 {self.brand.display_name} Windows Build")
|
||||
print("=" * 60)
|
||||
|
||||
self.clean()
|
||||
|
|
@ -650,6 +592,12 @@ def main() -> int:
|
|||
default=None,
|
||||
help="Path to .env file to bundle (default: project root .env)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--brand",
|
||||
type=str,
|
||||
default=None,
|
||||
help="Brand manifest name from build/brands (e.g. agravity)",
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
|
|
@ -657,7 +605,7 @@ def main() -> int:
|
|||
do_sync_version()
|
||||
|
||||
try:
|
||||
builder = WindowsBuilder(env_file=args.env_file)
|
||||
builder = WindowsBuilder(env_file=args.env_file, brand=args.brand)
|
||||
except FileNotFoundError as e:
|
||||
print(f"❌ Build failed: {e}")
|
||||
return 1
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue