Revamp Windows build workflow and update Python version

Updated the build-windows.yml workflow to use Python 3.13.6, improved build steps for both standard and FFmpeg bundle versions, added dependency caching, enhanced artifact packaging, and modernized the release draft with clearer instructions and changelog links.
This commit is contained in:
Your Name
2025-08-27 21:06:46 +03:00
parent b45434bf6e
commit a344d084a6
+258 -120
View File
@@ -9,141 +9,279 @@ permissions:
contents: write contents: write
env: env:
PYTHON_VERSION: '3.12.10' PYTHON_VERSION: '3.13.6'
jobs: jobs:
build-windows: build-windows:
runs-on: windows-latest runs-on: windows-latest
defaults:
run:
shell: pwsh
steps: steps:
- name: Checkout - name: Checkout code
uses: actions/checkout@v4 uses: actions/checkout@v4
with: with:
fetch-depth: 0 fetch-depth: 0
- name: Extract version from tag
id: get_version
shell: powershell
run: |
$tag = "${{ github.ref_name }}"
$version = $tag -replace '^v', ''
Write-Host "Extracted version: $version"
echo "VERSION=$version" >> $env:GITHUB_OUTPUT
- name: Derive version from tag - name: Setup Python
id: version uses: actions/setup-python@v4
run: | with:
$tag = "${{ github.ref_name }}" python-version: ${{ env.PYTHON_VERSION }}
$ver = $tag -replace '^v',''
echo "VERSION=$ver" >> $env:GITHUB_OUTPUT
echo "VERSION=$ver" >> $env:GITHUB_ENV
- name: Setup Python ${{ env.PYTHON_VERSION }} - name: Cache Python dependencies
uses: actions/setup-python@v5 uses: actions/cache@v3
with: with:
python-version: ${{ env.PYTHON_VERSION }} path: |
venv
~\AppData\Local\pip\Cache
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
restore-keys: |
${{ runner.os }}-pip-
- name: Create venv and install dependencies - name: Create virtual environment and install dependencies
run: | shell: powershell
$py = $env:pythonLocation run: |
& "$py\python.exe" -m venv venv python -m venv venv
.\venv\Scripts\Activate.ps1 .\venv\Scripts\Activate.ps1
python -m pip install --upgrade pip python -m pip install --upgrade pip
pip install --no-cache-dir -r requirements.txt pip install --no-cache-dir -r requirements.txt
pip install --no-cache-dir cx_Freeze yt-dlp pip install --no-cache-dir cx_Freeze
pip install --no-cache-dir yt-dlp
- name: Build Standard (portable ZIP) - name: Prepare build variables
run: | shell: powershell
.\venv\Scripts\Activate.ps1 run: |
$version = "$env:VERSION" $version = "${{ steps.get_version.outputs.VERSION }}"
Write-Host "Building Standard v$version" echo "VERSION=$version" >> $env:GITHUB_ENV
Write-Host "Prepared build variables for version: $version"
- name: Build Standard Version (ZIP)
shell: powershell
run: |
.\venv\Scripts\Activate.ps1
$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 using cx_Freeze CLI (use python -m to ensure proper base handling)
python -m cx_Freeze 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" `
--include-files "assets"
# Remove screenshots folder to reduce build size
if (Test-Path "dist\YTSage\assets\branding\screenshots") {
Remove-Item "dist\YTSage\assets\branding\screenshots" -Recurse -Force
Write-Host "Removed screenshots folder from standard build"
}
- name: Setup FFmpeg for bundle
shell: powershell
run: |
Write-Host "Setting up FFmpeg for bundled version..."
# Create ffmpeg directory
New-Item -ItemType Directory -Path "ffmpeg-temp" -Force
# Download FFmpeg
$ffmpegUrl = "https://github.com/GyanD/codexffmpeg/releases/download/7.1.1/ffmpeg-7.1.1-full_build.zip"
Write-Host "Downloading FFmpeg from: $ffmpegUrl"
Invoke-WebRequest -Uri $ffmpegUrl -OutFile "ffmpeg-temp\ffmpeg.zip"
# Extract FFmpeg
Expand-Archive -Path "ffmpeg-temp\ffmpeg.zip" -DestinationPath "ffmpeg-temp" -Force
# Find the extracted directory (GyanD ffmpeg has specific naming)
$ffmpegDir = Get-ChildItem -Path "ffmpeg-temp" -Directory | Where-Object { $_.Name -like "ffmpeg-*" } | Select-Object -First 1
if ($ffmpegDir) {
# Set environment variable for the build
$ffmpegBinPath = Join-Path $ffmpegDir.FullName "bin"
Write-Host "FFmpeg binaries found at: $ffmpegBinPath"
echo "FFMPEG_PATH=$ffmpegBinPath" >> $env:GITHUB_ENV
# Verify files exist
$ffmpegExe = Join-Path $ffmpegBinPath "ffmpeg.exe"
$ffprobeExe = Join-Path $ffmpegBinPath "ffprobe.exe"
$ffplayExe = Join-Path $ffmpegBinPath "ffplay.exe"
if (Test-Path $ffmpegExe) {
Write-Host "[OK] ffmpeg.exe found"
} else {
Write-Host "[ERROR] ffmpeg.exe missing"
}
if (Test-Path $ffprobeExe) {
Write-Host "[OK] ffprobe.exe found"
} else {
Write-Host "[ERROR] ffprobe.exe missing"
}
if (Test-Path $ffplayExe) {
Write-Host "[OK] ffplay.exe found"
} else {
Write-Host "[ERROR] ffplay.exe missing"
}
} else {
Write-Host "Error: Could not find FFmpeg directory after extraction"
exit 1
}
- name: Build FFmpeg Version (ZIP)
shell: powershell
run: |
.\venv\Scripts\Activate.ps1
$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 using cx_Freeze CLI (use python -m to ensure proper base handling)
python -m cx_Freeze 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" `
--include-files "assets"
if (Test-Path "build") { Remove-Item "build" -Recurse -Force } # Remove screenshots folder to reduce build size
if (Test-Path "dist") { Remove-Item "dist" -Recurse -Force } if (Test-Path "dist\YTSage-FFmpeg\assets\branding\screenshots") {
Remove-Item "dist\YTSage-FFmpeg\assets\branding\screenshots" -Recurse -Force
Write-Host "Removed screenshots folder from FFmpeg build"
}
.\venv\Scripts\python.exe -m cx_Freeze.cli cxfreeze main.py ` # Copy FFmpeg binaries into dist folder post-build (more reliable than CLI include)
--target-dir "dist\YTSage" ` if ($env:FFMPEG_PATH) {
--base-name Win32GUI ` $destDir = "dist\YTSage-FFmpeg"
--icon "assets\branding\icons\YTSage.ico" ` $ffmpegExe = Join-Path $env:FFMPEG_PATH "ffmpeg.exe"
--target-name "YTSage-v$version.exe" ` $ffprobeExe = Join-Path $env:FFMPEG_PATH "ffprobe.exe"
--optimize 2 ` $ffplayExe = Join-Path $env:FFMPEG_PATH "ffplay.exe"
--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" `
--include-files "assets"
$stdScreenshots = "dist\YTSage\assets\branding\screenshots" foreach ($file in @($ffmpegExe, $ffprobeExe, $ffplayExe)) {
if (Test-Path $stdScreenshots) { Remove-Item $stdScreenshots -Recurse -Force } if (Test-Path $file) {
$name = Split-Path $file -Leaf
- name: Download FFmpeg bundle Copy-Item $file -Destination (Join-Path $destDir $name) -Force
run: | Write-Host "Copied $name into $destDir"
New-Item -ItemType Directory -Path "ffmpeg-temp" -Force | Out-Null } else {
$ffmpegUrl = "https://github.com/GyanD/codexffmpeg/releases/download/7.1.1/ffmpeg-7.1.1-full_build.zip" Write-Host "Warning: FFmpeg binary not found: $file"
Invoke-WebRequest -Uri $ffmpegUrl -OutFile "ffmpeg-temp\ffmpeg.zip"
Expand-Archive -Path "ffmpeg-temp\ffmpeg.zip" -DestinationPath "ffmpeg-temp" -Force
$ffmpegDir = Get-ChildItem -Path "ffmpeg-temp" -Directory | Where-Object { $_.Name -like "ffmpeg-*" } | Select-Object -First 1
if (-not $ffmpegDir) { Write-Error "FFmpeg dir not found"; exit 1 }
$ffmpegBin = Join-Path $ffmpegDir.FullName "bin"
echo "FFMPEG_PATH=$ffmpegBin" >> $env:GITHUB_ENV
- name: Build FFmpeg bundle (portable ZIP)
run: |
.\venv\Scripts\Activate.ps1
$version = "$env:VERSION"
Write-Host "Building FFmpeg bundle v$version"
if (Test-Path "build\exe.*") { Remove-Item "build\exe.*" -Recurse -Force }
if (Test-Path "build\bdist.*") { Remove-Item "build\bdist.*" -Recurse -Force }
.\venv\Scripts\python.exe -m 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" `
--include-files "assets"
if ($env:FFMPEG_PATH) {
$dest = "dist\YTSage-FFmpeg"
foreach ($n in 'ffmpeg.exe','ffprobe.exe','ffplay.exe') {
$src = Join-Path $env:FFMPEG_PATH $n
if (Test-Path $src) { Copy-Item $src -Destination (Join-Path $dest $n) -Force }
} }
} }
}
- name: Package and prepare release artifacts (ZIPs)
shell: powershell
run: |
$version = "${{ steps.get_version.outputs.VERSION }}"
Write-Host "Preparing ZIP artifacts for version: $version"
New-Item -ItemType Directory -Path "artifacts" -Force
# 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
}
$ffShots = "dist\YTSage-FFmpeg\assets\branding\screenshots" # Create Standard ZIP
if (Test-Path $ffShots) { Remove-Item $ffShots -Recurse -Force } $standardDist = Join-Path (Get-Location) "dist\YTSage"
if (Test-Path $standardDist) {
$stdZip = "artifacts\YTSage-v$version-portable.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 dist folder not found at $standardDist"
}
- name: Package artifacts # Create FFmpeg ZIP
run: | $ffmpegDist = Join-Path (Get-Location) "dist\YTSage-FFmpeg"
$version = "$env:VERSION" if (Test-Path $ffmpegDist) {
New-Item -ItemType Directory -Path "artifacts" -Force | Out-Null $ffZip = "artifacts\YTSage-v$version-ffmpeg-portable.zip"
if (Test-Path $ffZip) { Remove-Item $ffZip -Force }
$std = "artifacts\YTSage-v$version-portable.zip" Compress-Archive -Path "$ffmpegDist\*" -DestinationPath $ffZip -CompressionLevel Optimal
if (Test-Path "dist\YTSage") { Write-Host "Created: $(Resolve-Path $ffZip)"
if (Test-Path $std) { Remove-Item $std -Force } } else {
Compress-Archive -Path "dist\YTSage\*" -DestinationPath $std -CompressionLevel Optimal Write-Host "Warning: FFmpeg dist folder not found at $ffmpegDist"
}
# List all artifacts
Write-Host "Final artifacts:"
if (Test-Path "artifacts") {
$artifactFiles = Get-ChildItem artifacts
if ($artifactFiles) {
$artifactFiles | ForEach-Object { Write-Host " $($_.Name)" }
} else {
Write-Host " No artifacts created!"
} }
} else {
Write-Host " Artifacts directory not found!"
}
$ff = "artifacts\YTSage-v$version-ffmpeg-portable.zip" - name: Create draft release
if (Test-Path "dist\YTSage-FFmpeg") { uses: softprops/action-gh-release@v1
if (Test-Path $ff) { Remove-Item $ff -Force } with:
Compress-Archive -Path "dist\YTSage-FFmpeg\*" -DestinationPath $ff -CompressionLevel Optimal tag_name: ${{ github.ref_name }}
} name: YTSage ${{ github.ref_name }}
draft: true
Write-Host "Artifacts:" prerelease: false
Get-ChildItem artifacts | ForEach-Object { Write-Host " - $($_.Name)" } body: |
# YTSage ${{ github.ref_name }}
- name: Create draft release
uses: softprops/action-gh-release@v2 **Release Date**: ${{ github.event.head_commit.timestamp }}
with:
tag_name: ${{ github.ref_name }} ## Downloads
name: YTSage ${{ github.ref_name }}
draft: true ### Standard Version (ZIP)
prerelease: false - **YTSage-v${{ steps.get_version.outputs.VERSION }}-portable.zip** Portable ZIP (downloads FFmpeg on first use if needed)
body: |
## Downloads ### FFmpeg Bundle Version (ZIP)
- **YTSage-v${{ steps.get_version.outputs.VERSION }}-ffmpeg-portable.zip** Portable ZIP with FFmpeg included
- YTSage-v${{ steps.version.outputs.VERSION }}-portable.zip (portable)
- YTSage-v${{ steps.version.outputs.VERSION }}-ffmpeg-portable.zip (portable with FFmpeg included) ## Installation / Usage
Extract and run the EXE. Console window is hidden. 1. Download the `.zip` file you want.
files: | 2. Right-click the ZIP, choose Properties, and if you see "Unblock", check it and Apply.
artifacts/* 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)
- Internet connection for yt-dlp updates
## Notes
- **Standard version**: Downloads FFmpeg automatically when needed.
- **FFmpeg bundle**: Includes FFmpeg binaries for offline use.
---
**Full Changelog**: https://github.com/${{ github.repository }}/compare/${{ github.ref_name }}
files: |
artifacts/*
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}