Fix PyInstaller spec file and build command to properly handle versio… #6
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Build Huntarr Windows Standalone Application | |
| on: | |
| push: | |
| tags: | |
| - 'v*.*.*' # This will trigger on version tags like v1.1.1 | |
| workflow_dispatch: # Allow manual triggering | |
| jobs: | |
| build-windows: | |
| runs-on: windows-latest | |
| steps: | |
| # Checkout code with full depth | |
| - name: Checkout code | |
| uses: actions/checkout@v3 | |
| with: | |
| fetch-depth: 0 | |
| # Set up Python environment | |
| - name: Set up Python 3.9 | |
| uses: actions/setup-python@v4 | |
| with: | |
| python-version: '3.9' | |
| cache: 'pip' | |
| # Install dependencies | |
| - name: Install dependencies | |
| run: | | |
| python -m pip install --upgrade pip | |
| pip install -r requirements.txt | |
| pip install pyinstaller pywin32 | |
| # Get version from tag or set to v1.1.1 if not available | |
| - name: Extract version | |
| id: get_version | |
| run: | | |
| if ("${{ github.ref }}" -like "refs/tags/v*") { | |
| $version = "${{ github.ref }}".Substring(11) | |
| echo "VERSION=$version" >> $env:GITHUB_OUTPUT | |
| } else { | |
| $version = "1.1.1" | |
| echo "VERSION=$version" >> $env:GITHUB_OUTPUT | |
| echo "BUILD_VERSION=$version" >> $env:GITHUB_ENV | |
| } | |
| # Verify Windows config file exists | |
| - name: Verify Windows config file | |
| run: | | |
| # Verify windows_config.py exists | |
| if (Test-Path src/primary/windows_config.py) { | |
| echo "Windows config file exists." | |
| } else { | |
| echo "ERROR: windows_config.py not found. Creating emergency version..." | |
| $windowsConfigContent = @" | |
| # Windows patch for config paths | |
| import os | |
| import sys | |
| import pathlib | |
| import logging | |
| import shutil | |
| # Setup a logger for this module | |
| logger = logging.getLogger("windows_config") | |
| # Patch function to ensure config path works on Windows | |
| def get_config_dir(): | |
| """ | |
| Get the appropriate config directory for Windows. | |
| When running as an executable, this will be next to the exe. | |
| When running as a script, this will be in the project root. | |
| """ | |
| if getattr(sys, 'frozen', False): | |
| # Running as compiled exe | |
| base_dir = os.path.dirname(sys.executable) | |
| logger.info(f"Running as frozen application from {base_dir}") | |
| else: | |
| # Running as script | |
| base_dir = os.path.abspath(os.path.dirname(os.path.dirname(os.path.dirname(__file__)))) | |
| logger.info(f"Running as script from {base_dir}") | |
| config_dir = os.path.join(base_dir, 'config') | |
| os.makedirs(config_dir, exist_ok=True) | |
| logger.info(f"Using config directory: {config_dir}") | |
| return config_dir | |
| # Create directories for config | |
| def ensure_config_dirs(): | |
| """ | |
| Ensure all required config directories exist. | |
| Returns the base config directory path. | |
| """ | |
| config_dir = get_config_dir() | |
| data_dir = os.path.join(config_dir, 'data') | |
| logs_dir = os.path.join(config_dir, 'logs') | |
| settings_dir = os.path.join(config_dir, 'settings') | |
| # Create each directory | |
| os.makedirs(data_dir, exist_ok=True) | |
| os.makedirs(logs_dir, exist_ok=True) | |
| os.makedirs(settings_dir, exist_ok=True) | |
| return config_dir | |
| "@ | |
| New-Item -Path src/primary -Name windows_config.py -ItemType File -Force | |
| Set-Content -Path src/primary/windows_config.py -Value $windowsConfigContent | |
| echo "Created emergency windows_config.py file." | |
| } | |
| # Patch the settings_manager.py file to handle Windows paths | |
| - name: Patch settings_manager for Windows | |
| run: | | |
| # Create a patch file | |
| $patchContent = @" | |
| # Path patch for settings_manager.py | |
| import re | |
| import os | |
| # Read the file | |
| with open('src/primary/settings_manager.py', 'r') as f: | |
| content = f.read() | |
| # Create the replacement for the SETTINGS_DIR declaration | |
| pathPatch = ''' | |
| # Settings directory setup - Dynamically determine based on environment | |
| import os | |
| import sys | |
| # Import Windows-specific config handling for the executable | |
| try: | |
| from primary.windows_config import get_config_dir, ensure_config_dirs | |
| # On Windows executable, use the special config directory | |
| if sys.platform == 'win32' and getattr(sys, 'frozen', False): | |
| config_dir = ensure_config_dirs() | |
| SETTINGS_DIR = pathlib.Path(os.path.join(config_dir, 'settings')) | |
| settings_logger.info(f"Windows executable mode: Using config at {SETTINGS_DIR}") | |
| else: | |
| # Default Docker/Linux behavior | |
| SETTINGS_DIR = pathlib.Path("/config") | |
| except ImportError: | |
| # Fallback to default behavior | |
| SETTINGS_DIR = pathlib.Path("/config") | |
| SETTINGS_DIR.mkdir(parents=True, exist_ok=True) | |
| ''' | |
| # Define the pattern to replace | |
| pattern = r'# Settings directory setup.*?SETTINGS_DIR\.mkdir\(parents=True, exist_ok=True\)' | |
| # Replace using regex with DOTALL flag to match across multiple lines | |
| newContent = re.sub(pattern, pathPatch, content, flags=re.DOTALL) | |
| # Write the modified content back | |
| with open('src/primary/settings_manager.py', 'w') as f: | |
| f.write(newContent) | |
| print("Successfully patched settings_manager.py for Windows compatibility") | |
| "@ | |
| Set-Content -Path patch_settings_manager.py -Value $patchContent | |
| # Run the patch script | |
| python patch_settings_manager.py | |
| # Create config directory and default settings | |
| - name: Set up default config structure | |
| run: | | |
| # Create config directory structure | |
| mkdir -p config\data | |
| mkdir -p config\logs | |
| mkdir -p config\settings | |
| # Copy default configs to the settings directory | |
| Copy-Item -Path src\primary\default_configs\*.json -Destination config\settings\ -Force | |
| # Create a launcher.bat file for better user experience | |
| $launcherContent = @" | |
| @echo off | |
| echo ====================================== | |
| echo Welcome to Huntarr v${{ steps.get_version.outputs.VERSION }} | |
| echo ====================================== | |
| echo Starting Huntarr server... | |
| echo. | |
| echo Access the web interface at: http://localhost:9705 | |
| echo. | |
| echo New features in this version: | |
| echo * Improved API handling with consistent timeout settings | |
| echo * Modern UI with animated app icons | |
| echo * Enhanced community links via GitHub | |
| echo. | |
| echo Press Ctrl+C in the Huntarr console window to quit | |
| echo. | |
| start http://localhost:9705 | |
| start Huntarr.exe | |
| "@ | |
| Set-Content -Path launcher.bat -Value $launcherContent | |
| # Create a Windows-specific README | |
| - name: Create Windows README | |
| run: | | |
| $readmeContent = @" | |
| # Huntarr for Windows v${{ steps.get_version.outputs.VERSION }} | |
| ## Quick Start | |
| 1. Double-click on the `launcher.bat` file to start Huntarr | |
| 2. Your default browser will open to http://localhost:9705 | |
| 3. Configure your Arr applications in the Settings page | |
| ## New Features | |
| - **Bazarr Integration**: Manage your subtitle needs directly from Huntarr | |
| - **Improved API Handling**: Consistent API timeouts and User-Agent (Huntarr/1.0) across all apps | |
| - **Enhanced UI**: Modern interface with animated app icons and improved tooltips | |
| - **GitHub Community**: All community links now point to GitHub resources instead of plex.one | |
| ## Configuration | |
| - All configuration files are stored in the `config` folder next to the executable | |
| - Logs are stored in the `config/logs` folder | |
| - App settings are in `config/settings` | |
| - Default API timeout is 120 seconds (configurable in general.json) | |
| ## Troubleshooting | |
| - If you encounter Error 500, check the logs in the `config/logs` folder | |
| - Ensure you have proper permissions on the `config` directory | |
| - For support, visit our [GitHub Discussions](https://github.com/Admin9705/Huntarr.io/discussions) | |
| - Report bugs at [GitHub Issues](https://github.com/Admin9705/Huntarr.io/issues) | |
| - Join our community on [Discord](https://discord.com/invite/PGJJjR5Cww) | |
| "@ | |
| Set-Content -Path Huntarr-Windows-README.md -Value $readmeContent | |
| # Create PyInstaller spec file | |
| - name: Create PyInstaller spec file | |
| run: | | |
| $specContent = @" | |
| # -*- mode: python ; coding: utf-8 -*- | |
| block_cipher = None | |
| a = Analysis( | |
| ['main.py'], | |
| pathex=[], | |
| binaries=[], | |
| datas=[ | |
| ('frontend', 'frontend'), | |
| ('config', 'config'), | |
| ('src/primary/default_configs', 'src/primary/default_configs'), | |
| ('static', 'static'), | |
| ('Huntarr-Windows-README.md', '.'), | |
| ('launcher.bat', '.'), | |
| ], | |
| hiddenimports=[ | |
| 'win32timezone', | |
| 'waitress', | |
| 'src.primary.windows_config', | |
| 'win32api', | |
| 'win32con', | |
| 'win32service', | |
| 'win32serviceutil', | |
| ], | |
| hookspath=[], | |
| hooksconfig={}, | |
| runtime_hooks=[], | |
| excludes=[], | |
| win_no_prefer_redirects=False, | |
| win_private_assemblies=False, | |
| cipher=block_cipher, | |
| noarchive=False, | |
| ) | |
| pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher) | |
| exe = EXE( | |
| pyz, | |
| a.scripts, | |
| a.binaries, | |
| a.zipfiles, | |
| a.datas, | |
| [], | |
| name='Huntarr', | |
| debug=False, | |
| bootloader_ignore_signals=False, | |
| strip=False, | |
| upx=True, | |
| upx_exclude=[], | |
| runtime_tmpdir=None, | |
| console=False, | |
| disable_windowed_traceback=False, | |
| argv_emulation=False, | |
| target_arch=None, | |
| codesign_identity=None, | |
| entitlements_file=None, | |
| icon='frontend/static/img/logo.ico', | |
| ) | |
| "@ | |
| Set-Content -Path Huntarr.spec -Value $specContent | |
| # Build executable with version information | |
| - name: Build executable with version information | |
| run: | | |
| # Get version from previous step | |
| $version = "${{ steps.get_version.outputs.VERSION }}" | |
| # Add version info to executable | |
| $versionInfo = @" | |
| VSVersionInfo( | |
| ffi=FixedFileInfo( | |
| filevers=($version.Split('.') + @(0) * 4)[0..3], | |
| prodvers=($version.Split('.') + @(0) * 4)[0..3], | |
| mask=0x3f, | |
| flags=0x0, | |
| OS=0x40004, | |
| fileType=0x1, | |
| subtype=0x0, | |
| date=(0, 0) | |
| ), | |
| kids=[ | |
| StringFileInfo( | |
| [ | |
| StringTable( | |
| '040904B0', | |
| [StringStruct('CompanyName', 'Huntarr.io'), | |
| StringStruct('FileDescription', 'Huntarr - Arr App Manager'), | |
| StringStruct('FileVersion', '$version'), | |
| StringStruct('InternalName', 'Huntarr'), | |
| StringStruct('LegalCopyright', 'Copyright (c) 2025'), | |
| StringStruct('OriginalFilename', 'Huntarr.exe'), | |
| StringStruct('ProductName', 'Huntarr'), | |
| StringStruct('ProductVersion', '$version')] | |
| ) | |
| ] | |
| ), | |
| VarFileInfo([VarStruct('Translation', [1033, 1200])]) | |
| ] | |
| ) | |
| "@ | |
| Set-Content -Path file_version_info.txt -Value $versionInfo | |
| # Build with the spec file - can't use --version-file with a spec file | |
| pyinstaller Huntarr.spec | |
| # Create Windows package | |
| - name: Create Windows package | |
| run: | | |
| # Get version from previous step | |
| $version = "${{ steps.get_version.outputs.VERSION }}" | |
| # Copy additional files to dist directory | |
| Copy-Item -Path launcher.bat -Destination dist\ | |
| Copy-Item -Path Huntarr-Windows-README.md -Destination dist\ | |
| # Create a zip file of the dist directory with version in name | |
| Compress-Archive -Path dist\* -DestinationPath "Huntarr-Windows-v$version.zip" -Force | |
| # Test the executable | |
| - name: Test executable setup | |
| run: | | |
| echo "Verifying build artifacts" | |
| dir dist | |
| # Create GitHub release | |
| - name: Create Release | |
| id: create_release | |
| uses: softprops/action-gh-release@v1 | |
| with: | |
| tag_name: v${{ steps.get_version.outputs.VERSION }} | |
| name: Huntarr Windows Build v${{ steps.get_version.outputs.VERSION }} | |
| body: | | |
| # Huntarr Windows Build v${{ steps.get_version.outputs.VERSION }} | |
| This is a Windows executable build of Huntarr.io v${{ steps.get_version.outputs.VERSION }}. | |
| ## New Features | |
| - **Bazarr Integration**: Manage your subtitle needs directly from Huntarr | |
| - **Improved API Handling**: Consistent API timeouts and User-Agent across all apps | |
| - **Enhanced UI**: Modern interface with animated app icons | |
| - **GitHub Community**: All community links now point to GitHub resources | |
| ## Installation | |
| 1. Download the zip file and extract all contents to a folder | |
| 2. Run the `launcher.bat` file to start Huntarr | |
| 3. Your browser will open automatically to http://localhost:9705 | |
| ## Notes | |
| - Configuration is stored in the `config` folder next to the executable | |
| - If you encounter issues, please report them on GitHub | |
| draft: false | |
| prerelease: true # Mark as pre-release | |
| files: | | |
| ./Huntarr-Windows-v${{ steps.get_version.outputs.VERSION }}.zip | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} |