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
1. **Setup**: Uses Python 3.12.10 on Windows
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
## 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:
- `YTSage-v<version>.msi` - Standard Windows installer
- `YTSage-v<version>.zip` - Standard portable version
- `YTSage-v<version>-ffmpeg.msi` - FFmpeg bundle installer
- `YTSage-v<version>-ffmpeg.zip` - FFmpeg bundle portable
### Release Notes
@@ -55,8 +53,7 @@ Automatic release notes are generated including:
### Automatic Version Detection
- Extracts version from git tag (removes 'v' prefix)
- Updates setup.py files with correct version
- Names all artifacts consistently
- Names all artifacts consistently (ZIPs)
### Caching
- Python dependencies are cached to speed up builds
@@ -98,16 +95,16 @@ Key configuration options:
- Release note templates
### Adding New Build Types
To add new build configurations:
1. Create new setup-*.py files
2. Add build steps to the workflow
3. Update artifact collection logic
To add new platform packages (e.g., .dmg for macOS, .deb for Linux):
1. Add separate jobs in `.github/workflows/build-windows.yml` (or a new workflow) targeting the OS (macos-latest, ubuntu-latest).
2. Build the executable with cx_Freeze or PyInstaller for that platform.
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
- The workflow only runs on Windows
- Requires Python 3.12.10 for MSI compatibility
- All builds use cx_Freeze for packaging
- FFmpeg binaries are expected in standard locations
- Draft releases allow for review before publication
+79 -111
View File
@@ -55,79 +55,38 @@ jobs:
pip install --no-cache-dir cx_Freeze
pip install --no-cache-dir yt-dlp
- name: Update version in setup files
- name: Prepare build variables
shell: powershell
run: |
$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
run: |
.\venv\Scripts\Activate.ps1
Write-Host "Building Standard Version..."
$version = "$env:VERSION"
Write-Host "Building Standard Version v$version ..."
# Clean previous build artifacts
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 "dist") { Remove-Item "dist" -Recurse -Force }
# Build executable
python build\windows\setup.py build
# Build MSI installer with error handling
Write-Host "Building standard MSI installer..."
Write-Host "Current working directory: $(Get-Location)"
Write-Host "Setup.py path: $(Resolve-Path 'build\windows\setup.py')"
# Run with maximum verbosity to capture any hidden errors
python build\windows\setup.py bdist_msi --verbose
if ($LASTEXITCODE -eq 0) {
Write-Host "Standard MSI build completed successfully"
# List MSI files created
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
}
# Build executable using cx_Freeze CLI
cxfreeze main.py `
--target-dir "dist\YTSage" `
--base-name Win32GUI `
--icon "assets\branding\icons\YTSage.ico" `
--target-name "YTSage-v$version.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"
- name: Setup FFmpeg for bundle
shell: powershell
@@ -179,71 +138,82 @@ jobs:
exit 1
}
- name: Build FFmpeg Version (MSI only)
- name: Build FFmpeg Version (ZIP)
shell: powershell
run: |
.\venv\Scripts\Activate.ps1
Write-Host "Building FFmpeg Version..."
$version = "$env:VERSION"
Write-Host "Building FFmpeg Version v$version ..."
# Clean previous build artifacts
if (Test-Path "build\exe.*") { Remove-Item "build\exe.*" -Recurse -Force }
if (Test-Path "build\bdist.*") { Remove-Item "build\bdist.*" -Recurse -Force }
# Build executable with FFmpeg
python build\windows\setup-ffmpeg.py build
# Build MSI installer with FFmpeg and error handling
Write-Host "Building FFmpeg MSI installer..."
python build\windows\setup-ffmpeg.py bdist_msi
if ($LASTEXITCODE -eq 0) {
Write-Host "FFmpeg MSI build completed successfully"
# List MSI files created
Write-Host "MSI files in dist directory after FFmpeg build:"
if (Test-Path "dist") {
Get-ChildItem -Path "dist" -Filter "*.msi" | ForEach-Object { Write-Host " $($_.Name)" }
}
} else {
Write-Host "Error building FFmpeg MSI - exit code: $LASTEXITCODE"
exit 1
# Prepare optional FFmpeg include arguments
$ffinc1 = ""
$ffinc2 = ""
$ffinc3 = ""
if ($env:FFMPEG_PATH) {
$ffmpegExe = Join-Path $env:FFMPEG_PATH "ffmpeg.exe"
$ffprobeExe = Join-Path $env:FFMPEG_PATH "ffprobe.exe"
$ffplayExe = Join-Path $env:FFMPEG_PATH "ffplay.exe"
if (Test-Path $ffmpegExe) { $ffinc1 = "--include-files `"$ffmpegExe:ffmpeg.exe`"" }
if (Test-Path $ffprobeExe) { $ffinc2 = "--include-files `"$ffprobeExe:ffprobe.exe`"" }
if (Test-Path $ffplayExe) { $ffinc3 = "--include-files `"$ffplayExe:ffplay.exe`"" }
}
- name: Prepare release artifacts
# 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: Package and prepare release artifacts (ZIPs)
shell: powershell
run: |
$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
# List all files in dist directory first
# List dist directories
Write-Host "Files in dist directory:"
if (Test-Path "dist") {
Get-ChildItem -Path "dist" -Recurse | ForEach-Object { Write-Host " $($_.FullName)" }
} else {
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"
# Create Standard ZIP
$standardDist = Join-Path (Get-Location) "dist\YTSage"
if (Test-Path $standardDist) {
$stdZip = "artifacts\YTSage-v$version.zip"
if (Test-Path $stdZip) { Remove-Item $stdZip -Force }
Compress-Archive -Path "$standardDist\*" -DestinationPath $stdZip -CompressionLevel Optimal
Write-Host "Created: $(Resolve-Path $stdZip)"
} 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)" }
Write-Host "Warning: Standard dist folder not found at $standardDist"
}
# Find and copy FFmpeg build artifacts from dist directory
# Look for any MSI file that contains "ffmpeg" or "FFmpeg" in the name
$ffmpegMsi = Get-ChildItem -Path "dist" -Filter "*.msi" | Where-Object { $_.Name -like "*ffmpeg*" -or $_.Name -like "*FFmpeg*" } | Select-Object -First 1
if ($ffmpegMsi) {
Copy-Item $ffmpegMsi.FullName "artifacts\YTSage-v$version-ffmpeg.msi"
Write-Host "Copied: $($ffmpegMsi.Name) -> YTSage-v$version-ffmpeg.msi"
# 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 MSI not found"
Write-Host "Warning: FFmpeg dist folder not found at $ffmpegDist"
}
# List all artifacts
@@ -273,30 +243,28 @@ jobs:
## Downloads
### Standard Version
- **YTSage-v${{ steps.get_version.outputs.VERSION }}.msi** - Windows Installer (Downloads FFmpeg when needed)
### Standard Version (ZIP)
- **YTSage-v${{ steps.get_version.outputs.VERSION }}.zip** Portable ZIP (downloads FFmpeg on first use if needed)
### FFmpeg Bundle Version
- **YTSage-v${{ steps.get_version.outputs.VERSION }}-ffmpeg.msi** - Windows Installer with FFmpeg included
### FFmpeg Bundle Version (ZIP)
- **YTSage-v${{ steps.get_version.outputs.VERSION }}-ffmpeg.zip** Portable ZIP with FFmpeg included
## Installation Instructions
## Installation / Usage
### MSI Installer
1. Download the `.msi` file
2. Double-click to run the installer
3. Follow the installation wizard
4. YTSage will be available in your Start Menu
1. Download the `.zip` file you want.
2. Right-click the ZIP, choose Properties, and if you see "Unblock", check it and Apply.
3. Extract the ZIP to a folder (e.g., `C:\Apps\YTSage`).
4. Run `YTSage-v${{ steps.get_version.outputs.VERSION }}.exe` inside the extracted folder.
## System Requirements
- Windows 10/11 (64-bit)
- .NET Framework (usually pre-installed)
- Internet connection for yt-dlp updates
## Notes
- **Standard version**: Downloads FFmpeg automatically when needed
- **FFmpeg bundle**: Includes FFmpeg binaries for offline use
- **Standard version**: Downloads FFmpeg automatically when needed.
- **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]
)