cleanup #417
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
name: Compile and Test PyKotor | |
concurrency: | |
group: ${{ github.workflow }}-${{ github.ref }} | |
cancel-in-progress: true | |
env: | |
OS_RUNNERS_JSON: '["windows-2019", "ubuntu-20.04", "macos-12"]' | |
PYTHON_VERSIONS_JSON: '["3.8", "3.9", "3.10", "3.11", "3.12"]' | |
ARCHITECTURES_JSON: '["x86", "x64"]' | |
UPX_VERSION: '4.2.2' | |
on: | |
- push | |
- workflow_dispatch | |
permissions: | |
contents: write | |
jobs: | |
build: | |
runs-on: ${{ matrix.os }} | |
strategy: | |
fail-fast: false # Disable automatic cancellation of other jobs | |
matrix: | |
os: [windows-2019, ubuntu-20.04, macos-12] # make sure you update the above one too. | |
python-version: ['3.8', '3.9', '3.10', '3.11', '3.12'] | |
architecture: ['x86', 'x64'] | |
include: | |
- arch: x86 | |
vc_redist2015: "https://download.microsoft.com/download/9/3/F/93FCF1E7-E6A4-478B-96E7-D4B285925B00/vc_redist.x86.exe" | |
vc_redist-latest: "https://aka.ms/vs/17/release/vc_redist.x86.exe" | |
vc_redist2019: "https://aka.ms/vs/17/release/vc_redist.x86.exe" | |
- arch: x64 | |
vc_redist2015: "https://download.microsoft.com/download/9/3/F/93FCF1E7-E6A4-478B-96E7-D4B285925B00/vc_redist.x64.exe" | |
vc_redist-latest: "https://aka.ms/vs/17/release/vc_redist.x64.exe" | |
vc_redist2019: "https://aka.ms/vs/17/release/vc_redist.x64.exe" | |
exclude: | |
# unix x86 is definitely not supported. | |
- os: ubuntu-20.04 | |
architecture: x86 | |
- os: macos-12 | |
architecture: x86 | |
outputs: | |
matrix-os: ${{ toJson(matrix.os) }} | |
matrix-python-version: ${{ toJson(matrix.python-version) }} | |
steps: | |
- uses: actions/checkout@v4 | |
- name: Set up Python ${{ matrix.python-version }} | |
if: ${{ runner.os != 'macOS' && runner.os != 'Linux' }} | |
uses: actions/setup-python@v5 | |
with: | |
python-version: ${{ matrix.python-version }} | |
architecture: ${{ matrix.architecture }} | |
- name: Set up Python ${{ matrix.python-version }} macOS | |
if: ${{ runner.os == 'macOS' }} | |
run: | # warning: `brew link --overwrite python@ver` is unsafe on any non-virtualized macos. | |
echo "NONINTERACTIVE DEFAULT: $NONINTERACTIVE" | |
export NONINTERACTIVE=1 | |
echo "NONINTERACTIVE NEW: $NONINTERACTIVE" | |
brew analytics on | |
brew update | |
brew install python@${{ matrix.python-version }} || brew link --overwrite python@${{ matrix.python-version }} | |
- name: Set up Python ${{ matrix.python-version }} Linux # macos too? | |
if: ${{ runner.os == 'Linux' }} | |
run: | | |
sudo apt-get update | |
sudo apt-get install build-essential zlib1g-dev libncurses5-dev libgdbm-dev libssl-dev libreadline-dev libffi-dev libsqlite3-dev libbz2-dev tk-dev -y | |
$pyVersion = switch ("${{ matrix.python-version }}") { | |
"3.7" { "3.7.17" } | |
"3.8" { "3.8.18" } | |
"3.9" { "3.9.18" } | |
"3.10" { "3.10.13" } | |
"3.11" { "3.11.8" } | |
"3.12" { "3.12.2" } | |
} | |
Write-Output "Downloading python '$pyVersion'..." | |
Invoke-WebRequest -Uri https://www.python.org/ftp/python/$pyVersion/Python-$pyVersion.tgz -OutFile Python-$pyVersion.tgz | |
tar -xvf Python-$pyVersion.tgz | |
$current_working_dir = (Get-Location).Path | |
Set-Location -LiteralPath "Python-$pyVersion" -ErrorAction Stop | |
$env:LDFLAGS="-Wl,-rpath=/usr/local/lib" | |
sudo ./configure --enable-optimizations --with-ensurepip=install --enable-shared --disable-new-dtags | |
sudo make -j $(nproc) | |
sudo make altinstall | |
Set-Location -LiteralPath $current_working_dir | |
shell: pwsh | |
- name: Reset APT sources to default | |
if: ${{ runner.os == 'Linux' }} | |
run: | | |
echo "Resetting APT sources to default Ubuntu repositories" | |
sudo rm /etc/apt/sources.list | |
echo "deb http://archive.ubuntu.com/ubuntu $(lsb_release -cs) main restricted universe multiverse" | sudo tee -a /etc/apt/sources.list | |
echo "deb http://archive.ubuntu.com/ubuntu $(lsb_release -cs)-updates main restricted universe multiverse" | sudo tee -a /etc/apt/sources.list | |
echo "deb http://archive.ubuntu.com/ubuntu $(lsb_release -cs)-backports main restricted universe multiverse" | sudo tee -a /etc/apt/sources.list | |
echo "deb http://security.ubuntu.com/ubuntu $(lsb_release -cs)-security main restricted universe multiverse" | sudo tee -a /etc/apt/sources.list | |
sudo apt-get update -y | |
- name: Setup python venvs | |
run: | # create the venv early to work around an issue with the matrix runners' concurrency | |
$pythonExeName = "python" | |
if ("${{ runner.os }}" -ne "Windows") | |
{ | |
$pythonExeName = "python${{ matrix.python-version }}" | |
# LD_LIBRARY_PATH must be updated. However this won't be permanent, just long enough to create the venv. | |
$env:LD_LIBRARY_PATH = "/usr/local/lib:$env:LD_LIBRARY_PATH" | |
} | |
& $pythonExeName -m venv .venv_${{ matrix.os }}_${{ matrix.python-version }}_${{ matrix.architecture }} | |
& $pythonExeName -m venv .venv_holopatcher_${{ matrix.os }}_${{ matrix.python-version }}_${{ matrix.architecture }} | |
& $pythonExeName -m venv .venv_toolset_${{ matrix.os }}_${{ matrix.python-version }}_${{ matrix.architecture }} | |
& $pythonExeName -m venv .venv_guiduplicator_${{ matrix.os }}_${{ matrix.python-version }}_${{ matrix.architecture }} | |
& $pythonExeName -m venv .venv_kotordiff_${{ matrix.os }}_${{ matrix.python-version }}_${{ matrix.architecture }} | |
& $pythonExeName -m venv .venv_batchpatcher_${{ matrix.os }}_${{ matrix.python-version }}_${{ matrix.architecture }} | |
shell: pwsh | |
- name: Set UPX download URL | |
# upx docs express that crashes are happening on ventura and above with upx, don't use on mac. | |
if: runner.os != 'macOS' | |
id: upx_setup | |
run: | | |
$build = "no" | |
$archiveName = "" | |
if ("${{ runner.os }}" -eq "Windows") { | |
if ("${{ matrix.architecture }}" -eq "x86") { | |
$archiveName = "upx-${{ env.UPX_VERSION }}-win32.zip" | |
} else { | |
$archiveName = "upx-${{ env.UPX_VERSION }}-win64.zip" | |
} | |
} elseif ("${{ runner.os }}" -eq "Linux") { | |
$archiveName = "upx-${{ env.UPX_VERSION }}-amd64_linux.tar.xz" | |
} elseif ("${{ runner.os }}" -eq "macOS") { | |
$build = "yes" | |
$archiveName = "upx-${{ env.UPX_VERSION }}-src.tar.xz" | |
} | |
$url = "https://github.com/upx/upx/releases/download/v${{ env.UPX_VERSION }}/$archiveName" | |
# Write to the GITHUB_OUTPUT environment file | |
Add-Content -Path $env:GITHUB_OUTPUT -Value "build=$build" | |
Add-Content -Path $env:GITHUB_OUTPUT -Value "url=$url" | |
Add-Content -Path $env:GITHUB_OUTPUT -Value "archiveName=$archiveName" | |
shell: pwsh | |
- name: Download and prepare UPX | |
if: runner.os != 'macOS' | |
run: | | |
$ext = "${{ runner.os }}" -eq "Windows" ? "zip" : "tar.xz" | |
$url = "${{ steps.upx_setup.outputs.url }}" | |
$archiveName = "${{ steps.upx_setup.outputs.archiveName }}" | |
$outputPath = "upx-dir" | |
# Use Invoke-WebRequest or curl depending on the OS | |
if ("${{ runner.os }}" -eq "Windows") { | |
Invoke-WebRequest -Uri $url -OutFile $archiveName | |
} elseif ("${{ runner.os }}" -eq "Linux") { | |
curl -L $url -o $archiveName | |
} | |
New-Item -ItemType Directory -Force -Path "upx-dir" -ErrorAction SilentlyContinue | |
if ("${{ runner.os }}" -ne "macOS") { | |
if ($ext -eq "zip") { | |
$fileNameWithoutExtension = [System.IO.Path]::GetFileNameWithoutExtension($archiveName) | |
Expand-Archive -Path $archiveName -DestinationPath "temp_folder_upx" | |
# Ensure upx-dir exists; create it if it doesn't | |
if (-not (Test-Path -Path "upx-dir")) { | |
New-Item -ItemType Directory -Path "upx-dir" | |
} | |
Get-ChildItem -Path "temp_folder_upx/$fileNameWithoutExtension" -Recurse | Move-Item -Destination "upx-dir" | |
Remove-Item "temp_folder_upx" -Recurse -Force -ErrorAction SilentlyContinue | |
} else { | |
tar -xvf $archiveName --strip-components=1 -C "upx-dir" | |
} | |
Remove-Item $archiveName # Clean up downloaded archive | |
} | |
shell: pwsh | |
- name: Set UPX directory path | |
if: runner.os != 'macOS' | |
id: upx_dir | |
run: | | |
$upx_dir = "./upx-dir" | |
$upx_dir = $([System.IO.Path]::GetFullPath('./upx-dir')) | |
Dir -Recurse $upx_dir | Get-Childitem | |
echo "UPX_DIR=$upx_dir" | Out-File -FilePath $env:GITHUB_ENV -Append | |
Write-Output "UPX_DIR set to '$upx_dir'" | |
shell: pwsh | |
- name: Install Visual Studio 2015 C++ Redistributable | |
if: runner.os == 'Windows' | |
run: | | |
$url = "${{ matrix.vc_redist2015 }}" | |
$output = "vc_redist.exe" | |
Invoke-WebRequest -Uri $url -OutFile $output | |
Start-Process $output -ArgumentList '/install', '/quiet', '/norestart' -Wait | |
Remove-Item -Path $output | |
#choco install vcredist2015 -y | |
shell: pwsh | |
- name: Install Visual Studio 2019 C++ Redistributable | |
if: runner.os == 'Windows' | |
run: | | |
$url = "${{ matrix.vc_redist2019 }}" | |
$output = "vc_redist.exe" | |
Invoke-WebRequest -Uri $url -OutFile $output | |
Start-Process $output -ArgumentList '/install', '/quiet', '/norestart' -Wait | |
Remove-Item -Path $output | |
#choco install vcredist2019 -y | |
shell: pwsh | |
- name: Install Visual Studio latest C++ Redistributable | |
if: runner.os == 'Windows' | |
run: | | |
$url = "${{ matrix.vc_redist-latest }}" | |
$output = "vc_redist.exe" | |
Invoke-WebRequest -Uri $url -OutFile $output | |
Start-Process $output -ArgumentList '/install', '/quiet', '/norestart' -Wait | |
Remove-Item -Path $output | |
shell: pwsh | |
- name: Install Holocron Toolset dependencies | |
if: ${{ success() || failure() }} | |
id: toolset_deps | |
run: | # known pyinstaller versions that work with upx compressions, not all are available on all python versions. | |
try { | |
./install_python_venv.ps1 -noprompt -venv_name .venv_toolset_${{ matrix.os }}_${{ matrix.python-version }}_${{ matrix.architecture }} | |
python -m pip install --upgrade pip | |
if ("${{ runner.os }}" -eq "Windows") { | |
if ("${{ matrix.python-version }}" -eq "3.12") { | |
pip install pyinstaller --prefer-binary -U | |
} elseif ("${{ matrix.python-version }}" -eq "3.11") { | |
pip install "pyinstaller==5.13.2" --prefer-binary | |
} else { | |
pip install "pyinstaller==5.4" --prefer-binary | |
} | |
} else { | |
pip install pyinstaller -U --prefer-binary | |
} | |
$output = "" | |
$errorLines = @() | |
./compile/deps_toolset.ps1 -noprompt -venv_name .venv_toolset_${{ matrix.os }}_${{ matrix.python-version }}_${{ matrix.architecture }} 2>&1 | ForEach-Object { | |
Write-Output $_.ToString() | |
$output += $_.ToString() + "`n" | |
if($_ -match 'ERROR:') { | |
$errorLines += $_.ToString() | |
} | |
} | |
if ($errorLines.Count -gt 0) { | |
$errorLines | ForEach-Object { Write-Error $_ } | |
Add-Content -Path $env:GITHUB_OUTPUT -Value "success=false" | |
exit 1 | |
} else { | |
Add-Content -Path $env:GITHUB_OUTPUT -Value "success=true" | |
} | |
} catch { | |
Add-Content -Path $env:GITHUB_OUTPUT -Value "success=false" | |
exit 1 | |
} | |
shell: pwsh | |
- name: HolocronToolset - Adjust RPATH for shared libraries in the virtual environment | |
if: ${{ (success() || failure()) && runner.os == 'Disabled_Linux' && steps.toolset_deps.outputs.success == 'true' }} | |
run: | | |
./install_python_venv.ps1 -noprompt -venv_name .venv_toolset_${{ matrix.os }}_${{ matrix.python-version }}_${{ matrix.architecture }} | |
# Ensure patchelf is installed | |
sudo apt-get update && sudo apt-get install -y patchelf | |
# Find the virtual environment's site-packages directory | |
$venvPath = $env:VIRTUAL_ENV | |
Write-Host "Virtual environment path: $venvPath" | |
# Dynamically find the Python version directory inside the venv | |
$pythonLibPath = Get-ChildItem -Path "$venvPath/lib" | Where-Object { $_.PSIsContainer } | Select-Object -First 1 | |
$sitePackagesPath = Join-Path -Path $pythonLibPath.FullName -ChildPath "site-packages" | |
Write-Host "Site-packages path: $sitePackagesPath" | |
# Use patchelf to adjust RPATH for all shared libraries in the virtual environment | |
Get-ChildItem -Path $sitePackagesPath -Filter *.so -Recurse | ForEach-Object { | |
$libPath = $_.FullName | |
Write-Host "Patching $libPath" | |
sudo patchelf --set-rpath '$ORIGIN' $libPath | |
} | |
shell: pwsh | |
- name: Compile Holocron Toolset | |
if: ${{ (success() || failure()) && steps.toolset_deps.outputs.success == 'true' }} | |
run: | | |
$upxDir = $env:UPX_DIR | |
Write-Host "Using UPX directory at '$upxDir'" | |
$output = "" | |
./compile/compile_toolset.ps1 -noprompt -venv_name .venv_toolset_${{ matrix.os }}_${{ matrix.python-version }}_${{ matrix.architecture }} -upx_dir $upxDir 2>&1 | ForEach-Object { | |
Write-Output $_.ToString() | |
$output += $_.ToString() + "`n" | |
if($_ -match 'ERROR:') { | |
$errorLines += $_.ToString() | |
} | |
} | |
$warningCount = 0 | |
$output -split "`n" | ForEach-Object { | |
if ($_ -match 'WARNING: Library not found: could not resolve' -or | |
$_ -match 'WARNING: Cannot find ' -or | |
$_ -match 'WARNING: lib not found:' -or | |
$_ -match 'WARNING: Tcl modules directory' -or | |
$_ -match 'WARNING: Failed to upx strip') { | |
$warningCount++ | |
} | |
} | |
if ($errorLines.Count -gt 0) { | |
$errorLines | ForEach-Object { Write-Error $_ } | |
Add-Content -Path $env:GITHUB_OUTPUT -Value "success=false" | |
exit 1 | |
} elseif ($warningCount -ge 3) { | |
Write-Output "Many 'library not found' warnings raised, pyinstaller was probably unsuccessful." | |
Add-Content -Path $env:GITHUB_OUTPUT -Value "success=false" | |
exit 1 | |
} else { | |
Add-Content -Path $env:GITHUB_OUTPUT -Value "success=true" | |
} | |
shell: pwsh | |
env: | |
UPX_DIR: ${{ env.UPX_DIR }} | |
- name: HolocronToolset - Create Static Binary | |
if: ${{ (success() || failure()) && runner.os == 'Disabled_Linux' && steps.toolset_deps.outputs.success == 'true' }} | |
run: | | |
./install_python_venv.ps1 -noprompt -venv_name .venv_toolset_${{ matrix.os }}_${{ matrix.python-version }}_${{ matrix.architecture }} | |
python -m pip install staticx | |
staticx ./dist/HolocronToolset ./dist/HolocronToolset-static | |
# Rename the static binary for clarity and consistency | |
Write-Output "./dist/HolocronToolset-static --> ./dist/HolocronToolset" | |
Move-Item -LiteralPath ./dist/HolocronToolset-static -Destination ./dist/HolocronToolset -Force | |
shell: pwsh | |
- name: Install HoloPatcher dependencies | |
if: ${{ success() || failure() }} | |
id: holopatcher_deps | |
run: | # known pyinstaller versions that work with upx compressions, not all are available on all python versions. | |
try { | |
./install_python_venv.ps1 -noprompt -venv_name .venv_holopatcher_${{ matrix.os }}_${{ matrix.python-version }}_${{ matrix.architecture }} | |
python -m pip install --upgrade pip | |
if ("${{ runner.os }}" -eq "Windows") { | |
if ("${{ matrix.python-version }}" -eq "3.12") { | |
pip install "pyinstaller==5.13.2" --prefer-binary | |
} elseif ("${{ matrix.python-version }}" -eq "3.11") { | |
pip install "pyinstaller==5.13.2" --prefer-binary | |
} else { | |
pip install "pyinstaller==5.4" --prefer-binary | |
} | |
} else { | |
pip install pyinstaller -U --prefer-binary | |
} | |
$output = "" | |
$errorLines = @() | |
./compile/deps_holopatcher.ps1 -noprompt -venv_name .venv_holopatcher_${{ matrix.os }}_${{ matrix.python-version }}_${{ matrix.architecture }} 2>&1 | ForEach-Object { | |
Write-Output $_.ToString() | |
$output += $_.ToString() + "`n" | |
if($_ -match 'ERROR:') { | |
$errorLines += $_.ToString() | |
} | |
} | |
if ($errorLines.Count -gt 0) { | |
$errorLines | ForEach-Object { Write-Error $_ } | |
Add-Content -Path $env:GITHUB_OUTPUT -Value "success=false" | |
exit 1 | |
} else { | |
Add-Content -Path $env:GITHUB_OUTPUT -Value "success=true" | |
} | |
} catch { | |
Add-Content -Path $env:GITHUB_OUTPUT -Value "success=false" | |
exit 1 | |
} | |
shell: pwsh | |
- name: HoloPatcher - Adjust RPATH for shared libraries in the virtual environment | |
if: ${{ (success() || failure()) && runner.os == 'Disabled_Linux' && steps.holopatcher_deps.outputs.success == 'true' }} | |
run: | | |
./install_python_venv.ps1 -noprompt -venv_name .venv_holopatcher_${{ matrix.os }}_${{ matrix.python-version }}_${{ matrix.architecture }} | |
# Ensure patchelf is installed | |
sudo apt-get update && sudo apt-get install -y patchelf | |
# Find the virtual environment's site-packages directory | |
$venvPath = $env:VIRTUAL_ENV | |
Write-Host "Virtual environment path: $venvPath" | |
# Dynamically find the Python version directory inside the venv | |
$pythonLibPath = Get-ChildItem -Path "$venvPath/lib" | Where-Object { $_.PSIsContainer } | Select-Object -First 1 | |
$sitePackagesPath = Join-Path -Path $pythonLibPath.FullName -ChildPath "site-packages" | |
Write-Host "Site-packages path: $sitePackagesPath" | |
# Use patchelf to adjust RPATH for all shared libraries in the virtual environment | |
Get-ChildItem -Path $sitePackagesPath -Filter *.so -Recurse | ForEach-Object { | |
$libPath = $_.FullName | |
Write-Host "Patching $libPath" | |
sudo patchelf --set-rpath '$ORIGIN' $libPath | |
} | |
shell: pwsh | |
- name: Compile HoloPatcher | |
if: ${{ (success() || failure()) && steps.holopatcher_deps.outputs.success == 'true' }} | |
run: | | |
$upxDir = $env:UPX_DIR | |
Write-Host "Using UPX directory at '$upxDir'" | |
$output = "" | |
./compile/compile_holopatcher.ps1 -noprompt -venv_name .venv_holopatcher_${{ matrix.os }}_${{ matrix.python-version }}_${{ matrix.architecture }} -upx_dir $upxDir 2>&1 | ForEach-Object { | |
Write-Output $_.ToString() | |
$output += $_.ToString() + "`n" | |
if($_ -match 'ERROR:') { | |
$errorLines += $_.ToString() | |
} | |
} | |
$warningCount = 0 | |
$output -split "`n" | ForEach-Object { | |
if ($_ -match 'WARNING: Library not found: could not resolve' -or | |
$_ -match 'WARNING: Cannot find ' -or | |
$_ -match 'WARNING: lib not found:' -or | |
$_ -match 'WARNING: Tcl modules directory' -or | |
$_ -match 'WARNING: Failed to upx strip') { | |
$warningCount++ | |
} | |
} | |
if ($errorLines.Count -gt 0) { | |
$errorLines | ForEach-Object { Write-Error $_ } | |
Add-Content -Path $env:GITHUB_OUTPUT -Value "success=false" | |
exit 1 | |
} elseif ($warningCount -ge 3) { | |
Write-Output "Many 'library not found' warnings raised, pyinstaller was probably unsuccessful." | |
Add-Content -Path $env:GITHUB_OUTPUT -Value "success=false" | |
exit 1 | |
} else { | |
Add-Content -Path $env:GITHUB_OUTPUT -Value "success=true" | |
} | |
shell: pwsh | |
- name: HoloPatcher - Create Static Binary | |
if: ${{ (success() || failure()) && runner.os == 'Disabled_Linux' && steps.holopatcher_deps.outputs.success == 'true' }} | |
run: | | |
./install_python_venv.ps1 -noprompt -venv_name .venv_holopatcher_${{ matrix.os }}_${{ matrix.python-version }}_${{ matrix.architecture }} | |
python -m pip install staticx | |
staticx ./dist/HoloPatcher ./dist/HoloPatcher-static | |
# Rename the static binary for clarity and consistency | |
Write-Output "./dist/HoloPatcher-static --> ./dist/HoloPatcher" | |
Move-Item -LiteralPath ./dist/HoloPatcher-static -Destination ./dist/HoloPatcher -Force | |
shell: pwsh | |
- name: Install BatchPatcher dependencies | |
if: ${{ success() || failure() }} | |
id: batchpatcher_deps | |
run: | # known pyinstaller versions that work with upx compressions, not all are available on all python versions. | |
try { | |
./install_python_venv.ps1 -noprompt -venv_name .venv_batchpatcher_${{ matrix.os }}_${{ matrix.python-version }}_${{ matrix.architecture }} | |
python -m pip install --upgrade pip | |
if ("${{ runner.os }}" -eq "Windows") { | |
if ("${{ matrix.python-version }}" -eq "3.12") { | |
pip install pyinstaller --prefer-binary -U | |
} elseif ("${{ matrix.python-version }}" -eq "3.11") { | |
pip install "pyinstaller==5.13.2" --prefer-binary | |
} else { | |
pip install "pyinstaller==5.4" --prefer-binary | |
} | |
} else { | |
pip install pyinstaller -U --prefer-binary | |
} | |
$output = "" | |
$errorLines = @() | |
./compile/deps_batchpatcher.ps1 -noprompt -venv_name .venv_batchpatcher_${{ matrix.os }}_${{ matrix.python-version }}_${{ matrix.architecture }} 2>&1 | ForEach-Object { | |
Write-Output $_.ToString() | |
$output += $_.ToString() + "`n" | |
if($_ -match 'ERROR:') { | |
$errorLines += $_.ToString() | |
} | |
} | |
if ($errorLines.Count -gt 0) { | |
$errorLines | ForEach-Object { Write-Error $_ } | |
Add-Content -Path $env:GITHUB_OUTPUT -Value "success=false" | |
exit 1 | |
} else { | |
Add-Content -Path $env:GITHUB_OUTPUT -Value "success=true" | |
} | |
} catch { | |
Add-Content -Path $env:GITHUB_OUTPUT -Value "success=false" | |
exit 1 | |
} | |
shell: pwsh | |
- name: BatchPatcher - Adjust RPATH for shared libraries in the virtual environment | |
if: ${{ (success() || failure()) && runner.os == 'Disabled_Linux' && steps.batchpatcher_deps.outputs.success == 'true' }} | |
run: | | |
./install_python_venv.ps1 -noprompt -venv_name .venv_batchpatcher_${{ matrix.os }}_${{ matrix.python-version }}_${{ matrix.architecture }} | |
# Ensure patchelf is installed | |
sudo apt-get update && sudo apt-get install -y patchelf | |
# Find the virtual environment's site-packages directory | |
$venvPath = $env:VIRTUAL_ENV | |
Write-Host "Virtual environment path: $venvPath" | |
# Dynamically find the Python version directory inside the venv | |
$pythonLibPath = Get-ChildItem -Path "$venvPath/lib" | Where-Object { $_.PSIsContainer } | Select-Object -First 1 | |
$sitePackagesPath = Join-Path -Path $pythonLibPath.FullName -ChildPath "site-packages" | |
Write-Host "Site-packages path: $sitePackagesPath" | |
# Use patchelf to adjust RPATH for all shared libraries in the virtual environment | |
Get-ChildItem -Path $sitePackagesPath -Filter *.so -Recurse | ForEach-Object { | |
$libPath = $_.FullName | |
Write-Host "Patching $libPath" | |
sudo patchelf --set-rpath '$ORIGIN' $libPath | |
} | |
shell: pwsh | |
- name: Compile BatchPatcher | |
if: ${{ (success() || failure()) && steps.batchpatcher_deps.outputs.success == 'true' }} | |
run: | | |
$upxDir = $env:UPX_DIR | |
Write-Host "Using UPX directory at '$upxDir'" | |
$output = "" | |
./compile/compile_batchpatcher.ps1 -noprompt -venv_name .venv_batchpatcher_${{ matrix.os }}_${{ matrix.python-version }}_${{ matrix.architecture }} -upx_dir $upxDir 2>&1 | ForEach-Object { | |
Write-Output $_.ToString() | |
$output += $_.ToString() + "`n" | |
if($_ -match 'ERROR:') { | |
$errorLines += $_.ToString() | |
} | |
} | |
$warningCount = 0 | |
$output -split "`n" | ForEach-Object { | |
if ($_ -match 'WARNING: Library not found: could not resolve' -or | |
$_ -match 'WARNING: Cannot find ' -or | |
$_ -match 'WARNING: lib not found:' -or | |
$_ -match 'WARNING: Tcl modules directory' -or | |
$_ -match 'WARNING: Failed to upx strip') { | |
$warningCount++ | |
} | |
} | |
if ($errorLines.Count -gt 0) { | |
$errorLines | ForEach-Object { Write-Error $_ } | |
Add-Content -Path $env:GITHUB_OUTPUT -Value "success=false" | |
exit 1 | |
} elseif ($warningCount -ge 3) { | |
Write-Output "Many 'library not found' warnings raised, pyinstaller was probably unsuccessful." | |
Add-Content -Path $env:GITHUB_OUTPUT -Value "success=false" | |
exit 1 | |
} else { | |
Add-Content -Path $env:GITHUB_OUTPUT -Value "success=true" | |
} | |
shell: pwsh | |
- name: BatchPatcher - Create Static Binary | |
if: ${{ (success() || failure()) && runner.os == 'Disabled_Linux' && steps.batchpatcher_deps.outputs.success == 'true' }} | |
run: | | |
./install_python_venv.ps1 -noprompt -venv_name .venv_batchpatcher_${{ matrix.os }}_${{ matrix.python-version }}_${{ matrix.architecture }} | |
python -m pip install staticx | |
staticx ./dist/K_BatchPatcher ./dist/K_BatchPatcher-static | |
# Rename the static binary for clarity and consistency | |
Write-Output "./dist/K_BatchPatcher-static --> ./dist/K_BatchPatcher" | |
Move-Item -LiteralPath ./dist/K_BatchPatcher-static -Destination ./dist/K_BatchPatcher -Force | |
shell: pwsh | |
- name: Compile KotorDiff | |
if: ${{ success() || failure() }} | |
run: | | |
$upxDir = $env:UPX_DIR | |
Write-Host "Using UPX directory at '$upxDir'" | |
./install_python_venv.ps1 -noprompt -venv_name .venv_kotordiff_${{ matrix.os }}_${{ matrix.python-version }}_${{ matrix.architecture }} | |
python -m pip install --upgrade pip | |
if ("${{ runner.os }}" -eq "Windows") { | |
if ("${{ matrix.python-version }}" -eq "3.12") { | |
pip install "pyinstaller==5.13.2" --prefer-binary | |
} elseif ("${{ matrix.python-version }}" -eq "3.11") { | |
pip install "pyinstaller==5.13.2" --prefer-binary | |
} else { | |
pip install "pyinstaller==5.4" --prefer-binary | |
} | |
} else { | |
pip install pyinstaller -U --prefer-binary | |
} | |
pip install -r Tools/KotorDiff/requirements.txt --prefer-binary | |
$output = "" | |
./compile/compile_kotordiff.ps1 -noprompt -venv_name .venv_kotordiff_${{ matrix.os }}_${{ matrix.python-version }}_${{ matrix.architecture }} -upx_dir $upxDir 2>&1 | ForEach-Object { | |
Write-Output $_.ToString() | |
$output += $_.ToString() + "`n" | |
if($_ -match 'ERROR:') { | |
$errorLines += $_.ToString() | |
} | |
} | |
$warningCount = 0 | |
$output -split "`n" | ForEach-Object { | |
if ($_ -match 'WARNING: Library not found: could not resolve' -or | |
$_ -match 'WARNING: Cannot find ' -or | |
$_ -match 'WARNING: lib not found:' -or | |
$_ -match 'WARNING: Tcl modules directory' -or | |
$_ -match 'WARNING: Failed to upx strip') { | |
$warningCount++ | |
} | |
} | |
if ($errorLines.Count -gt 0) { | |
$errorLines | ForEach-Object { Write-Error $_ } | |
Add-Content -Path $env:GITHUB_OUTPUT -Value "success=false" | |
exit 1 | |
} elseif ($warningCount -ge 3) { | |
Write-Output "Many 'library not found' warnings raised, pyinstaller was probably unsuccessful." | |
Add-Content -Path $env:GITHUB_OUTPUT -Value "success=false" | |
exit 1 | |
} else { | |
Add-Content -Path $env:GITHUB_OUTPUT -Value "success=true" | |
} | |
shell: pwsh | |
- name: KotorDiff - Create Static Binary | |
if: ${{ (success() || failure()) && runner.os == 'Linux' }} | |
run: | | |
./install_python_venv.ps1 -noprompt -venv_name .venv_kotordiff_${{ matrix.os }}_${{ matrix.python-version }}_${{ matrix.architecture }} | |
python -m pip install staticx | |
staticx ./dist/KotorDiff ./dist/KotorDiff-static | |
# Rename the static binary for clarity and consistency | |
Write-Output "./dist/KotorDiff-static --> ./dist/KotorDiff" | |
Move-Item -LiteralPath ./dist/KotorDiff-static -Destination ./dist/KotorDiff -Force | |
shell: pwsh | |
- name: Compile GUIDuplicator | |
if: ${{ success() || failure() }} | |
run: | | |
$upxDir = $env:UPX_DIR | |
Write-Host "Using UPX directory at '$upxDir'" | |
./install_python_venv.ps1 -noprompt -venv_name .venv_guiduplicator_${{ matrix.os }}_${{ matrix.python-version }}_${{ matrix.architecture }} | |
python -m pip install --upgrade pip | |
if ("${{ runner.os }}" -eq "Windows") { | |
if ("${{ matrix.python-version }}" -eq "3.12") { | |
pip install "pyinstaller==5.13.2" --prefer-binary | |
} elseif ("${{ matrix.python-version }}" -eq "3.11") { | |
pip install "pyinstaller==5.13.2" --prefer-binary | |
} else { | |
pip install "pyinstaller==5.4" --prefer-binary | |
} | |
} else { | |
pip install pyinstaller -U --prefer-binary | |
} | |
pip install -r Tools/GuiDuplicator/requirements.txt --prefer-binary | |
$output = "" | |
./compile/compile_gui_duplicator.ps1 -noprompt -venv_name .venv_guiduplicator_${{ matrix.os }}_${{ matrix.python-version }}_${{ matrix.architecture }} -upx_dir $upxDir 2>&1 | ForEach-Object { | |
Write-Output $_.ToString() | |
if($_ -match 'ERROR:') { | |
$errorLines += $_.ToString() | |
} | |
} | |
$warningCount = 0 | |
$output -split "`n" | ForEach-Object { | |
if ($_ -match 'WARNING: Library not found: could not resolve' -or | |
$_ -match 'WARNING: Cannot find ' -or | |
$_ -match 'WARNING: lib not found:' -or | |
$_ -match 'WARNING: Tcl modules directory' -or | |
$_ -match 'WARNING: Failed to upx strip') { | |
$warningCount++ | |
} | |
} | |
if ($errorLines.Count -gt 0) { | |
$errorLines | ForEach-Object { Write-Error $_ } | |
Add-Content -Path $env:GITHUB_OUTPUT -Value "success=false" | |
exit 1 | |
} elseif ($warningCount -ge 3) { | |
Write-Output "Many 'library not found' warnings raised, pyinstaller was probably unsuccessful." | |
Add-Content -Path $env:GITHUB_OUTPUT -Value "success=false" | |
exit 1 | |
} else { | |
Add-Content -Path $env:GITHUB_OUTPUT -Value "success=true" | |
} | |
shell: pwsh | |
- name: GUIDuplicator - Create Static Binary | |
if: ${{ (success() || failure()) && runner.os == 'Linux' }} | |
run: | | |
./install_python_venv.ps1 -noprompt -venv_name .venv_guiduplicator_${{ matrix.os }}_${{ matrix.python-version }}_${{ matrix.architecture }} | |
python -m pip install staticx | |
staticx ./dist/GuiDuplicator ./dist/GuiDuplicator-static | |
# Rename the static binary for clarity and consistency | |
Write-Output "./dist/GuiDuplicator-static --> ./dist/GuiDuplicator" | |
Move-Item -LiteralPath ./dist/GuiDuplicator-static -Destination ./dist/GuiDuplicator -Force | |
shell: pwsh | |
- name: Upload compiled binaries | |
if: ${{ success() || failure() }} | |
uses: actions/upload-artifact@v4 | |
with: | |
name: publish_${{ matrix.os }}_${{ matrix.python-version }}_${{ matrix.architecture }} | |
path: ./dist/** | |
retention-days: 90 | |
- name: Install MinGW with GCC on Windows | |
if: runner.os == 'Windows' && matrix.python-version == '3.13.0-alpha.4' | |
run: | | |
choco install mingw -y | |
$mingwPath = Get-ChildItem -Path C:\, D:\, B:\ -Filter mingw64 -Recurse -ErrorAction SilentlyContinue -Directory | Select-Object -First 1 -ExpandProperty FullName | |
if (-not $mingwPath) { throw "MinGW installation not found" } | |
$binPath = Join-Path -Path $mingwPath -ChildPath "bin" | |
echo "$binPath" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append | |
shell: pwsh | |
- name: Install development packages | |
if: ${{ success() || failure() }} | |
run: | | |
./install_python_venv.ps1 -noprompt -venv_name .venv_${{ matrix.os }}_${{ matrix.python-version }}_${{ matrix.architecture }} | |
python -m pip install --upgrade pip | |
if ($env:python_version -eq "3.7") { | |
pip install -r requirements-dev-py37.txt --prefer-binary | |
} | |
else { | |
pip install -r requirements-dev.txt --prefer-binary | |
} | |
shell: pwsh | |
- name: Run all unittests/pytests | |
if: ${{ success() || failure() }} | |
run: | | |
./install_python_venv.ps1 -noprompt -venv_name .venv_${{ matrix.os }}_${{ matrix.python-version }}_${{ matrix.architecture }} | |
python -m pytest tests -v -ra -o log_cli=true --capture=no --junitxml=pytest_report.xml --html=pytest_report.html --self-contained-html --tb=no --continue-on-collection-errors | |
shell: pwsh | |
continue-on-error: true | |
- name: Upload Pytest Reports | |
if: ${{ success() || failure() }} | |
uses: actions/upload-artifact@v4 | |
with: | |
name: pytest_report_${{ matrix.os }}_${{ matrix.python-version }}_${{ matrix.architecture }} | |
path: | | |
pytest_report.html | |
pytest_report.xml | |
retention-days: 90 | |
package: # The goal of this job is to repackage by toolname, rather than all tools by os_pyversion_arch | |
needs: build # do not start this job until all 'build' jobs complete | |
if: ${{ success() || failure() }} | |
runs-on: ubuntu-latest | |
outputs: | |
filesToArchive: ${{ steps.set-matrix.outputs.matrixJson }} | |
steps: | |
- name: Download all artifacts | |
uses: actions/download-artifact@v4 | |
with: | |
path: published_workflow_builds/ | |
pattern: publish_* | |
- name: Re-package artifacts by Python version and architecture | |
shell: pwsh | |
run: | | |
$here_dirpath = (Get-Location) | |
Write-Output "here: '$here_dirpath'" | |
$packagedArtifactsPath = Join-Path -Path $here_dirpath -ChildPath "packaged_artifacts" | |
New-Item -ItemType Directory -Force -Path $packagedArtifactsPath | |
# Navigate to the directory with downloaded artifacts | |
Set-Location -Path "published_workflow_builds" | |
Get-ChildItem -Recurse -Path "." -Directory | ForEach-Object { Write-Output $_.FullName } | |
$artifactNames = New-Object 'System.Collections.Generic.HashSet[string]' | |
# Iterate through each os_pythonversion_arch folder. | |
Get-ChildItem -Filter "publish_*" | ForEach-Object { | |
$matrixPackagePath = $_.FullName | |
Write-Output "Found matrix package '$matrixPackagePath'" | |
$os, $pythonVersion, $architecture = $_.BaseName -replace '^publish_', '' -split '_' | |
$shortOsName = $os | |
if ($os -eq "ubuntu-20.04" -or $os -eq "ubuntu-22.04" -or $os -eq "ubuntu-latest") { | |
$shortOsName = "Linux" | |
} elseif ($os -eq "macos-11" -or $os -eq "macos-12" -or $os -eq "macos-latest") { | |
$shortOsName = "Mac" | |
} elseif ($os -eq "windows-2019" -or $os -eq "windows-2022" -or $os -eq "windows-latest") { | |
$shortOsName = "Win" | |
} | |
# Iterate through each tool in the folder | |
Get-ChildItem -Path $matrixPackagePath | ForEach-Object { | |
$toolExePath = $_.FullName | |
$toolFileName = $_.Name | |
$fileBaseName = [IO.Path]::GetFileNameWithoutExtension($_.Name) | |
$fileExtension = $_.Extension | |
$outerArchiveName = "$fileBaseName`_$pythonVersion" | |
$outerZipPath = Join-Path -Path $packagedArtifactsPath -ChildPath $outerArchiveName | |
$osSpecificArchiveName = "$fileBaseName`_$shortOsName-$architecture" | |
$osSpecificArchivePath = Join-Path -Path $outerZipPath -ChildPath $osSpecificArchiveName | |
Write-Output " ToolFileName: '$toolFileName'" | |
Write-Output " Tool original filepath: '$toolExePath'" | |
Write-Output "outerArchiveName: '$outerArchiveName'" | |
Write-Output " - osSpecificArchiveName: '$osSpecificArchiveName'" | |
Write-Output "outerZipPath: '$outerZipPath'" | |
Write-Output " - osSpecificArchivePath: '$osSpecificArchivePath'" | |
New-Item -ItemType Directory -Force -Path $outerZipPath | |
chmod 777 -R $toolExePath | |
$toolExeParentDirPath = Split-Path -Parent $toolExePath | |
Write-Output "Creating archive for '$toolFileName' at '$toolExeParentDirPath'..." | |
Write-Output ("Push-Location to start in '$toolExeParentDirPath' (originally at '$(Get-Location)')") | |
Push-Location -Path $toolExeParentDirPath | |
# Archive the tool by os identifier. | |
if ((-not $fileExtension) -or (-not $fileExtension.Trim()) -or ($fileExtension.ToLower().Trim() -eq ".app")) { | |
$osSpecificArchivePath = "$osSpecificArchivePath.tar.gz" | |
if (Test-Path $toolExePath -PathType Container -ErrorAction SilentlyContinue) { # It's a directory | |
tar -czf "$osSpecificArchivePath" -C "$toolExePath" . | |
} else { # It's a file | |
tar -czf "$osSpecificArchivePath" -C "$toolExeParentDirPath" $toolFileName | |
} | |
} else { | |
$osSpecificArchivePath = "$osSpecificArchivePath.zip" | |
zip -r -9 "$osSpecificArchivePath" $toolFileName | |
} | |
Pop-Location | |
Write-Output ("Pop-Location to return to $(Get-Location) (was pushed to '$toolExeParentDirPath')") | |
Write-Output "Compressed archive saved to '$osSpecificArchivePath'" | |
chmod 777 -R "$osSpecificArchivePath" | |
$artifactNames.Add($outerArchiveName) | |
} | |
} | |
# Save artifact names to a file | |
$artifactNames | Out-File -FilePath "../artifact-names.txt" -Encoding UTF8 | |
Get-ChildItem -Force | ForEach-Object { | |
$size = if ($_.PSIsContainer) { "N/A" } else { $_.Length } | |
$attrs = $_.Attributes.ToString() -replace 'ReadOnly', 'RO' -replace 'Hidden', 'H' -replace 'System', 'S' -replace 'Archive', 'A' -replace 'Directory', 'D' -replace ', ', '|' | |
[PSCustomObject]@{ | |
Name = $_.Name | |
Size = $size | |
Attributes = $attrs | |
} | |
} | Format-Table -AutoSize | |
# Navigate back to the root of the workspace | |
Set-Location -Path "../" | |
- name: Generate matrix for uploading | |
if: ${{ success() || failure() }} | |
id: set-matrix | |
shell: pwsh | |
run: | | |
$artifactNames = Get-Content 'artifact-names.txt' -ReadCount 0 | |
# Initialize an array to hold the artifact names directly | |
$matrixArray = @() | |
foreach ($name in $artifactNames) { | |
if (-not [string]::IsNullOrEmpty($name)) { | |
Write-Host "Processing artifact name: $name" | |
# Trim the name and add directly to the array | |
$matrixArray += $name.trim() | |
} | |
} | |
# Convert the array directly to JSON | |
$jsonMatrix = $matrixArray | ConvertTo-Json -Depth 5 -Compress | |
# Use a single line of JSON for the matrix to avoid issues | |
$singleLineJsonMatrix = $jsonMatrix -replace "`r", "" | |
$singleLineJsonMatrix = $singleLineJsonMatrix -replace "`n", "" | |
Write-Host "Matrix JSON:" | |
Write-Host $singleLineJsonMatrix | |
echo "matrixJson<<EOF" >> $env:GITHUB_OUTPUT | |
echo $singleLineJsonMatrix >> $env:GITHUB_OUTPUT | |
echo "EOF" >> $env:GITHUB_OUTPUT | |
- name: Upload all repackages for next job | |
if: ${{ success() || failure() }} | |
uses: actions/upload-artifact@v4 | |
with: | |
name: all_tool_dists_onearchive | |
path: packaged_artifacts/** | |
retention-days: 1 # only used for the next job. | |
compression-level: 0 | |
upload: | |
needs: package | |
if: ${{ success() || failure() }} | |
runs-on: ubuntu-latest | |
strategy: | |
fail-fast: false | |
matrix: | |
artifactb: ${{ fromJson(needs.package.outputs.filesToArchive) }} | |
steps: | |
- name: Download repackages from package job | |
if: ${{ success() || failure() }} | |
uses: actions/download-artifact@v4 | |
with: | |
path: all_tool_dists_onearchive | |
pattern: all_tool_dists* | |
- name: Upload re-packaged artifact | |
if: ${{ success() || failure() }} | |
uses: actions/upload-artifact@v4 | |
with: | |
name: ${{ matrix.artifactb }} | |
path: all_tool_dists_onearchive/all_tool_dists_onearchive/${{ matrix.artifactb }} | |
compression-level: 9 | |
if-no-files-found: error | |
add-test-result-status-badges: | |
needs: build # do not start this job until all 'build' jobs complete | |
if: ${{ success() || failure() }} | |
runs-on: ubuntu-latest | |
concurrency: | |
group: add-test-status-badges-${{ github.ref }} | |
cancel-in-progress: true | |
steps: | |
- name: Checkout code | |
uses: actions/checkout@v4 | |
- name: Download all artifacts | |
uses: actions/download-artifact@v4 | |
with: | |
path: all_pytest_reports | |
pattern: pytest_report_* | |
- name: Extract and update README with custom test status badges | |
shell: pwsh | |
run: | | |
# Git configuration and commit | |
git config --global user.name "GitHub Action" | |
git config --global user.email "[email protected]" | |
# Determine the branch that triggered the workflow | |
$branchName = "${{ github.ref_name }}" | |
$repository_owner = "${{ github.repository_owner }}" | |
$repository = "${{ github.repository }}" | |
$commitSHA = "${{ github.sha }}" | |
$testsResultsPath = "tests/results" | |
Remove-Item -Path $testsResultsPath -Recurse -Force -ErrorAction SilentlyContinue | |
$testsResultsCommitPath = Join-Path -Path $testsResultsPath -ChildPath "$commitSHA" | |
New-Item -ItemType Directory -Force -Path $testsResultsCommitPath -ErrorAction SilentlyContinue | |
$OS_NAMES = '${{ env.OS_RUNNERS_JSON }}' | ConvertFrom-Json | |
$PYTHON_VERSIONS = '${{ env.PYTHON_VERSIONS_JSON }}' | ConvertFrom-Json | |
$ARCHITECTURES = '${{ env.ARCHITECTURES_JSON }}' | ConvertFrom-Json | |
$artifact_reports_dir = "./all_pytest_reports" | |
New-Item -ItemType Directory -Force -Path $artifact_reports_dir -ErrorAction SilentlyContinue | |
Dir -Recurse $artifact_reports_dir | Get-Childitem | |
Get-ChildItem $artifact_reports_dir | ForEach-Object { | |
Write-Output "Moving $($_.FullName) to $testsResultsCommitPath..." | |
Move-Item -LiteralPath $_.FullName -Destination $testsResultsCommitPath | |
} | |
Remove-Item -Path $artifact_reports_dir -Recurse -Force -ErrorAction SilentlyContinue | |
git fetch origin $branchName | |
git add $testsResultsPath --force | |
git add $testsResultsCommitPath --force | |
# Checking if there are newer commits on the remote branch than the commit that triggered the workflow | |
if (git log "${{ github.sha }}"..origin/$branchName --oneline) { | |
Write-Error "Newer commits are present on the remote branch, cannot update readme" | |
exit 1 | |
} | |
git commit -m "Add test results" | |
git push --force-with-lease origin HEAD:${{ github.ref_name }} | |
$commitSHA = git rev-parse HEAD | |
$testResults = @{} | |
Write-Output "Iterate through $testsResultsCommitPath" | |
Get-ChildItem $testsResultsCommitPath -Recurse -Filter pytest_report.xml | ForEach-Object { | |
[xml]$TestResultsXml = Get-Content $_.FullName | |
$totalTests = [int]$TestResultsXml.testsuites.testsuite.tests | |
$failedTests = [int]$TestResultsXml.testsuites.testsuite.failures | |
$errors = [int]$TestResultsXml.testsuites.testsuite.errors | |
$passedTests = $totalTests - $failedTests - $errors | |
$resultFilePathHtml = $_.FullName -replace '\.xml$', '.html' | |
$relHtmlFilePath = Resolve-Path -Path $resultFilePathHtml -Relative | |
if ($relHtmlFilePath.StartsWith(".\") -or $relHtmlFilePath.StartsWith("./")) { | |
$cleanRelHtmlFilePath = $relHtmlFilePath.Substring(2) | |
} else { | |
$cleanRelHtmlFilePath = $relHtmlFilePath | |
} | |
$DetailsURL = "https://htmlpreview.github.io/?https://github.com/$repository/blob/$commitSHA/$cleanRelHtmlFilePath" | |
$key = $_.Directory.Name.Replace('pytest_report_', '').Replace('_', '-') | |
Write-Host "KEY FOUND: '$key'" | |
$testResults[$key] = @{ | |
Passed = $passedTests | |
Failed = $failedTests + $errors | |
Total = $totalTests | |
DetailsURL = $DetailsURL | |
} | |
} | |
$ReadmePath = "./README.md" | |
$ReadmeContent = Get-Content $ReadmePath -Raw | |
$WindowsBadgeContent = "" | |
$LinuxBadgeContent = "" | |
$MacOSBadgeContent = "" | |
$windowsBadgesStartPlaceholder = "<!-- WINDOWS-BADGES-START -->" | |
$windowsBadgesEndPlaceholder = "<!-- WINDOWS-BADGES-END -->" | |
$linuxBadgesStartPlaceholder = "<!-- LINUX-BADGES-START -->" | |
$linuxBadgesEndPlaceholder = "<!-- LINUX-BADGES-END -->" | |
$macosBadgesStartPlaceholder = "<!-- MACOS-BADGES-START -->" | |
$macosBadgesEndPlaceholder = "<!-- MACOS-BADGES-END -->" | |
function Replace-BadgeContent { | |
param ( | |
[string]$readmeContent, | |
[string]$badgeContent, | |
[string]$startPlaceholder, | |
[string]$endPlaceholder | |
) | |
$pattern = [regex]::Escape($startPlaceholder) + "(.|\n)*?" + [regex]::Escape($endPlaceholder) | |
$replacement = $startPlaceholder + "`n" + $badgeContent + "`n" + $endPlaceholder | |
return $readmeContent -replace $pattern, $replacement | |
} | |
foreach ($OS in $OS_NAMES) { | |
foreach ($PYTHON_VERSION in $PYTHON_VERSIONS) { | |
foreach ($ARCH in $ARCHITECTURES) { | |
if ($OS -ne "windows-2019" -and $OS -ne "windows-2022" -and $OS -ne "windows-latest" -and $ARCH -eq "x86") { | |
continue # no x86 support for unix. | |
} | |
$key = "$OS-$PYTHON_VERSION-$ARCH" | |
$shortKey = "$PYTHON_VERSION-$ARCH" | |
if ($testResults.ContainsKey($key)) { | |
$passedTests = $testResults[$key]['Passed'] | |
$failedTests = $testResults[$key]['Failed'] | |
$DetailsURL = $testResults[$key]['DetailsURL'] | |
# Encode the label to replace spaces with underscores and URI-encode other special characters | |
$encodedKey = [System.Web.HttpUtility]::UrlEncode($shortKey.Replace(' ', '_').Replace('-', '--')) | |
$BadgeMarkdown = '[![' + $key + '](https://img.shields.io/badge/build-' + $encodedKey + '_Passing_' + $passedTests + '-brightgreen?style=plastic&logo=simple-icons&logoColor=%23FF5e34&label=' + $failedTests + '&labelColor=%23c71818&color=%232f991a)](' + $DetailsURL + ')' | |
} else { | |
Write-Host "No test results for '$key', must have failed, generating 'Build Failed' badge..." | |
$encodedKey = [System.Web.HttpUtility]::UrlEncode($shortKey.Replace(' ', '_').Replace('-', '--')) | |
$BadgeURLBuildFailed = "https://img.shields.io/badge/${encodedKey}_Build_Failed-lightgrey" | |
$DetailsURL = "https://github.com/$repository/actions/runs/${{ github.run_id }}" | |
$BadgeMarkdown = "[![$shortKey-Build_Failed]($BadgeURLBuildFailed)]($DetailsURL)" | |
} | |
switch ($OS) { | |
"windows-2019" { $WindowsBadgeContent += $BadgeMarkdown + "`n" } | |
"ubuntu-20.04" { $LinuxBadgeContent += $BadgeMarkdown + "`n" } | |
"macos-12" { $MacOSBadgeContent += $BadgeMarkdown + "`n" } | |
} | |
} | |
} | |
} | |
$ReadmeContent = Replace-BadgeContent -readmeContent $ReadmeContent -badgeContent $WindowsBadgeContent.TrimEnd() -startPlaceholder $windowsBadgesStartPlaceholder -endPlaceholder $windowsBadgesEndPlaceholder | |
$ReadmeContent = Replace-BadgeContent -readmeContent $ReadmeContent -badgeContent $LinuxBadgeContent.TrimEnd() -startPlaceholder $linuxBadgesStartPlaceholder -endPlaceholder $linuxBadgesEndPlaceholder | |
$ReadmeContent = Replace-BadgeContent -readmeContent $ReadmeContent -badgeContent $MacOSBadgeContent.TrimEnd() -startPlaceholder $macosBadgesStartPlaceholder -endPlaceholder $macosBadgesEndPlaceholder | |
Set-Content -Path $ReadmePath -Value $ReadmeContent | |
git fetch origin $branchName | |
git add $ReadmePath | |
# Checking if there are newer commits on the remote branch than the commit that triggered the workflow | |
if (git log "${{ github.sha }}"..origin/$branchName --oneline) { | |
Write-Error "Newer commits are present on the remote branch, cannot update readme" | |
exit 1 | |
} | |
git commit -m "Update README.md status badges." | |
git push --force-with-lease origin HEAD:${{ github.ref_name }} |