Switch Windows build to Python script, remove PowerShell

Replaces PowerShell build script with Python in CI workflow and deletes the old build/README.md and windows-build-universal.ps1. This streamlines the build process and consolidates build logic into Python for better maintainability.
This commit is contained in:
Your Name
2025-08-27 01:57:58 +03:00
parent c54893fd1b
commit b337667ce8
3 changed files with 2 additions and 687 deletions
+2 -2
View File
@@ -75,14 +75,14 @@ jobs:
run: | run: |
.\venv\Scripts\Activate.ps1 .\venv\Scripts\Activate.ps1
Write-Host "Building Standard Version..." Write-Host "Building Standard Version..."
.\build\windows\windows-build-universal.ps1 -Verbose python build\windows\windows_build_universal.py --verbose
- name: Build FFmpeg Version (MSI + ZIP) - name: Build FFmpeg Version (MSI + ZIP)
shell: powershell shell: powershell
run: | run: |
.\venv\Scripts\Activate.ps1 .\venv\Scripts\Activate.ps1
Write-Host "Building FFmpeg Version..." Write-Host "Building FFmpeg Version..."
.\build\windows\windows-build-universal.ps1 -FFmpeg -Verbose python build\windows\windows_build_universal.py --ffmpeg --verbose
- name: Prepare release artifacts - name: Prepare release artifacts
shell: powershell shell: powershell
-211
View File
@@ -1,211 +0,0 @@
# YTSage Build System
This directory contains all build-related files and configurations for creating YTSage distributions.
## 🏗️ Build Architecture
### Automated Builds (CI/CD)
- **GitHub Actions**: Fully automated releases triggered by git tags
- **Location**: `.github/workflows/build-windows.yml`
- **Triggers**: Version tags (e.g., `v4.8.0`, `v4.9.1`)
- **Output**: MSI installers + ZIP portable versions (Standard & FFmpeg)
### Local Development Builds
- **Script**: `build/windows/windows-build-universal.ps1`
- **Purpose**: Local testing and development
- **Flexibility**: Manual control over build options
## 📁 Directory Structure
```
build/
├── windows/
│ ├── setup.py # Standard cx_Freeze configuration
│ ├── setup-ffmpeg.py # FFmpeg bundle cx_Freeze configuration
│ ├── windows-build-universal.ps1 # Local development build script
│ └── README.md # This file
└── (future platforms)
```
## 🚀 Quick Start
### For Releases (Recommended)
```bash
# Create and push a version tag
git tag v4.8.1
git push origin v4.8.1
# GitHub Actions will automatically:
# 1. Build both versions
# 2. Create MSI installers
# 3. Create ZIP portable versions
# 4. Generate draft release with artifacts
```
### For Local Development
```powershell
# Navigate to project root
cd c:\path\to\YTSage\build-env
# Run the universal build script
.\build\windows\windows-build-universal.ps1 -Verbose
# Build only FFmpeg version
.\build\windows\windows-build-universal.ps1 -FFmpeg -Verbose
# Build only MSI (no ZIP)
.\build\windows\windows-build-universal.ps1 -MSIOnly
# Build only ZIP (no MSI)
.\build\windows\windows-build-universal.ps1 -ZIPOnly
```
## 📋 Build Configurations
### Standard Version (`setup.py`)
- **Target**: General users
- **Size**: Smaller download
- **FFmpeg**: Downloaded automatically when needed
### FFmpeg Bundle (`setup-ffmpeg.py`)
- **Target**: Users without internet or restricted environments
- **Size**: Larger download (~100MB+)
- **FFmpeg**: Included in installer
## 🔧 Build Script Options
### windows-build-universal.ps1 Parameters
```powershell
-SkipVenv # Skip virtual environment creation
-Verbose # Enable detailed output
-FFmpeg # Build FFmpeg bundle version
-MSIOnly # Create only MSI installer
-ZIPOnly # Create only ZIP distribution
-OutputDir # Custom output directory (default: "releases")
-FFmpegPath # Custom FFmpeg binaries path
```
### Examples
```powershell
# Full build with verbose output
.\build\windows\windows-build-universal.ps1 -Verbose
# Quick standard version build
.\build\windows\windows-build-universal.ps1 -MSIOnly
# FFmpeg version to custom directory
.\build\windows\windows-build-universal.ps1 -FFmpeg -OutputDir "custom-builds"
```
## 📦 Output Artifacts
### CI/CD Builds (GitHub Actions)
```
releases/
├── YTSage-v4.8.1.msi # Standard installer
├── YTSage-v4.8.1.zip # Standard portable
├── YTSage-v4.8.1-ffmpeg.msi # FFmpeg installer
└── YTSage-v4.8.1-ffmpeg.zip # FFmpeg portable
```
### Local Builds
```
releases/
├── YTSage-4.8.1.msi # Standard installer
├── YTSage-4.8.1.zip # Standard portable
├── YTSage-4.8.1-ffmpeg.msi # FFmpeg installer (if built)
└── YTSage-4.8.1-ffmpeg.zip # FFmpeg portable (if built)
```
## 🛠️ Build Requirements
### System Requirements
- **OS**: Windows 10/11
- **PowerShell**: 5.1 or later
- **Python**: 3.12+ (3.12.10 recommended for MSI compatibility)
- **Memory**: 4GB+ RAM recommended
- **Storage**: 2GB+ free space
### Python Dependencies
- **cx_Freeze**: 6.x for MSI creation
- **playsound3**: Audio notification system
- **All project dependencies**: Listed in `requirements.txt`
### Optional Components
- **FFmpeg**: For bundled builds (auto-downloaded if not present)
- **Code Signing**: Certificate for signed installers (optional)
## 🔍 Troubleshooting
### Common Issues
#### Build Fails with Import Errors
```powershell
# Recreate virtual environment
Remove-Item venv -Recurse -Force
.\build\windows\windows-build-universal.ps1 -Verbose
```
#### MSI Creation Fails
- Ensure Python 3.12.x (required for MSI compatibility)
- Check cx_Freeze version (should be 6.x)
- Verify all dependencies are installed
#### FFmpeg Not Found
```powershell
# Specify custom FFmpeg path
.\build\windows\windows-build-universal.ps1 -FFmpeg -FFmpegPath "C:\ffmpeg\bin"
```
#### CI/CD Build Fails
- Check GitHub Actions logs
- Verify tag format (`v*`)
- Ensure all required files are present
- Check file paths in workflow
### Debug Mode
```powershell
# Enable maximum verbosity
.\build\windows\windows-build-universal.ps1 -Verbose -Debug
```
## 📈 Version Management
### Automatic Version Detection
- **CI/CD**: Extracts version from git tag
- **Local**: Uses version from setup.py files
- **Format**: Semantic versioning (e.g., 4.8.1)
### Manual Version Update
```powershell
# Update version in setup files
(Get-Content build\windows\setup.py) -replace 'version="[^"]*"', 'version="4.8.2"' | Set-Content build\windows\setup.py
(Get-Content build\windows\setup-ffmpeg.py) -replace 'version="[^"]*"', 'version="4.8.2"' | Set-Content build\windows\setup-ffmpeg.py
```
## 🔮 Future Enhancements
### Planned Features
- **Linux builds**: Future cross-platform support
- **macOS builds**: Apple platform support
- **Auto-updater**: Built-in update mechanism
- **Digital signing**: Code signing for Windows
### Contributing
- Build improvements welcome
- Cross-platform configurations
- Performance optimizations
- Documentation enhancements
## 📚 Related Documentation
- **CI/CD Guide**: `.github/CI_CD_README.md`
- **Project README**: `../README.md`
- **Release Notes**: GitHub Releases
- **Issue Tracking**: GitHub Issues
---
**Last Updated**: August 2025
**Build System Version**: 4.8.x
**Supported Platforms**: Windows (primary)
-474
View File
@@ -1,474 +0,0 @@
#Requires -Version 5.1
<#
.SYNOPSIS
YTSage Universal Build Script - Creates MSI Installers and ZIP Distributions
.DESCRIPTION
This PowerShell script builds YTSage using cx_Freeze and creates:
- MSI installer packages
- ZIP file distributions (portable)
- Both standard and FFmpeg versions
.NOTES
Version: Auto-detected from setup.py
Location: build/windows/windows-build-universal.ps1
Usage: Run from project root directory (build-env/)
IMPORTANT: This script must be executed from the project root directory!
Example: .\build\windows\windows-build-universal.ps1 -Verbose
#>
param(
[switch]$SkipVenv,
[switch]$Verbose,
[switch]$FFmpeg,
[switch]$MSIOnly,
[switch]$ZIPOnly,
[string]$OutputDir = "releases",
[string]$FFmpegPath = "$env:LOCALAPPDATA\ffmpeg\ffmpeg-7.1.1-full_build\bin"
)
# Set error handling
$ErrorActionPreference = "Stop"
# Script configuration - Auto-detect version from setup.py
$APP_NAME = "YTSage"
$RELEASE_DATE = Get-Date -Format "yyyy-MM-dd"
# Auto-detect version from setup.py
function Get-VersionFromSetup {
try {
$setupContent = Get-Content "build\windows\setup.py" -Raw
if ($setupContent -match 'version="([^"]*)"') {
$version = $matches[1]
Write-Host "Detected version: $version" -ForegroundColor Gray
return $version
} else {
Write-Warning-Custom "Could not extract version from setup.py, using 1.0.0"
return "1.0.0"
}
} catch {
Write-Warning-Custom "Could not read setup.py, using 1.0.0"
return "1.0.0"
}
}
$SCRIPT_VERSION = Get-VersionFromSetup
# Determine build type
$BUILD_TYPE = if ($FFmpeg) { "FFmpeg" } else { "Standard" }
$SETUP_FILE = if ($FFmpeg) { "build\windows\setup-ffmpeg.py" } else { "build\windows\setup.py" }
$OUTPUT_NAME_BASE = if ($FFmpeg) { "$APP_NAME-v$SCRIPT_VERSION-ffmpeg" } else { "$APP_NAME-v$SCRIPT_VERSION" }
# Banner
Write-Host "================================================" -ForegroundColor Cyan
Write-Host "YTSage Universal Build Script v$SCRIPT_VERSION" -ForegroundColor Cyan
Write-Host "Build Type: $BUILD_TYPE" -ForegroundColor Yellow
Write-Host "================================================" -ForegroundColor Cyan
Write-Host ""
Write-Host "This script will create:" -ForegroundColor Yellow
if (-not $ZIPOnly) {
Write-Host "- MSI installer package" -ForegroundColor White
}
if (-not $MSIOnly) {
Write-Host "- ZIP portable distribution" -ForegroundColor White
}
Write-Host "- Professional packaging with metadata" -ForegroundColor White
Write-Host ""
function Write-Step {
param($Message)
Write-Host "[STEP] $Message" -ForegroundColor Green
}
function Write-Error-Custom {
param($Message)
Write-Host "[ERROR] $Message" -ForegroundColor Red
}
function Write-Warning-Custom {
param($Message)
Write-Host "[WARNING] $Message" -ForegroundColor Yellow
}
function Test-Command {
param($Command)
try {
& $Command --version | Out-Null
return $true
}
catch {
return $false
}
}
function Test-FFmpegBinaries {
if (-not $FFmpeg) { return $null }
Write-Step "Checking FFmpeg binaries..."
$ffmpegExe = Join-Path $FFmpegPath "ffmpeg.exe"
$ffprobeExe = Join-Path $FFmpegPath "ffprobe.exe"
$ffplayExe = Join-Path $FFmpegPath "ffplay.exe"
$missingFiles = @()
if (-not (Test-Path $ffmpegExe)) { $missingFiles += "ffmpeg.exe" }
if (-not (Test-Path $ffprobeExe)) { $missingFiles += "ffprobe.exe" }
if (-not (Test-Path $ffplayExe)) { $missingFiles += "ffplay.exe" }
if ($missingFiles.Count -gt 0) {
Write-Error-Custom "Missing FFmpeg binaries: $($missingFiles -join ', ')"
Write-Host "Expected FFmpeg path: $FFmpegPath" -ForegroundColor Yellow
throw "FFmpeg binaries not found"
}
Write-Host "✓ Found all FFmpeg binaries" -ForegroundColor Green
return @{
ffmpeg = $ffmpegExe
ffprobe = $ffprobeExe
ffplay = $ffplayExe
}
}
function New-VirtualEnvironment {
Write-Step "Creating virtual environment..."
try {
python -m venv venv
Write-Host "Virtual environment created successfully." -ForegroundColor Green
}
catch {
Write-Error-Custom "Failed to create virtual environment: $($_.Exception.Message)"
throw
}
}
function Start-VirtualEnvironment {
Write-Step "Activating virtual environment..."
$venvScript = ".\venv\Scripts\Activate.ps1"
if (-not (Test-Path $venvScript)) {
Write-Error-Custom "Virtual environment activation script not found at: $venvScript"
throw "Virtual environment not properly created"
}
try {
& $venvScript
Write-Host "Virtual environment activated successfully." -ForegroundColor Green
}
catch {
Write-Error-Custom "Failed to activate virtual environment: $($_.Exception.Message)"
throw
}
}
function Install-Dependencies {
Write-Step "Installing/Upgrading required packages..."
try {
Write-Host "Upgrading pip..." -ForegroundColor White
python -m pip install --upgrade pip | Out-Null
Write-Host "Installing requirements..." -ForegroundColor White
pip install --no-cache-dir -r requirements.txt | Out-Null
Write-Host "Installing cx_Freeze..." -ForegroundColor White
pip install --no-cache-dir cx_Freeze | Out-Null
Write-Host "Installing yt-dlp (temporary for build)..." -ForegroundColor White
pip install --no-cache-dir yt-dlp | Out-Null
Write-Host "All packages installed successfully." -ForegroundColor Green
}
catch {
Write-Error-Custom "Failed to install required packages: $($_.Exception.Message)"
throw
}
}
function Remove-BuildArtifacts {
Write-Step "Cleaning previous build artifacts..."
$itemsToRemove = @("build", "dist", $OutputDir)
foreach ($item in $itemsToRemove) {
if (Test-Path $item) {
Remove-Item $item -Recurse -Force
Write-Host "Removed: $item" -ForegroundColor Gray
}
}
Write-Host "Build artifacts cleaned." -ForegroundColor Green
}
function Build-Executable {
param($FFmpegBinaries)
Write-Step "Building executable with cx_Freeze..."
try {
if ($FFmpegBinaries) {
$env:FFMPEG_PATH = $FFmpegBinaries.ffmpeg | Split-Path
}
# Build the executable
python $SETUP_FILE build
if ($LASTEXITCODE -ne 0) {
throw "cx_Freeze build failed with exit code $LASTEXITCODE"
}
Write-Host "Executable built successfully." -ForegroundColor Green
}
catch {
Write-Error-Custom "Failed to build executable: $($_.Exception.Message)"
throw
}
}
function Build-MSI {
Write-Step "Creating MSI installer..."
try {
# Build MSI package
python $SETUP_FILE bdist_msi
if ($LASTEXITCODE -ne 0) {
throw "MSI build failed with exit code $LASTEXITCODE"
}
Write-Host "MSI installer created successfully." -ForegroundColor Green
}
catch {
Write-Error-Custom "Failed to build MSI: $($_.Exception.Message)"
throw
}
}
function New-ZIPDistribution {
Write-Step "Creating ZIP distribution..."
try {
# Ensure output directory exists
if (-not (Test-Path $OutputDir)) {
New-Item -ItemType Directory -Path $OutputDir | Out-Null
}
# Find the built executable directory
$executableDir = if ($FFmpeg) { "dist\YTSage-FFmpeg" } else { "dist\YTSage" }
if (-not (Test-Path $executableDir)) {
throw "Executable directory not found: $executableDir"
}
# Create ZIP file
$zipPath = Join-Path $OutputDir "$OUTPUT_NAME_BASE.zip"
Compress-Archive -Path "$executableDir\*" -DestinationPath $zipPath -Force
Write-Host "ZIP distribution created: $zipPath" -ForegroundColor Green
# Get ZIP file size
$zipSize = (Get-Item $zipPath).Length
$zipSizeMB = [math]::Round($zipSize / 1MB, 2)
Write-Host "ZIP size: $zipSizeMB MB" -ForegroundColor Gray
}
catch {
Write-Error-Custom "Failed to create ZIP distribution: $($_.Exception.Message)"
throw
}
}
function Move-MSIFiles {
Write-Step "Organizing MSI files..."
try {
# Ensure output directory exists
if (-not (Test-Path $OutputDir)) {
New-Item -ItemType Directory -Path $OutputDir | Out-Null
}
# Find and move MSI files
$msiFiles = Get-ChildItem -Path "dist" -Filter "*.msi" -Recurse
foreach ($msiFile in $msiFiles) {
$newName = "$OUTPUT_NAME_BASE.msi"
$destination = Join-Path $OutputDir $newName
Move-Item $msiFile.FullName $destination -Force
Write-Host "MSI installer moved to: $destination" -ForegroundColor Green
# Get MSI file size
$msiSize = (Get-Item $destination).Length
$msiSizeMB = [math]::Round($msiSize / 1MB, 2)
Write-Host "MSI size: $msiSizeMB MB" -ForegroundColor Gray
}
}
catch {
Write-Error-Custom "Failed to organize MSI files: $($_.Exception.Message)"
throw
}
}
function New-ReleaseNotes {
Write-Step "Creating release notes..."
$releaseNotes = "# YTSage v$SCRIPT_VERSION Release`n`n"
$releaseNotes += "**Release Date:** $RELEASE_DATE`n"
$releaseNotes += "**Build Type:** $BUILD_TYPE`n`n"
$releaseNotes += "## Installation Options`n`n"
$releaseNotes += "### MSI Installer (Recommended)`n"
$releaseNotes += "- **File:** $OUTPUT_NAME_BASE.msi`n"
$releaseNotes += "- **Installation:** Double-click to install system-wide`n"
$releaseNotes += "- **Updates:** Automatic update notifications`n"
$releaseNotes += "- **Uninstall:** Via Windows Programs `& Features`n`n"
$releaseNotes += "### Portable ZIP`n"
$releaseNotes += "- **File:** $OUTPUT_NAME_BASE.zip`n"
$releaseNotes += "- **Installation:** Extract and run - no installation required`n"
$releaseNotes += "- **Updates:** Manual download and replacement`n"
$releaseNotes += "- **Uninstall:** Simply delete the folder`n`n"
$releaseNotes += "## System Requirements`n"
$releaseNotes += "- Windows 10/11 64-bit`n"
$releaseNotes += "- .NET Framework 4.7.2 or later`n"
$releaseNotes += "- Internet connection for yt-dlp updates`n`n"
$releaseNotes += "## Support`n"
$releaseNotes += "For issues or questions, please visit the project repository.`n`n"
$releaseNotes += "---`n"
$releaseNotes += "*Built with cx_Freeze*`n"
try {
$releaseNotesPath = Join-Path $OutputDir "RELEASE_NOTES.md"
$releaseNotes | Out-File -FilePath $releaseNotesPath -Encoding UTF8
Write-Host "Release notes created: $releaseNotesPath" -ForegroundColor Green
}
catch {
Write-Warning-Custom "Failed to create release notes: $($_.Exception.Message)"
}
}
function Remove-PostBuildArtifacts {
Write-Step "Cleaning up post-build artifacts..."
try {
if (Test-Path "build") {
Remove-Item "build" -Recurse -Force
Write-Host "Removed build folder." -ForegroundColor Gray
}
Write-Host "Post-build cleanup completed." -ForegroundColor Green
}
catch {
Write-Warning-Custom "Some cleanup operations failed: $($_.Exception.Message)"
}
}
function Stop-VirtualEnvironment {
try {
deactivate
Write-Host "Virtual environment deactivated." -ForegroundColor Green
}
catch {
Write-Warning-Custom "Failed to deactivate virtual environment (this might be normal)"
}
}
function Show-BuildSummary {
Write-Host ""
Write-Host "================================================" -ForegroundColor Cyan
Write-Host "Build completed successfully!" -ForegroundColor Green
Write-Host "================================================" -ForegroundColor Cyan
Write-Host ""
Write-Host "Build Type: $BUILD_TYPE" -ForegroundColor Yellow
Write-Host "Version: $SCRIPT_VERSION" -ForegroundColor Yellow
Write-Host "Output Directory: $OutputDir" -ForegroundColor Yellow
Write-Host ""
if (Test-Path $OutputDir) {
Write-Host "Created files:" -ForegroundColor Green
Get-ChildItem $OutputDir | ForEach-Object {
$size = if ($_.PSIsContainer) { "Folder" } else { "$([math]::Round($_.Length / 1MB, 2)) MB" }
Write-Host "$($_.Name) ($size)" -ForegroundColor White
}
}
Write-Host ""
Write-Host "Distribution ready for release!" -ForegroundColor Green
Write-Host ""
}
# Main execution
try {
# Validation
Write-Step "Checking prerequisites..."
if (-not (Test-Command "python")) {
Write-Error-Custom "Python is not installed or not in PATH"
exit 1
}
if (-not (Test-Path $SETUP_FILE)) {
Write-Error-Custom "Setup file not found: $SETUP_FILE"
exit 1
}
$pythonVersion = python --version
Write-Host "Found: $pythonVersion" -ForegroundColor Green
# Check for Python 3.13 MSI limitation
$pythonVersionInfo = python -c "import sys; print(f'{sys.version_info.major}.{sys.version_info.minor}')"
$isPython313 = $pythonVersionInfo -eq "3.13"
if ($isPython313 -and -not $ZIPOnly) {
Write-Warning-Custom "Python 3.13 detected. MSI building is not supported yet by cx_Freeze."
Write-Host "See: https://github.com/marcelotduarte/cx_Freeze/issues/2837" -ForegroundColor Yellow
Write-Host "Switching to ZIP-only mode. Use Python 3.12 for MSI support." -ForegroundColor Yellow
$ZIPOnly = $true
$MSIOnly = $false
}
# Check FFmpeg if needed
$ffmpegBinaries = Test-FFmpegBinaries
# Virtual environment
if (-not $SkipVenv) {
if (-not (Test-Path "venv")) {
New-VirtualEnvironment
} else {
Write-Step "Using existing virtual environment..."
}
Start-VirtualEnvironment
}
# Build process
Install-Dependencies
Remove-BuildArtifacts
Build-Executable -FFmpegBinaries $ffmpegBinaries
# Create distributions
if (-not $ZIPOnly) {
Build-MSI
Move-MSIFiles
}
if (-not $MSIOnly) {
New-ZIPDistribution
}
New-ReleaseNotes
Remove-PostBuildArtifacts
Show-BuildSummary
}
catch {
Write-Error-Custom "Build failed: $($_.Exception.Message)"
if ($Verbose) {
Write-Host $_.Exception.StackTrace -ForegroundColor Red
}
exit 1
}
finally {
if (-not $SkipVenv) {
Stop-VirtualEnvironment
}
}
Write-Host "Press any key to continue..." -ForegroundColor Gray
$null = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")