Switch Windows build to ZIP only, remove MSI setup scripts

Updated CI/CD documentation and workflow to generate only ZIP portable versions for Windows, removing MSI installer support. Deleted setup.py and setup-ffmpeg.py scripts for MSI builds, and refactored workflow to use cx_Freeze CLI for ZIP packaging. Documentation and artifact instructions now reflect ZIP-only distribution.
This commit is contained in:
Your Name
2025-08-27 20:02:22 +03:00
parent 4a425661f7
commit 44b69d99e9
4 changed files with 89 additions and 359 deletions
+7 -10
View File
@@ -10,7 +10,7 @@ The workflow is triggered when you push a git tag that starts with `v` (e.g., `v
### Build Process ### Build Process
1. **Setup**: Uses Python 3.12.10 on Windows 1. **Setup**: Uses Python 3.12.10 on Windows
2. **Builds**: Creates both Standard and FFmpeg versions 2. **Builds**: Creates both Standard and FFmpeg versions
3. **Packages**: Generates MSI installers and ZIP portable versions 3. **Packages**: Generates ZIP portable versions only (no MSI)
4. **Release**: Creates a draft GitHub release with all artifacts 4. **Release**: Creates a draft GitHub release with all artifacts
## Usage ## Usage
@@ -38,9 +38,7 @@ The workflow is triggered when you push a git tag that starts with `v` (e.g., `v
The workflow creates the following files: The workflow creates the following files:
- `YTSage-v<version>.msi` - Standard Windows installer
- `YTSage-v<version>.zip` - Standard portable version - `YTSage-v<version>.zip` - Standard portable version
- `YTSage-v<version>-ffmpeg.msi` - FFmpeg bundle installer
- `YTSage-v<version>-ffmpeg.zip` - FFmpeg bundle portable - `YTSage-v<version>-ffmpeg.zip` - FFmpeg bundle portable
### Release Notes ### Release Notes
@@ -55,8 +53,7 @@ Automatic release notes are generated including:
### Automatic Version Detection ### Automatic Version Detection
- Extracts version from git tag (removes 'v' prefix) - Extracts version from git tag (removes 'v' prefix)
- Updates setup.py files with correct version - Names all artifacts consistently (ZIPs)
- Names all artifacts consistently
### Caching ### Caching
- Python dependencies are cached to speed up builds - Python dependencies are cached to speed up builds
@@ -98,16 +95,16 @@ Key configuration options:
- Release note templates - Release note templates
### Adding New Build Types ### Adding New Build Types
To add new build configurations: To add new platform packages (e.g., .dmg for macOS, .deb for Linux):
1. Create new setup-*.py files 1. Add separate jobs in `.github/workflows/build-windows.yml` (or a new workflow) targeting the OS (macos-latest, ubuntu-latest).
2. Add build steps to the workflow 2. Build the executable with cx_Freeze or PyInstaller for that platform.
3. Update artifact collection logic 3. Package the build output (e.g., create DMG on macOS, DEB on Ubuntu) using platform tools.
4. Upload the artifacts and include them in the release.
## Notes ## Notes
- The workflow only runs on Windows - The workflow only runs on Windows
- Requires Python 3.12.10 for MSI compatibility
- All builds use cx_Freeze for packaging - All builds use cx_Freeze for packaging
- FFmpeg binaries are expected in standard locations - FFmpeg binaries are expected in standard locations
- Draft releases allow for review before publication - Draft releases allow for review before publication
+82 -114
View File
@@ -55,79 +55,38 @@ jobs:
pip install --no-cache-dir cx_Freeze pip install --no-cache-dir cx_Freeze
pip install --no-cache-dir yt-dlp pip install --no-cache-dir yt-dlp
- name: Update version in setup files - name: Prepare build variables
shell: powershell shell: powershell
run: | run: |
$version = "${{ steps.get_version.outputs.VERSION }}" $version = "${{ steps.get_version.outputs.VERSION }}"
Write-Host "Updating version to: $version" echo "VERSION=$version" >> $env:GITHUB_ENV
Write-Host "Prepared build variables for version: $version"
# Update setup.py
$setupContent = Get-Content "build/windows/setup.py" -Raw
$setupContent = $setupContent -replace 'version="[^"]*"', "version=`"$version`""
$setupContent = $setupContent -replace 'target_name="[^"]*"', "target_name=`"YTSage-v$version.exe`""
Set-Content "build/windows/setup.py" -Value $setupContent
# Update setup-ffmpeg.py
$setupFFmpegContent = Get-Content "build/windows/setup-ffmpeg.py" -Raw
$setupFFmpegContent = $setupFFmpegContent -replace 'version="[^"]*"', "version=`"$version`""
$setupFFmpegContent = $setupFFmpegContent -replace 'target_name="[^"]*"', "target_name=`"YTSage-v$version-ffmpeg.exe`""
Set-Content "build/windows/setup-ffmpeg.py" -Value $setupFFmpegContent
Write-Host "Version updated in setup files (build script auto-detects version)"
- name: Build Standard Version (MSI only) - name: Build Standard Version (ZIP)
shell: powershell shell: powershell
run: | run: |
.\venv\Scripts\Activate.ps1 .\venv\Scripts\Activate.ps1
Write-Host "Building Standard Version..." $version = "$env:VERSION"
Write-Host "Building Standard Version v$version ..."
# Clean previous build artifacts # Clean previous build artifacts
if (Test-Path "build\exe.*") { Remove-Item "build\exe.*" -Recurse -Force } if (Test-Path "build\exe.*") { Remove-Item "build\exe.*" -Recurse -Force }
if (Test-Path "build\bdist.*") { Remove-Item "build\bdist.*" -Recurse -Force } if (Test-Path "build\bdist.*") { Remove-Item "build\bdist.*" -Recurse -Force }
if (Test-Path "dist") { Remove-Item "dist" -Recurse -Force } if (Test-Path "dist") { Remove-Item "dist" -Recurse -Force }
# Build executable # Build executable using cx_Freeze CLI
python build\windows\setup.py build cxfreeze main.py `
--target-dir "dist\YTSage" `
# Build MSI installer with error handling --base-name Win32GUI `
Write-Host "Building standard MSI installer..." --icon "assets\branding\icons\YTSage.ico" `
Write-Host "Current working directory: $(Get-Location)" --target-name "YTSage-v$version.exe" `
Write-Host "Setup.py path: $(Resolve-Path 'build\windows\setup.py')" --optimize 2 `
--packages "PySide6.QtCore,PySide6.QtGui,PySide6.QtWidgets,requests,PIL,packaging,markdown,playsound3,loguru,setuptools" `
# Run with maximum verbosity to capture any hidden errors --excludes "PySide6.QtBluetooth,PySide6.QtNetwork,PySide6.QtOpenGL,PySide6.QtPrintSupport,PySide6.QtSvg,PySide6.QtTest,PySide6.QtXml,PySide6.QtSql,PySide6.QtHelp,PySide6.QtMultimedia,PySide6.QtQml,PySide6.QtQuick,PySide6.QtWebEngineCore,PIL.ImageDraw,PIL.ImageFont,numpy,scipy,wx,pandas,tkinter,yt_dlp,unittest,test,tests" `
python build\windows\setup.py bdist_msi --verbose --include-files "src/:src/" `
if ($LASTEXITCODE -eq 0) { --include-files "assets/Icon/icon.png:assets/Icon/icon.png" `
Write-Host "Standard MSI build completed successfully" --include-files "assets/sound/notification.mp3:assets/sound/notification.mp3" `
# List MSI files created --include-files "assets/branding/icons/YTSage.ico:YTSage.ico"
Write-Host "MSI files in dist directory after standard build:"
if (Test-Path "dist") {
$msiFiles = Get-ChildItem -Path "dist" -Filter "*.msi"
if ($msiFiles) {
$msiFiles | ForEach-Object { Write-Host " $($_.Name)" }
} else {
Write-Host " No MSI files found! Attempting alternative build method..."
# Alternative approach: try building without excludes that might cause issues
Write-Host "Retrying MSI build with simplified configuration..."
python build\windows\setup.py bdist_msi --skip-build
if (Test-Path "dist/*.msi") {
Write-Host "Alternative MSI build succeeded!"
Get-ChildItem -Path "dist" -Filter "*.msi" | ForEach-Object { Write-Host " $($_.Name)" }
} else {
Write-Host "ERROR: MSI creation failed completely. Build directory contents:"
if (Test-Path "build") {
Get-ChildItem -Path "build" -Recurse | ForEach-Object { Write-Host " $($_.FullName)" }
}
exit 1
}
}
} else {
Write-Host " No dist directory found!"
exit 1
}
} else {
Write-Host "Error building standard MSI - exit code: $LASTEXITCODE"
exit 1
}
- name: Setup FFmpeg for bundle - name: Setup FFmpeg for bundle
shell: powershell shell: powershell
@@ -179,71 +138,82 @@ jobs:
exit 1 exit 1
} }
- name: Build FFmpeg Version (MSI only) - name: Build FFmpeg Version (ZIP)
shell: powershell shell: powershell
run: | run: |
.\venv\Scripts\Activate.ps1 .\venv\Scripts\Activate.ps1
Write-Host "Building FFmpeg Version..." $version = "$env:VERSION"
Write-Host "Building FFmpeg Version v$version ..."
# Clean previous build artifacts # Clean previous build artifacts
if (Test-Path "build\exe.*") { Remove-Item "build\exe.*" -Recurse -Force } if (Test-Path "build\exe.*") { Remove-Item "build\exe.*" -Recurse -Force }
if (Test-Path "build\bdist.*") { Remove-Item "build\bdist.*" -Recurse -Force } if (Test-Path "build\bdist.*") { Remove-Item "build\bdist.*" -Recurse -Force }
# Build executable with FFmpeg # Prepare optional FFmpeg include arguments
python build\windows\setup-ffmpeg.py build $ffinc1 = ""
$ffinc2 = ""
# Build MSI installer with FFmpeg and error handling $ffinc3 = ""
Write-Host "Building FFmpeg MSI installer..." if ($env:FFMPEG_PATH) {
python build\windows\setup-ffmpeg.py bdist_msi $ffmpegExe = Join-Path $env:FFMPEG_PATH "ffmpeg.exe"
if ($LASTEXITCODE -eq 0) { $ffprobeExe = Join-Path $env:FFMPEG_PATH "ffprobe.exe"
Write-Host "FFmpeg MSI build completed successfully" $ffplayExe = Join-Path $env:FFMPEG_PATH "ffplay.exe"
# List MSI files created if (Test-Path $ffmpegExe) { $ffinc1 = "--include-files `"$ffmpegExe:ffmpeg.exe`"" }
Write-Host "MSI files in dist directory after FFmpeg build:" if (Test-Path $ffprobeExe) { $ffinc2 = "--include-files `"$ffprobeExe:ffprobe.exe`"" }
if (Test-Path "dist") { if (Test-Path $ffplayExe) { $ffinc3 = "--include-files `"$ffplayExe:ffplay.exe`"" }
Get-ChildItem -Path "dist" -Filter "*.msi" | ForEach-Object { Write-Host " $($_.Name)" }
}
} else {
Write-Host "Error building FFmpeg MSI - exit code: $LASTEXITCODE"
exit 1
} }
# Build executable with FFmpeg using cx_Freeze CLI
cxfreeze main.py `
--target-dir "dist\YTSage-FFmpeg" `
--base-name Win32GUI `
--icon "assets\branding\icons\YTSage.ico" `
--target-name "YTSage-v$version-ffmpeg.exe" `
--optimize 2 `
--packages "PySide6.QtCore,PySide6.QtGui,PySide6.QtWidgets,requests,PIL,packaging,markdown,playsound3,loguru,setuptools" `
--excludes "PySide6.QtBluetooth,PySide6.QtNetwork,PySide6.QtOpenGL,PySide6.QtPrintSupport,PySide6.QtSvg,PySide6.QtTest,PySide6.QtXml,PySide6.QtSql,PySide6.QtHelp,PySide6.QtMultimedia,PySide6.QtQml,PySide6.QtQuick,PySide6.QtWebEngineCore,PIL.ImageDraw,PIL.ImageFont,numpy,scipy,wx,pandas,tkinter,yt_dlp,unittest,test,tests" `
--include-files "src/:src/" `
--include-files "assets/Icon/icon.png:assets/Icon/icon.png" `
--include-files "assets/sound/notification.mp3:assets/sound/notification.mp3" `
--include-files "assets/branding/icons/YTSage.ico:YTSage.ico" `
$ffinc1 $ffinc2 $ffinc3
- name: Prepare release artifacts - name: Package and prepare release artifacts (ZIPs)
shell: powershell shell: powershell
run: | run: |
$version = "${{ steps.get_version.outputs.VERSION }}" $version = "${{ steps.get_version.outputs.VERSION }}"
Write-Host "Preparing artifacts for version: $version" Write-Host "Preparing ZIP artifacts for version: $version"
New-Item -ItemType Directory -Path "artifacts" -Force New-Item -ItemType Directory -Path "artifacts" -Force
# List all files in dist directory first # List dist directories
Write-Host "Files in dist directory:" Write-Host "Files in dist directory:"
if (Test-Path "dist") { if (Test-Path "dist") {
Get-ChildItem -Path "dist" -Recurse | ForEach-Object { Write-Host " $($_.FullName)" } Get-ChildItem -Path "dist" -Recurse | ForEach-Object { Write-Host " $($_.FullName)" }
} else { } else {
Write-Host " No dist directory found!" Write-Host " No dist directory found!"
} exit 1
# Find and copy standard build artifacts from dist directory
# Look for any MSI file that doesn't contain "ffmpeg" in the name
$standardMsi = Get-ChildItem -Path "dist" -Filter "*.msi" | Where-Object { $_.Name -notlike "*ffmpeg*" -and $_.Name -notlike "*FFmpeg*" } | Select-Object -First 1
if ($standardMsi) {
Copy-Item $standardMsi.FullName "artifacts\YTSage-v$version.msi"
Write-Host "Copied: $($standardMsi.Name) -> YTSage-v$version.msi"
} else {
Write-Host "Warning: Standard MSI not found"
# List all MSI files for debugging
Write-Host "Available MSI files:"
Get-ChildItem -Path "dist" -Filter "*.msi" | ForEach-Object { Write-Host " $($_.Name)" }
} }
# Find and copy FFmpeg build artifacts from dist directory # Create Standard ZIP
# Look for any MSI file that contains "ffmpeg" or "FFmpeg" in the name $standardDist = Join-Path (Get-Location) "dist\YTSage"
$ffmpegMsi = Get-ChildItem -Path "dist" -Filter "*.msi" | Where-Object { $_.Name -like "*ffmpeg*" -or $_.Name -like "*FFmpeg*" } | Select-Object -First 1 if (Test-Path $standardDist) {
if ($ffmpegMsi) { $stdZip = "artifacts\YTSage-v$version.zip"
Copy-Item $ffmpegMsi.FullName "artifacts\YTSage-v$version-ffmpeg.msi" if (Test-Path $stdZip) { Remove-Item $stdZip -Force }
Write-Host "Copied: $($ffmpegMsi.Name) -> YTSage-v$version-ffmpeg.msi" Compress-Archive -Path "$standardDist\*" -DestinationPath $stdZip -CompressionLevel Optimal
Write-Host "Created: $(Resolve-Path $stdZip)"
} else { } else {
Write-Host "Warning: FFmpeg MSI not found" Write-Host "Warning: Standard dist folder not found at $standardDist"
}
# Create FFmpeg ZIP
$ffmpegDist = Join-Path (Get-Location) "dist\YTSage-FFmpeg"
if (Test-Path $ffmpegDist) {
$ffZip = "artifacts\YTSage-v$version-ffmpeg.zip"
if (Test-Path $ffZip) { Remove-Item $ffZip -Force }
Compress-Archive -Path "$ffmpegDist\*" -DestinationPath $ffZip -CompressionLevel Optimal
Write-Host "Created: $(Resolve-Path $ffZip)"
} else {
Write-Host "Warning: FFmpeg dist folder not found at $ffmpegDist"
} }
# List all artifacts # List all artifacts
@@ -273,30 +243,28 @@ jobs:
## Downloads ## Downloads
### Standard Version ### Standard Version (ZIP)
- **YTSage-v${{ steps.get_version.outputs.VERSION }}.msi** - Windows Installer (Downloads FFmpeg when needed) - **YTSage-v${{ steps.get_version.outputs.VERSION }}.zip** Portable ZIP (downloads FFmpeg on first use if needed)
### FFmpeg Bundle Version ### FFmpeg Bundle Version (ZIP)
- **YTSage-v${{ steps.get_version.outputs.VERSION }}-ffmpeg.msi** - Windows Installer with FFmpeg included - **YTSage-v${{ steps.get_version.outputs.VERSION }}-ffmpeg.zip** Portable ZIP with FFmpeg included
## Installation Instructions ## Installation / Usage
### MSI Installer 1. Download the `.zip` file you want.
1. Download the `.msi` file 2. Right-click the ZIP, choose Properties, and if you see "Unblock", check it and Apply.
2. Double-click to run the installer 3. Extract the ZIP to a folder (e.g., `C:\Apps\YTSage`).
3. Follow the installation wizard 4. Run `YTSage-v${{ steps.get_version.outputs.VERSION }}.exe` inside the extracted folder.
4. YTSage will be available in your Start Menu
## System Requirements ## System Requirements
- Windows 10/11 (64-bit) - Windows 10/11 (64-bit)
- .NET Framework (usually pre-installed)
- Internet connection for yt-dlp updates - Internet connection for yt-dlp updates
## Notes ## Notes
- **Standard version**: Downloads FFmpeg automatically when needed - **Standard version**: Downloads FFmpeg automatically when needed.
- **FFmpeg bundle**: Includes FFmpeg binaries for offline use - **FFmpeg bundle**: Includes FFmpeg binaries for offline use.
--- ---
-126
View File
@@ -1,126 +0,0 @@
"""
cx_Freeze setup script for YTSage with FFmpeg bundle
Supports building executables, MSI installers, and ZIP distributions with FFmpeg
"""
import sys
from pathlib import Path
from cx_Freeze import setup, Executable
import os
# Get FFmpeg path from environment variable or use default
FFMPEG_PATH = os.environ.get('FFMPEG_PATH', r'C:\Users\atela\AppData\Local\ffmpeg\ffmpeg-7.1.1-full_build\bin')
# Prepare include files list
include_files = [
("src/", "src/"), # Include entire src directory
("assets/Icon/icon.png", "assets/Icon/icon.png"),
("assets/sound/notification.mp3", "assets/sound/notification.mp3"),
("assets/branding/icons/YTSage.ico", "YTSage.ico"),
]
# Add FFmpeg binaries if they exist
ffmpeg_binaries = ["ffmpeg.exe", "ffprobe.exe", "ffplay.exe"]
for binary in ffmpeg_binaries:
ffmpeg_file = os.path.join(FFMPEG_PATH, binary)
if os.path.exists(ffmpeg_file):
include_files.append((ffmpeg_file, binary))
print(f"Including FFmpeg binary: {binary}")
else:
print(f"Warning: FFmpeg binary not found: {ffmpeg_file}")
# Build options for executable
build_exe_options = {
# Let cx_Freeze auto-discover packages from main.py instead of explicit listing
"packages": [
"PySide6.QtCore",
"PySide6.QtGui",
"PySide6.QtWidgets",
# Additional packages that might be needed
"requests",
"PIL", # Pillow is imported as PIL
"packaging",
"markdown",
"playsound3",
"loguru",
"setuptools"
],
# Packages to exclude (to reduce size)
"excludes": [
"PySide6.QtBluetooth",
"PySide6.QtNetwork",
"PySide6.QtOpenGL",
"PySide6.QtPrintSupport",
"PySide6.QtSvg",
"PySide6.QtTest",
"PySide6.QtXml",
"PySide6.QtSql",
"PySide6.QtHelp",
"PySide6.QtMultimedia",
"PySide6.QtQml",
"PySide6.QtQuick",
"PySide6.QtWebEngineCore",
"PIL.ImageDraw",
"PIL.ImageFont",
"numpy",
"scipy",
"wx",
"pandas",
"tkinter",
"yt_dlp", # Excluded - downloaded dynamically at runtime (all imports are conditional)
"unittest",
"test",
"tests"
],
# Include files (assets + FFmpeg binaries + source code)
"include_files": include_files,
# Zip includes (can help reduce file count)
"zip_include_packages": "*",
"zip_exclude_packages": [],
# Other options
"optimize": 2, # Optimize bytecode
"build_exe": "dist/YTSage-FFmpeg", # Output directory for executable
}
# MSI build options - simplified to avoid cx_Freeze issues
bdist_msi_options = {
"upgrade_code": "{87654321-4321-8765-CBA9-987654321CBA}", # Different GUID for FFmpeg version
"add_to_path": False,
"initial_target_dir": r"[ProgramFilesFolder]\YTSage"
# Removed summary_data that might cause silent failures
}
# Base configuration for Windows GUI application
base = None
if sys.platform == "win32":
base = "Win32GUI" # This prevents console window from appearing
# Executable configuration
executable = Executable(
"main.py",
base=base,
icon="assets/branding/icons/YTSage.ico",
target_name="YTSage-v4.7.4-ffmpeg.exe",
copyright="Copyright (c) 2024-2025 YTSage",
trademarks="YTSage"
)
# Setup configuration
setup(
name="YTSage-FFmpeg",
version="4.7.4", # Updated to match expected version
description="YouTube Video Downloader with FFmpeg",
author="oop7",
author_email="oop7_support@proton.me",
url="https://github.com/oop7/ytsage",
license="MIT",
options={
"build_exe": build_exe_options,
"bdist_msi": bdist_msi_options
},
executables=[executable]
)
-109
View File
@@ -1,109 +0,0 @@
"""
cx_Freeze setup script for YTSage
Supports building executables, MSI installers, and ZIP distributions
"""
import sys
from pathlib import Path
from cx_Freeze import setup, Executable
# Build options for executable
build_exe_options = {
# Let cx_Freeze auto-discover packages from main.py instead of explicit listing
"packages": [
"PySide6.QtCore",
"PySide6.QtGui",
"PySide6.QtWidgets",
# Additional packages that might be needed
"requests",
"PIL", # Pillow is imported as PIL
"packaging",
"markdown",
"playsound3",
"loguru",
"setuptools"
],
# Packages to exclude (to reduce size)
"excludes": [
"PySide6.QtBluetooth",
"PySide6.QtNetwork",
"PySide6.QtOpenGL",
"PySide6.QtPrintSupport",
"PySide6.QtSvg",
"PySide6.QtTest",
"PySide6.QtXml",
"PySide6.QtSql",
"PySide6.QtHelp",
"PySide6.QtMultimedia",
"PySide6.QtQml",
"PySide6.QtQuick",
"PySide6.QtWebEngineCore",
"PIL.ImageDraw",
"PIL.ImageFont",
"numpy",
"scipy",
"wx",
"pandas",
"tkinter",
"yt_dlp", # Excluded - downloaded dynamically at runtime (all imports are conditional)
"unittest",
"test",
"tests"
],
# Include files (assets and source code)
"include_files": [
("src/", "src/"), # Include entire src directory
("assets/Icon/icon.png", "assets/Icon/icon.png"),
("assets/sound/notification.mp3", "assets/sound/notification.mp3"),
("assets/branding/icons/YTSage.ico", "YTSage.ico")
],
# Zip includes (can help reduce file count)
"zip_include_packages": "*",
"zip_exclude_packages": [],
# Other options
"optimize": 2, # Optimize bytecode
"build_exe": "dist/YTSage", # Output directory for executable
}
# MSI build options - simplified to avoid cx_Freeze issues
bdist_msi_options = {
"upgrade_code": "{12345678-1234-5678-9ABC-123456789ABC}", # Generate unique GUID
"add_to_path": False,
"initial_target_dir": r"[ProgramFilesFolder]\YTSage"
# Removed target_name and summary_data that might cause silent failures
}
# Base configuration for Windows GUI application
base = None
if sys.platform == "win32":
base = "Win32GUI" # This prevents console window from appearing
# Executable configuration
executable = Executable(
"main.py",
base=base,
icon="assets/branding/icons/YTSage.ico",
target_name="YTSage-v4.7.4.exe",
copyright="Copyright (c) 2024-2025 YTSage",
trademarks="YTSage"
)
# Setup configuration
setup(
name="YTSage",
version="4.7.4", # Updated to match expected version
description="YouTube Video Downloader",
author="oop7",
author_email="oop7_support@proton.me",
url="https://github.com/oop7/ytsage",
license="MIT",
options={
"build_exe": build_exe_options,
"bdist_msi": bdist_msi_options
},
executables=[executable]
)