Files
SageTube/.github/workflows/build-windows.yml
T
Your Name e28cf90a5f Update Python version to 3.13 in CI workflows
Changed the PYTHON_VERSION from 3.13.6 to 3.13 in the Linux, macOS, and Windows GitHub Actions workflows to use the latest 3.13.x release.
2025-10-06 16:20:55 +03:00

318 lines
14 KiB
YAML

name: Build Windows Release
on:
push:
tags:
- 'v*'
permissions:
contents: write
env:
PYTHON_VERSION: '3.13'
jobs:
build-windows:
runs-on: windows-latest
steps:
- name: Checkout code
uses: actions/checkout@v5
with:
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: Setup Python
uses: actions/setup-python@v6
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: Cache Python dependencies
uses: actions/cache@v4
with:
path: |
venv
~\AppData\Local\pip\Cache
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
restore-keys: |
${{ runner.os }}-pip-
- name: Create virtual environment and install dependencies
shell: powershell
run: |
python -m venv venv
.\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
- name: Prepare build variables
shell: powershell
run: |
$version = "${{ steps.get_version.outputs.VERSION }}"
echo "VERSION=$version" >> $env:GITHUB_ENV
Write-Host "Prepared build variables for version: $version"
- name: Create cx_Freeze setup script
shell: powershell
run: |
# Create setup script for cx_Freeze
New-Item -Path "setup_cxfreeze.py" -ItemType File -Force
Add-Content -Path "setup_cxfreeze.py" -Value "import os"
Add-Content -Path "setup_cxfreeze.py" -Value "from cx_Freeze import setup, Executable"
Add-Content -Path "setup_cxfreeze.py" -Value ""
Add-Content -Path "setup_cxfreeze.py" -Value 'version = os.environ.get("VERSION", "0.0.0")'
Add-Content -Path "setup_cxfreeze.py" -Value ""
Add-Content -Path "setup_cxfreeze.py" -Value "build_exe_options = dict("
Add-Content -Path "setup_cxfreeze.py" -Value " optimize=2,"
Add-Content -Path "setup_cxfreeze.py" -Value " packages=["
Add-Content -Path "setup_cxfreeze.py" -Value ' "PySide6.QtCore",'
Add-Content -Path "setup_cxfreeze.py" -Value ' "PySide6.QtGui",'
Add-Content -Path "setup_cxfreeze.py" -Value ' "PySide6.QtWidgets",'
Add-Content -Path "setup_cxfreeze.py" -Value ' "requests",'
Add-Content -Path "setup_cxfreeze.py" -Value ' "PIL",'
Add-Content -Path "setup_cxfreeze.py" -Value ' "packaging",'
Add-Content -Path "setup_cxfreeze.py" -Value ' "markdown",'
Add-Content -Path "setup_cxfreeze.py" -Value ' "pyglet",'
Add-Content -Path "setup_cxfreeze.py" -Value ' "loguru",'
Add-Content -Path "setup_cxfreeze.py" -Value ' "setuptools",'
Add-Content -Path "setup_cxfreeze.py" -Value " ],"
Add-Content -Path "setup_cxfreeze.py" -Value " excludes=["
Add-Content -Path "setup_cxfreeze.py" -Value ' "PySide6.QtBluetooth",'
Add-Content -Path "setup_cxfreeze.py" -Value ' "PySide6.QtNetwork",'
Add-Content -Path "setup_cxfreeze.py" -Value ' "PySide6.QtOpenGL",'
Add-Content -Path "setup_cxfreeze.py" -Value ' "PySide6.QtPrintSupport",'
Add-Content -Path "setup_cxfreeze.py" -Value ' "PySide6.QtSvg",'
Add-Content -Path "setup_cxfreeze.py" -Value ' "PySide6.QtTest",'
Add-Content -Path "setup_cxfreeze.py" -Value ' "PySide6.QtXml",'
Add-Content -Path "setup_cxfreeze.py" -Value ' "PySide6.QtSql",'
Add-Content -Path "setup_cxfreeze.py" -Value ' "PySide6.QtHelp",'
Add-Content -Path "setup_cxfreeze.py" -Value ' "PySide6.QtMultimedia",'
Add-Content -Path "setup_cxfreeze.py" -Value ' "PySide6.QtQml",'
Add-Content -Path "setup_cxfreeze.py" -Value ' "PySide6.QtQuick",'
Add-Content -Path "setup_cxfreeze.py" -Value ' "PySide6.QtWebEngineCore",'
Add-Content -Path "setup_cxfreeze.py" -Value ' "PIL.ImageDraw",'
Add-Content -Path "setup_cxfreeze.py" -Value ' "PIL.ImageFont",'
Add-Content -Path "setup_cxfreeze.py" -Value ' "numpy",'
Add-Content -Path "setup_cxfreeze.py" -Value ' "scipy",'
Add-Content -Path "setup_cxfreeze.py" -Value ' "wx",'
Add-Content -Path "setup_cxfreeze.py" -Value ' "pandas",'
Add-Content -Path "setup_cxfreeze.py" -Value ' "tkinter",'
Add-Content -Path "setup_cxfreeze.py" -Value ' "yt_dlp",'
Add-Content -Path "setup_cxfreeze.py" -Value ' "unittest",'
Add-Content -Path "setup_cxfreeze.py" -Value ' "test",'
Add-Content -Path "setup_cxfreeze.py" -Value ' "tests",'
Add-Content -Path "setup_cxfreeze.py" -Value " ],"
Add-Content -Path "setup_cxfreeze.py" -Value " include_files=["
Add-Content -Path "setup_cxfreeze.py" -Value ' ("src", "src"),'
Add-Content -Path "setup_cxfreeze.py" -Value ' ("assets", "lib/assets"),'
Add-Content -Path "setup_cxfreeze.py" -Value " ],"
Add-Content -Path "setup_cxfreeze.py" -Value ")"
Add-Content -Path "setup_cxfreeze.py" -Value ""
Add-Content -Path "setup_cxfreeze.py" -Value "executables = ["
Add-Content -Path "setup_cxfreeze.py" -Value " Executable("
Add-Content -Path "setup_cxfreeze.py" -Value ' script="main.py",'
Add-Content -Path "setup_cxfreeze.py" -Value ' target_name=f"YTSage-v{version}.exe",'
Add-Content -Path "setup_cxfreeze.py" -Value ' base="Win32GUI",'
Add-Content -Path "setup_cxfreeze.py" -Value ' icon="assets/branding/icons/YTSage.ico",'
Add-Content -Path "setup_cxfreeze.py" -Value " )"
Add-Content -Path "setup_cxfreeze.py" -Value "]"
Add-Content -Path "setup_cxfreeze.py" -Value ""
Add-Content -Path "setup_cxfreeze.py" -Value "setup("
Add-Content -Path "setup_cxfreeze.py" -Value ' name="YTSage",'
Add-Content -Path "setup_cxfreeze.py" -Value " version=version,"
Add-Content -Path "setup_cxfreeze.py" -Value ' description="YTSage",'
Add-Content -Path "setup_cxfreeze.py" -Value ' options={"build_exe": build_exe_options},'
Add-Content -Path "setup_cxfreeze.py" -Value " executables=executables,"
Add-Content -Path "setup_cxfreeze.py" -Value ")"
- 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 setup script
python setup_cxfreeze.py build_exe --build-exe "dist\YTSage"
# Remove screenshots folder to reduce build size
if (Test-Path "dist\YTSage\lib\assets\branding\screenshots") {
Remove-Item "dist\YTSage\lib\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 }
# Create modified setup script for FFmpeg version
(Get-Content "setup_cxfreeze.py") -replace 'YTSage-v\{version\}\.exe', 'YTSage-v{version}-ffmpeg.exe' | Set-Content "setup_cxfreeze_ffmpeg.py"
# Build executable with FFmpeg using setup script
python setup_cxfreeze_ffmpeg.py build_exe --build-exe "dist\YTSage-FFmpeg"
# Remove screenshots folder to reduce build size
if (Test-Path "dist\YTSage-FFmpeg\lib\assets\branding\screenshots") {
Remove-Item "dist\YTSage-FFmpeg\lib\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"
}
}
}
- 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
}
# 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!"
}
- name: Create draft release
uses: softprops/action-gh-release@v1
with:
tag_name: ${{ github.ref_name }}
name: YTSage ${{ github.ref_name }}
draft: true
prerelease: false
body: |
# YTSage ${{ github.ref_name }}
**Release Date**: ${{ github.event.head_commit.timestamp }}
**Full Changelog**: https://github.com/${{ github.repository }}/compare/${{ github.ref_name }}
files: |
artifacts/*
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}