Refactor Windows build workflow for YTSage
Simplifies and modernizes the GitHub Actions workflow for building Windows releases. Updates Python version, streamlines steps for dependency installation, build, FFmpeg bundling, artifact packaging, and release creation. Uses more concise PowerShell scripts and updates action versions for improved maintainability.
This commit is contained in:
@@ -9,73 +9,53 @@ permissions:
|
|||||||
contents: write
|
contents: write
|
||||||
|
|
||||||
env:
|
env:
|
||||||
PYTHON_VERSION: '3.13.6'
|
PYTHON_VERSION: '3.12.10'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build-windows:
|
build-windows:
|
||||||
runs-on: windows-latest
|
runs-on: windows-latest
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
shell: pwsh
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: Extract version from tag
|
- name: Derive version from tag
|
||||||
id: get_version
|
id: version
|
||||||
shell: powershell
|
|
||||||
run: |
|
run: |
|
||||||
$tag = "${{ github.ref_name }}"
|
$tag = "${{ github.ref_name }}"
|
||||||
$version = $tag -replace '^v', ''
|
$ver = $tag -replace '^v',''
|
||||||
Write-Host "Extracted version: $version"
|
echo "VERSION=$ver" >> $env:GITHUB_OUTPUT
|
||||||
echo "VERSION=$version" >> $env:GITHUB_OUTPUT
|
echo "VERSION=$ver" >> $env:GITHUB_ENV
|
||||||
|
|
||||||
- name: Setup Python
|
- name: Setup Python ${{ env.PYTHON_VERSION }}
|
||||||
uses: actions/setup-python@v4
|
uses: actions/setup-python@v5
|
||||||
with:
|
with:
|
||||||
python-version: ${{ env.PYTHON_VERSION }}
|
python-version: ${{ env.PYTHON_VERSION }}
|
||||||
|
|
||||||
- name: Cache Python dependencies
|
- name: Create venv and install dependencies
|
||||||
uses: actions/cache@v3
|
|
||||||
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: |
|
run: |
|
||||||
python -m venv venv
|
$py = $env:pythonLocation
|
||||||
|
& "$py\python.exe" -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
|
pip install --no-cache-dir cx_Freeze yt-dlp
|
||||||
pip install --no-cache-dir yt-dlp
|
|
||||||
|
|
||||||
- name: Prepare build variables
|
- name: Build Standard (portable ZIP)
|
||||||
shell: powershell
|
|
||||||
run: |
|
|
||||||
$version = "${{ steps.get_version.outputs.VERSION }}"
|
|
||||||
echo "VERSION=$version" >> $env:GITHUB_ENV
|
|
||||||
Write-Host "Prepared build variables for version: $version"
|
|
||||||
|
|
||||||
- name: Build Standard Version (ZIP)
|
|
||||||
shell: powershell
|
|
||||||
run: |
|
run: |
|
||||||
.\venv\Scripts\Activate.ps1
|
.\venv\Scripts\Activate.ps1
|
||||||
$version = "$env:VERSION"
|
$version = "$env:VERSION"
|
||||||
Write-Host "Building Standard Version v$version ..."
|
Write-Host "Building Standard v$version"
|
||||||
|
|
||||||
# Clean previous build artifacts
|
if (Test-Path "build") { Remove-Item "build" -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 }
|
if (Test-Path "dist") { Remove-Item "dist" -Recurse -Force }
|
||||||
|
|
||||||
# Build executable using cx_Freeze CLI
|
.\venv\Scripts\python.exe -m cx_Freeze.cli cxfreeze main.py `
|
||||||
cxfreeze main.py `
|
|
||||||
--target-dir "dist\YTSage" `
|
--target-dir "dist\YTSage" `
|
||||||
--base-name Win32GUI `
|
--base-name Win32GUI `
|
||||||
--icon "assets\branding\icons\YTSage.ico" `
|
--icon "assets\branding\icons\YTSage.ico" `
|
||||||
@@ -86,76 +66,30 @@ jobs:
|
|||||||
--include-files "src" `
|
--include-files "src" `
|
||||||
--include-files "assets"
|
--include-files "assets"
|
||||||
|
|
||||||
# Exclude screenshots folder from Standard build output
|
|
||||||
$stdScreenshots = "dist\YTSage\assets\branding\screenshots"
|
$stdScreenshots = "dist\YTSage\assets\branding\screenshots"
|
||||||
if (Test-Path $stdScreenshots) {
|
if (Test-Path $stdScreenshots) { Remove-Item $stdScreenshots -Recurse -Force }
|
||||||
Remove-Item $stdScreenshots -Recurse -Force
|
|
||||||
Write-Host "Removed screenshots from Standard build: $stdScreenshots"
|
|
||||||
}
|
|
||||||
|
|
||||||
- name: Setup FFmpeg for bundle
|
- name: Download FFmpeg bundle
|
||||||
shell: powershell
|
|
||||||
run: |
|
run: |
|
||||||
Write-Host "Setting up FFmpeg for bundled version..."
|
New-Item -ItemType Directory -Path "ffmpeg-temp" -Force | Out-Null
|
||||||
|
|
||||||
# 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"
|
$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"
|
Invoke-WebRequest -Uri $ffmpegUrl -OutFile "ffmpeg-temp\ffmpeg.zip"
|
||||||
|
|
||||||
# Extract FFmpeg
|
|
||||||
Expand-Archive -Path "ffmpeg-temp\ffmpeg.zip" -DestinationPath "ffmpeg-temp" -Force
|
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
|
$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
|
||||||
|
|
||||||
if ($ffmpegDir) {
|
- name: Build FFmpeg bundle (portable ZIP)
|
||||||
# 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: |
|
run: |
|
||||||
.\venv\Scripts\Activate.ps1
|
.\venv\Scripts\Activate.ps1
|
||||||
$version = "$env:VERSION"
|
$version = "$env:VERSION"
|
||||||
Write-Host "Building FFmpeg Version v$version ..."
|
Write-Host "Building FFmpeg bundle v$version"
|
||||||
|
|
||||||
# Clean previous build artifacts
|
|
||||||
if (Test-Path "build\exe.*") { Remove-Item "build\exe.*" -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 "build\bdist.*") { Remove-Item "build\bdist.*" -Recurse -Force }
|
||||||
|
|
||||||
# Build executable with FFmpeg using cx_Freeze CLI
|
.\venv\Scripts\python.exe -m cx_Freeze.cli cxfreeze main.py `
|
||||||
cxfreeze main.py `
|
|
||||||
--target-dir "dist\YTSage-FFmpeg" `
|
--target-dir "dist\YTSage-FFmpeg" `
|
||||||
--base-name Win32GUI `
|
--base-name Win32GUI `
|
||||||
--icon "assets\branding\icons\YTSage.ico" `
|
--icon "assets\branding\icons\YTSage.ico" `
|
||||||
@@ -166,124 +100,50 @@ jobs:
|
|||||||
--include-files "src" `
|
--include-files "src" `
|
||||||
--include-files "assets"
|
--include-files "assets"
|
||||||
|
|
||||||
# Copy FFmpeg binaries into dist folder post-build (more reliable than CLI include)
|
|
||||||
if ($env:FFMPEG_PATH) {
|
if ($env:FFMPEG_PATH) {
|
||||||
$destDir = "dist\YTSage-FFmpeg"
|
$dest = "dist\YTSage-FFmpeg"
|
||||||
$ffmpegExe = Join-Path $env:FFMPEG_PATH "ffmpeg.exe"
|
foreach ($n in 'ffmpeg.exe','ffprobe.exe','ffplay.exe') {
|
||||||
$ffprobeExe = Join-Path $env:FFMPEG_PATH "ffprobe.exe"
|
$src = Join-Path $env:FFMPEG_PATH $n
|
||||||
$ffplayExe = Join-Path $env:FFMPEG_PATH "ffplay.exe"
|
if (Test-Path $src) { Copy-Item $src -Destination (Join-Path $dest $n) -Force }
|
||||||
|
|
||||||
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"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
# Exclude screenshots folder from FFmpeg build output
|
$ffShots = "dist\YTSage-FFmpeg\assets\branding\screenshots"
|
||||||
$ffScreenshots = "dist\YTSage-FFmpeg\assets\branding\screenshots"
|
if (Test-Path $ffShots) { Remove-Item $ffShots -Recurse -Force }
|
||||||
if (Test-Path $ffScreenshots) {
|
|
||||||
Remove-Item $ffScreenshots -Recurse -Force
|
|
||||||
Write-Host "Removed screenshots from FFmpeg build: $ffScreenshots"
|
|
||||||
}
|
|
||||||
|
|
||||||
- name: Package and prepare release artifacts (ZIPs)
|
- name: Package artifacts
|
||||||
shell: powershell
|
|
||||||
run: |
|
run: |
|
||||||
$version = "${{ steps.get_version.outputs.VERSION }}"
|
$version = "$env:VERSION"
|
||||||
|
New-Item -ItemType Directory -Path "artifacts" -Force | Out-Null
|
||||||
|
|
||||||
Write-Host "Preparing ZIP artifacts for version: $version"
|
$std = "artifacts\YTSage-v$version-portable.zip"
|
||||||
New-Item -ItemType Directory -Path "artifacts" -Force
|
if (Test-Path "dist\YTSage") {
|
||||||
|
if (Test-Path $std) { Remove-Item $std -Force }
|
||||||
# List dist directories
|
Compress-Archive -Path "dist\YTSage\*" -DestinationPath $std -CompressionLevel Optimal
|
||||||
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 (portable)
|
$ff = "artifacts\YTSage-v$version-ffmpeg-portable.zip"
|
||||||
$standardDist = Join-Path (Get-Location) "dist\YTSage"
|
if (Test-Path "dist\YTSage-FFmpeg") {
|
||||||
if (Test-Path $standardDist) {
|
if (Test-Path $ff) { Remove-Item $ff -Force }
|
||||||
$stdZip = "artifacts\YTSage-v$version-portable.zip"
|
Compress-Archive -Path "dist\YTSage-FFmpeg\*" -DestinationPath $ff -CompressionLevel Optimal
|
||||||
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 (portable)
|
Write-Host "Artifacts:"
|
||||||
$ffmpegDist = Join-Path (Get-Location) "dist\YTSage-FFmpeg"
|
Get-ChildItem artifacts | ForEach-Object { Write-Host " - $($_.Name)" }
|
||||||
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
|
- name: Create draft release
|
||||||
uses: softprops/action-gh-release@v1
|
uses: softprops/action-gh-release@v2
|
||||||
with:
|
with:
|
||||||
tag_name: ${{ github.ref_name }}
|
tag_name: ${{ github.ref_name }}
|
||||||
name: YTSage ${{ github.ref_name }}
|
name: YTSage ${{ github.ref_name }}
|
||||||
draft: true
|
draft: true
|
||||||
prerelease: false
|
prerelease: false
|
||||||
body: |
|
body: |
|
||||||
# YTSage ${{ github.ref_name }}
|
|
||||||
|
|
||||||
**Release Date**: ${{ github.event.head_commit.timestamp }}
|
|
||||||
|
|
||||||
## Downloads
|
## Downloads
|
||||||
|
|
||||||
### Standard Version (ZIP)
|
- YTSage-v${{ steps.version.outputs.VERSION }}-portable.zip (portable)
|
||||||
- **YTSage-v${{ steps.get_version.outputs.VERSION }}-portable.zip** – Portable ZIP (downloads FFmpeg on first use if needed)
|
- YTSage-v${{ steps.version.outputs.VERSION }}-ffmpeg-portable.zip (portable with FFmpeg included)
|
||||||
|
|
||||||
### FFmpeg Bundle Version (ZIP)
|
Extract and run the EXE. Console window is hidden.
|
||||||
- **YTSage-v${{ steps.get_version.outputs.VERSION }}-ffmpeg-portable.zip** – Portable ZIP with FFmpeg included
|
|
||||||
|
|
||||||
## 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: |
|
files: |
|
||||||
artifacts/*
|
artifacts/*
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
Reference in New Issue
Block a user