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:
+247
-109
@@ -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: Derive version from tag
|
- name: Extract version from tag
|
||||||
id: version
|
id: get_version
|
||||||
run: |
|
shell: powershell
|
||||||
$tag = "${{ github.ref_name }}"
|
run: |
|
||||||
$ver = $tag -replace '^v',''
|
$tag = "${{ github.ref_name }}"
|
||||||
echo "VERSION=$ver" >> $env:GITHUB_OUTPUT
|
$version = $tag -replace '^v', ''
|
||||||
echo "VERSION=$ver" >> $env:GITHUB_ENV
|
Write-Host "Extracted version: $version"
|
||||||
|
echo "VERSION=$version" >> $env:GITHUB_OUTPUT
|
||||||
|
|
||||||
- name: Setup Python ${{ env.PYTHON_VERSION }}
|
- name: Setup Python
|
||||||
uses: actions/setup-python@v5
|
uses: actions/setup-python@v4
|
||||||
with:
|
with:
|
||||||
python-version: ${{ env.PYTHON_VERSION }}
|
python-version: ${{ env.PYTHON_VERSION }}
|
||||||
|
|
||||||
- name: Create venv and install dependencies
|
- name: Cache Python dependencies
|
||||||
run: |
|
uses: actions/cache@v3
|
||||||
$py = $env:pythonLocation
|
with:
|
||||||
& "$py\python.exe" -m venv venv
|
path: |
|
||||||
.\venv\Scripts\Activate.ps1
|
venv
|
||||||
python -m pip install --upgrade pip
|
~\AppData\Local\pip\Cache
|
||||||
pip install --no-cache-dir -r requirements.txt
|
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
|
||||||
pip install --no-cache-dir cx_Freeze yt-dlp
|
restore-keys: |
|
||||||
|
${{ runner.os }}-pip-
|
||||||
|
|
||||||
- name: Build Standard (portable ZIP)
|
- name: Create virtual environment and install dependencies
|
||||||
run: |
|
shell: powershell
|
||||||
.\venv\Scripts\Activate.ps1
|
run: |
|
||||||
$version = "$env:VERSION"
|
python -m venv venv
|
||||||
Write-Host "Building Standard v$version"
|
.\venv\Scripts\Activate.ps1
|
||||||
|
python -m pip install --upgrade pip
|
||||||
|
pip install --no-cache-dir -r requirements.txt
|
||||||
|
pip install --no-cache-dir cx_Freeze
|
||||||
|
pip install --no-cache-dir yt-dlp
|
||||||
|
|
||||||
if (Test-Path "build") { Remove-Item "build" -Recurse -Force }
|
- name: Prepare build variables
|
||||||
if (Test-Path "dist") { Remove-Item "dist" -Recurse -Force }
|
shell: powershell
|
||||||
|
run: |
|
||||||
|
$version = "${{ steps.get_version.outputs.VERSION }}"
|
||||||
|
echo "VERSION=$version" >> $env:GITHUB_ENV
|
||||||
|
Write-Host "Prepared build variables for version: $version"
|
||||||
|
|
||||||
.\venv\Scripts\python.exe -m cx_Freeze.cli cxfreeze main.py `
|
- name: Build Standard Version (ZIP)
|
||||||
--target-dir "dist\YTSage" `
|
shell: powershell
|
||||||
--base-name Win32GUI `
|
run: |
|
||||||
--icon "assets\branding\icons\YTSage.ico" `
|
.\venv\Scripts\Activate.ps1
|
||||||
--target-name "YTSage-v$version.exe" `
|
$version = "$env:VERSION"
|
||||||
--optimize 2 `
|
Write-Host "Building Standard Version v$version ..."
|
||||||
--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"
|
# Clean previous build artifacts
|
||||||
if (Test-Path $stdScreenshots) { Remove-Item $stdScreenshots -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 "dist") { Remove-Item "dist" -Recurse -Force }
|
||||||
|
|
||||||
- name: Download FFmpeg bundle
|
# Build executable using cx_Freeze CLI (use python -m to ensure proper base handling)
|
||||||
run: |
|
python -m cx_Freeze main.py `
|
||||||
New-Item -ItemType Directory -Path "ffmpeg-temp" -Force | Out-Null
|
--target-dir "dist\YTSage" `
|
||||||
$ffmpegUrl = "https://github.com/GyanD/codexffmpeg/releases/download/7.1.1/ffmpeg-7.1.1-full_build.zip"
|
--base-name Win32GUI `
|
||||||
Invoke-WebRequest -Uri $ffmpegUrl -OutFile "ffmpeg-temp\ffmpeg.zip"
|
--icon "assets\branding\icons\YTSage.ico" `
|
||||||
Expand-Archive -Path "ffmpeg-temp\ffmpeg.zip" -DestinationPath "ffmpeg-temp" -Force
|
--target-name "YTSage-v$version.exe" `
|
||||||
$ffmpegDir = Get-ChildItem -Path "ffmpeg-temp" -Directory | Where-Object { $_.Name -like "ffmpeg-*" } | Select-Object -First 1
|
--optimize 2 `
|
||||||
if (-not $ffmpegDir) { Write-Error "FFmpeg dir not found"; exit 1 }
|
--packages "PySide6.QtCore,PySide6.QtGui,PySide6.QtWidgets,requests,PIL,packaging,markdown,playsound3,loguru,setuptools" `
|
||||||
$ffmpegBin = Join-Path $ffmpegDir.FullName "bin"
|
--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" `
|
||||||
echo "FFMPEG_PATH=$ffmpegBin" >> $env:GITHUB_ENV
|
--include-files "src" `
|
||||||
|
--include-files "assets"
|
||||||
|
|
||||||
- name: Build FFmpeg bundle (portable ZIP)
|
# Remove screenshots folder to reduce build size
|
||||||
run: |
|
if (Test-Path "dist\YTSage\assets\branding\screenshots") {
|
||||||
.\venv\Scripts\Activate.ps1
|
Remove-Item "dist\YTSage\assets\branding\screenshots" -Recurse -Force
|
||||||
$version = "$env:VERSION"
|
Write-Host "Removed screenshots folder from standard build"
|
||||||
Write-Host "Building FFmpeg bundle v$version"
|
}
|
||||||
|
|
||||||
if (Test-Path "build\exe.*") { Remove-Item "build\exe.*" -Recurse -Force }
|
- name: Setup FFmpeg for bundle
|
||||||
if (Test-Path "build\bdist.*") { Remove-Item "build\bdist.*" -Recurse -Force }
|
shell: powershell
|
||||||
|
run: |
|
||||||
|
Write-Host "Setting up FFmpeg for bundled version..."
|
||||||
|
|
||||||
.\venv\Scripts\python.exe -m cx_Freeze.cli cxfreeze main.py `
|
# Create ffmpeg directory
|
||||||
--target-dir "dist\YTSage-FFmpeg" `
|
New-Item -ItemType Directory -Path "ffmpeg-temp" -Force
|
||||||
--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) {
|
# Download FFmpeg
|
||||||
$dest = "dist\YTSage-FFmpeg"
|
$ffmpegUrl = "https://github.com/GyanD/codexffmpeg/releases/download/7.1.1/ffmpeg-7.1.1-full_build.zip"
|
||||||
foreach ($n in 'ffmpeg.exe','ffprobe.exe','ffplay.exe') {
|
Write-Host "Downloading FFmpeg from: $ffmpegUrl"
|
||||||
$src = Join-Path $env:FFMPEG_PATH $n
|
Invoke-WebRequest -Uri $ffmpegUrl -OutFile "ffmpeg-temp\ffmpeg.zip"
|
||||||
if (Test-Path $src) { Copy-Item $src -Destination (Join-Path $dest $n) -Force }
|
|
||||||
|
# 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"
|
||||||
|
|
||||||
|
# Remove screenshots folder to reduce build size
|
||||||
|
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"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Copy FFmpeg binaries into dist folder post-build (more reliable than CLI include)
|
||||||
|
if ($env:FFMPEG_PATH) {
|
||||||
|
$destDir = "dist\YTSage-FFmpeg"
|
||||||
|
$ffmpegExe = Join-Path $env:FFMPEG_PATH "ffmpeg.exe"
|
||||||
|
$ffprobeExe = Join-Path $env:FFMPEG_PATH "ffprobe.exe"
|
||||||
|
$ffplayExe = Join-Path $env:FFMPEG_PATH "ffplay.exe"
|
||||||
|
|
||||||
|
foreach ($file in @($ffmpegExe, $ffprobeExe, $ffplayExe)) {
|
||||||
|
if (Test-Path $file) {
|
||||||
|
$name = Split-Path $file -Leaf
|
||||||
|
Copy-Item $file -Destination (Join-Path $destDir $name) -Force
|
||||||
|
Write-Host "Copied $name into $destDir"
|
||||||
|
} else {
|
||||||
|
Write-Host "Warning: FFmpeg binary not found: $file"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$ffShots = "dist\YTSage-FFmpeg\assets\branding\screenshots"
|
- name: Package and prepare release artifacts (ZIPs)
|
||||||
if (Test-Path $ffShots) { Remove-Item $ffShots -Recurse -Force }
|
shell: powershell
|
||||||
|
run: |
|
||||||
|
$version = "${{ steps.get_version.outputs.VERSION }}"
|
||||||
|
|
||||||
- name: Package artifacts
|
Write-Host "Preparing ZIP artifacts for version: $version"
|
||||||
run: |
|
New-Item -ItemType Directory -Path "artifacts" -Force
|
||||||
$version = "$env:VERSION"
|
|
||||||
New-Item -ItemType Directory -Path "artifacts" -Force | Out-Null
|
|
||||||
|
|
||||||
$std = "artifacts\YTSage-v$version-portable.zip"
|
# List dist directories
|
||||||
if (Test-Path "dist\YTSage") {
|
Write-Host "Files in dist directory:"
|
||||||
if (Test-Path $std) { Remove-Item $std -Force }
|
if (Test-Path "dist") {
|
||||||
Compress-Archive -Path "dist\YTSage\*" -DestinationPath $std -CompressionLevel Optimal
|
Get-ChildItem -Path "dist" -Recurse | ForEach-Object { Write-Host " $($_.FullName)" }
|
||||||
|
} else {
|
||||||
|
Write-Host " No dist directory found!"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Create Standard ZIP
|
||||||
|
$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"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Create FFmpeg ZIP
|
||||||
|
$ffmpegDist = Join-Path (Get-Location) "dist\YTSage-FFmpeg"
|
||||||
|
if (Test-Path $ffmpegDist) {
|
||||||
|
$ffZip = "artifacts\YTSage-v$version-ffmpeg-portable.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
|
||||||
|
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
|
||||||
|
prerelease: false
|
||||||
|
body: |
|
||||||
|
# YTSage ${{ github.ref_name }}
|
||||||
|
|
||||||
Write-Host "Artifacts:"
|
**Release Date**: ${{ github.event.head_commit.timestamp }}
|
||||||
Get-ChildItem artifacts | ForEach-Object { Write-Host " - $($_.Name)" }
|
|
||||||
|
|
||||||
- name: Create draft release
|
## Downloads
|
||||||
uses: softprops/action-gh-release@v2
|
|
||||||
with:
|
|
||||||
tag_name: ${{ github.ref_name }}
|
|
||||||
name: YTSage ${{ github.ref_name }}
|
|
||||||
draft: true
|
|
||||||
prerelease: false
|
|
||||||
body: |
|
|
||||||
## Downloads
|
|
||||||
|
|
||||||
- YTSage-v${{ steps.version.outputs.VERSION }}-portable.zip (portable)
|
### Standard Version (ZIP)
|
||||||
- YTSage-v${{ steps.version.outputs.VERSION }}-ffmpeg-portable.zip (portable with FFmpeg included)
|
- **YTSage-v${{ steps.get_version.outputs.VERSION }}-portable.zip** – Portable ZIP (downloads FFmpeg on first use if needed)
|
||||||
|
|
||||||
Extract and run the EXE. Console window is hidden.
|
### FFmpeg Bundle Version (ZIP)
|
||||||
files: |
|
- **YTSage-v${{ steps.get_version.outputs.VERSION }}-ffmpeg-portable.zip** – Portable ZIP with FFmpeg included
|
||||||
artifacts/*
|
|
||||||
|
## Installation / Usage
|
||||||
|
|
||||||
|
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)
|
||||||
|
- 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 }}
|
||||||
Reference in New Issue
Block a user