Skip to content
Merged

Dev #475

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
55ced08
feat: add Windows compatibility with proper distribution structure
Admin9705 May 19, 2025
7314d13
fix: add missing main.py script and update Windows workflow
Admin9705 May 19, 2025
e577376
fix: update Windows build scripts with correct path handling
Admin9705 May 19, 2025
c1ac682
fix: update Windows installer workflow to handle existing directories
Admin9705 May 19, 2025
1bc7aca
fix: improve installer build process to handle existing directories
Admin9705 May 19, 2025
cd26e9c
Enhance installer to verify executable file existence and permissions
May 19, 2025
aa42125
Fix mismatched quotes in installer script that caused build failure
May 19, 2025
808cbcd
Fix path references in installer script to ensure installer is create…
May 19, 2025
bdb3383
Fix file paths in installer script to use absolute paths for GitHub A…
May 19, 2025
1ac98f1
Enhance build script with better error handling and file verification
May 19, 2025
36bef9d
Fix spec file to properly locate main.py in GitHub Actions environment
May 19, 2025
489a09d
Add -y flag to PyInstaller command to force overwriting output directory
May 19, 2025
0a8fbb6
Replace installer build step with direct Inno Setup compilation for r…
May 19, 2025
0415131
Fix mismatched quotes in installer script parameters
May 19, 2025
b55ac21
Fix template missing errors by properly including frontend templates …
May 19, 2025
c2cce0f
chore: remove unused file
May 19, 2025
de97dbe
Fix Windows service installation in Inno Setup script by using SC.exe…
May 19, 2025
4474352
Fix all quoting issues in the Inno Setup installer script
May 19, 2025
373f44d
Refactor Inno Setup installer script to improve quoting consistency a…
May 19, 2025
47e3655
Fix quote formatting in installer script for icacls commands
May 19, 2025
2bdaf93
Fix: Correct quote handling for sc.exe parameters in Inno Setup script
May 19, 2025
1949cda
Fix(ISS): Alt quote for sc.exe description param
May 19, 2025
0d693c5
Revert(ISS): Align [Run] section with win-555 structure
May 19, 2025
8d9d34d
Fix Windows service implementation to resolve service startup issues
May 19, 2025
b7ad15d
Fix Windows service installation to resolve error 1639 (invalid comma…
May 19, 2025
97b7e68
Fix Windows service installation with simplified win32serviceutil app…
May 19, 2025
945db4b
Improve Windows service reliability with direct SC.EXE commands
May 19, 2025
41465fc
Implement batch file approach for more reliable Windows service insta…
May 19, 2025
11d4007
Fix service startup to properly report status and avoid timeout (erro…
May 19, 2025
68d6077
Simplify installer to use non-service mode with auto-start option
May 19, 2025
febaf2d
Fix quoting issue in installer script for GitHub Actions compatibility
May 19, 2025
1474474
feat: add Windows installer script with directory permissions and ser…
May 19, 2025
980c97f
Switch to NSIS installer for better compatibility with GitHub Actions
May 19, 2025
cff0ee4
Update upload-artifact action to v4
May 19, 2025
1036ef4
Fix version file handling in NSIS installer
May 19, 2025
59d6c55
Refine NSIS version file path handling in GHA workflow
May 19, 2025
6d0413a
Fix NSIS compile-time file check syntax
May 19, 2025
b5324dd
Fix icon path in NSIS script using PROJECT_ROOT define
May 19, 2025
a075937
Add debug echo for PROJECT_ROOT in NSIS script
May 20, 2025
98f39c0
Fix PowerShell variable expansion for PROJECT_ROOT in GHA workflow
May 20, 2025
a775caf
Comment out license page in NSIS installer
May 20, 2025
ef9a07c
feat: add Windows NSIS installer and GitHub Actions workflow for auto…
May 20, 2025
5624401
feat: add Windows installer script with NSIS for Huntarr application
May 20, 2025
2bfe688
Create installer directories before running NSIS
May 20, 2025
3b77600
Fix installer path in workflow to match actual NSIS output location
May 20, 2025
3203fe7
chore: bump version to 7.0.6 and remove Windows installer workflow
Admin9705 May 20, 2025
391763b
Add version.txt to Windows installer package
May 20, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 96 additions & 0 deletions .github/workflows/windows-build-nsis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
name: Windows Build with NSIS

on:
push:
branches: [ "main", "dev", "win-*" ]
pull_request:
branches: [ "main", "dev" ]
workflow_dispatch:

jobs:
build:
runs-on: windows-latest

steps:
- uses: actions/checkout@v3

- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.10'

- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
pip install pyinstaller==5.13.0
pip install pywin32

- name: Create directories
run: |
mkdir -p config/logs
dir
dir config

- name: Build with PyInstaller
run: |
# Copy spec file from distribution directory to root
cp distribution/windows/huntarr.spec .

# Use the dedicated build script from the distribution directory
python -m pip install -r requirements.txt
python -m pip install pywin32
pyinstaller -y distribution/windows/huntarr.spec

# Display contents of dist/Huntarr
dir dist/Huntarr

- name: Install NSIS
run: |
choco install nsis -y

- name: Build NSIS Installer
run: |
# Display current directory structure
dir

# Create installer output directory
mkdir -Force distribution\windows\installer\installer
mkdir -Force installer

# Prepare version file path
$versionContent = Get-Content version.txt -Raw
Write-Host "Version from file: $versionContent"
$AbsVersionFile = Join-Path -Path $PWD.Path -ChildPath "version.txt"
Write-Host "Absolute path for VERSIONFILE: $AbsVersionFile"

# Prepare arguments for makensis.exe
$MakensisArgs = @(
"/DVERSIONFILE=$AbsVersionFile",
"/DPROJECT_ROOT=$($PWD.Path)",
"distribution\windows\installer\huntarr_installer.nsi"
)

# Run NSIS compiler
Write-Host "Running makensis.exe with arguments: $MakensisArgs"
& "C:\Program Files (x86)\NSIS\makensis.exe" $MakensisArgs

# Check if installer was created
$installerPath = "distribution\windows\installer\installer\Huntarr_Setup.exe"
if (Test-Path $installerPath) {
Write-Host "Installer created successfully at $installerPath"
# Copy to expected upload location
Copy-Item -Path $installerPath -Destination "installer\Huntarr_Setup.exe" -Force
} else {
Write-Error "Installer was not created. Check the logs above for errors."
exit 1
}

# List any exe files in the installer directory
Get-ChildItem -Path installer -Filter *.exe | ForEach-Object { Write-Host $_.FullName }

- name: Upload installer
uses: actions/upload-artifact@v4
with:
name: huntarr-installer
path: installer/Huntarr_Setup.exe
203 changes: 203 additions & 0 deletions distribution/windows/build.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
#!/usr/bin/env python3
"""
Huntarr Windows Build Script
This script builds the Windows executable and installer for Huntarr.
"""

import os
import sys
import subprocess
import shutil
import argparse
from pathlib import Path

# Constants
SCRIPT_DIR = Path(os.path.dirname(os.path.abspath(__file__)))
ROOT_DIR = SCRIPT_DIR.parent.parent # Navigate up two directories to project root

def run_command(cmd, cwd=None):
"""Run a command and return the result

Args:
cmd: Command list to run
cwd: Current working directory for the command

Returns:
True if command succeeded, False otherwise
"""
print(f"Running: {' '.join(cmd)}")
result = subprocess.run(cmd, check=False, cwd=cwd)
return result.returncode == 0

def build_exe():
"""Build the Windows executable using PyInstaller"""
print("Building Huntarr Windows executable...")

# Make sure PyInstaller is installed
try:
import PyInstaller
except ImportError:
print("PyInstaller not found. Installing...")
run_command([sys.executable, "-m", "pip", "install", "pyinstaller"])

# Make sure all requirements are installed
run_command([sys.executable, "-m", "pip", "install", "-r", str(ROOT_DIR / "requirements.txt")])
run_command([sys.executable, "-m", "pip", "install", "pywin32"])

# Build using the spec file
spec_file = SCRIPT_DIR / "huntarr.spec"

# Verify the main.py file exists in the expected location
main_file = ROOT_DIR / "main.py"
if not main_file.exists():
print(f"ERROR: Main file not found at {main_file}")
print("Listing files in root directory:")
for file in ROOT_DIR.glob("*"):
print(f" {file}")
return False

# Make sure we're in the project root directory when running PyInstaller
# This helps with finding relative paths
# Add the -y option to force overwrite of the output directory
result = run_command([sys.executable, "-m", "PyInstaller", "-y", str(spec_file)], cwd=str(ROOT_DIR))

if not result:
print("ERROR: PyInstaller failed to build the executable")
return False

# Check if Huntarr.exe was created
exe_path = ROOT_DIR / "dist" / "Huntarr" / "Huntarr.exe"
if not exe_path.exists():
print(f"ERROR: Executable not created at {exe_path}")
return False

print("Executable build complete.")
return True

def build_installer():
"""Build the Windows installer using Inno Setup"""
print("Building Huntarr Windows installer...")

# Check if Inno Setup is installed
inno_compiler = "C:\\Program Files (x86)\\Inno Setup 6\\ISCC.exe"
if not os.path.exists(inno_compiler):
print(f"ERROR: Inno Setup compiler not found at {inno_compiler}")
print("Please install Inno Setup 6 from https://jrsoftware.org/isdl.php")
return False

# Check if the exe file was created by PyInstaller
exe_path = ROOT_DIR / "dist" / "Huntarr" / "Huntarr.exe"
if not exe_path.exists():
print(f"ERROR: Executable not found at {exe_path}")
print("PyInstaller did not create the executable. Please run build_exe() first.")
return False

# Create installer directory if it doesn't exist
installer_dir = ROOT_DIR / "installer"
os.makedirs(str(installer_dir), exist_ok=True)

# Make sure the dist directory exists and has the expected structure
dist_dir = ROOT_DIR / "dist" / "Huntarr"
resources_dir = dist_dir / "resources"
scripts_dir = dist_dir / "scripts"

os.makedirs(str(resources_dir), exist_ok=True)
os.makedirs(str(scripts_dir), exist_ok=True)

# Copy resources and scripts if they don't exist in the dist directory
src_resources = SCRIPT_DIR / "resources"
src_scripts = SCRIPT_DIR / "scripts"

if src_resources.exists():
# Copy all files from resources directory
for src_file in src_resources.glob("*"):
dst_file = resources_dir / src_file.name
if src_file.is_file():
shutil.copy2(str(src_file), str(dst_file))

if src_scripts.exists():
# Copy all files from scripts directory
for src_file in src_scripts.glob("*"):
dst_file = scripts_dir / src_file.name
if src_file.is_file():
shutil.copy2(str(src_file), str(dst_file))

# Copy the installer script to the root
installer_script = SCRIPT_DIR / "installer" / "huntarr_installer.iss"
target_script = ROOT_DIR / "huntarr_installer.iss"
shutil.copy2(str(installer_script), str(target_script))

# Ensure LICENSE file exists at the root
license_path = ROOT_DIR / "LICENSE"
if not license_path.exists():
print(f"ERROR: LICENSE file not found at {license_path}")
print("Checking for LICENSE file in other locations...")
for possible_license in ROOT_DIR.glob("*LICENSE*"):
print(f" Found license-like file: {possible_license}")
return False

# Run the Inno Setup compiler
result = run_command([inno_compiler, str(target_script)])

# Check if the installer was created
installer_path = ROOT_DIR / "installer" / "Huntarr_Setup.exe"
if not installer_path.exists():
print(f"ERROR: Installer not created at {installer_path}")
print("The Inno Setup compiler failed to create the installer.")
return False

# Clean up
if target_script.exists():
target_script.unlink()

print("Installer build complete.")
return True

def clean():
"""Clean up build artifacts"""
print("Cleaning up build artifacts...")

# Remove PyInstaller build directories
build_dir = ROOT_DIR / "build"
dist_dir = ROOT_DIR / "dist"

if build_dir.exists():
shutil.rmtree(build_dir)

if dist_dir.exists():
shutil.rmtree(dist_dir)

# Remove any .spec files in the root directory
for spec_file in ROOT_DIR.glob("*.spec"):
spec_file.unlink()

print("Cleanup complete.")
return True

def main():
"""Main entry point"""
parser = argparse.ArgumentParser(description="Build Huntarr for Windows")
parser.add_argument("--clean", action="store_true", help="Clean up build artifacts")
parser.add_argument("--exe-only", action="store_true", help="Build only the executable, not the installer")
parser.add_argument("--installer-only", action="store_true", help="Build only the installer, assuming executable is already built")

args = parser.parse_args()

if args.clean:
clean()
if not (args.exe_only or args.installer_only):
return 0

if args.installer_only:
build_installer()
elif args.exe_only:
build_exe()
else:
# Build both
if build_exe():
build_installer()

return 0

if __name__ == "__main__":
sys.exit(main())
Loading
Loading