Test PyKotor #176
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: 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.7", "3.8", "3.9", "3.10", "3.11", "3.12"]' | |
ARCHITECTURES_JSON: '["x86", "x64"]' | |
INTERPRETERS_JSON: '["python"]' | |
on: | |
# push: | |
pull_request: | |
types: [opened, reopened] | |
workflow_dispatch: | |
schedule: | |
- cron: '0 5 * * 1' # Runs every Monday at 05:00 UTC | |
permissions: | |
contents: write | |
jobs: | |
setup: | |
runs-on: ubuntu-latest | |
outputs: | |
os: ${{ steps.set_env.outputs.os }} | |
python-version: ${{ steps.set_env.outputs.python-version }} | |
architecture: ${{ steps.set_env.outputs.architecture }} | |
python-pypy: ${{ steps.set_env.outputs.python-pypy }} | |
#qt_version: ${#{ steps.set_env.outputs.qt_version }} | |
steps: | |
- name: Set environment variables | |
id: set_env | |
run: | | |
# Use a single line of JSON to avoid issues | |
$singleLineJson = '${{ env.OS_RUNNERS_JSON }}' -replace "`r", "" | |
$singleLineJson = $singleLineJson -replace "`n", "" | |
Write-Host $singleLineJson | |
echo "os<<EOF" >> $env:GITHUB_OUTPUT | |
echo $singleLineJson >> $env:GITHUB_OUTPUT | |
echo "EOF" >> $env:GITHUB_OUTPUT | |
$singleLineJson = '${{ env.PYTHON_VERSIONS_JSON }}' -replace "`r", "" | |
$singleLineJson = $singleLineJson -replace "`n", "" | |
Write-Host $singleLineJson | |
echo "python-version<<EOF" >> $env:GITHUB_OUTPUT | |
echo $singleLineJson >> $env:GITHUB_OUTPUT | |
echo "EOF" >> $env:GITHUB_OUTPUT | |
$singleLineJson = '${{ env.ARCHITECTURES_JSON }}' -replace "`r", "" | |
$singleLineJson = $singleLineJson -replace "`n", "" | |
Write-Host $singleLineJson | |
echo "architecture<<EOF" >> $env:GITHUB_OUTPUT | |
echo $singleLineJson >> $env:GITHUB_OUTPUT | |
echo "EOF" >> $env:GITHUB_OUTPUT | |
$singleLineJson = '${{ env.INTERPRETERS_JSON }}' -replace "`r", "" | |
$singleLineJson = $singleLineJson -replace "`n", "" | |
Write-Host $singleLineJson | |
echo "python-pypy<<EOF" >> $env:GITHUB_OUTPUT | |
echo $singleLineJson >> $env:GITHUB_OUTPUT | |
echo "EOF" >> $env:GITHUB_OUTPUT | |
shell: pwsh | |
runtests: | |
needs: setup | |
runs-on: ${{ matrix.os }} | |
strategy: | |
fail-fast: false # Disable automatic cancellation of other jobs | |
matrix: | |
os: ${{ fromJson(needs.setup.outputs.os) }} | |
python-version: ${{ fromJson(needs.setup.outputs.python-version) }} | |
architecture: ${{ fromJson(needs.setup.outputs.architecture) }} | |
python_pypy: ${{ fromJson(needs.setup.outputs.python-pypy) }} | |
#qt_version: ['pyqt5', 'pyqt6', 'pyside2', 'pyside6'] | |
exclude: | |
# unix x86 is definitely not supported. | |
- os: ubuntu-20.04 | |
architecture: x86 | |
- os: macos-12 | |
architecture: x86 | |
# Latest pypy is 3.10 | |
- python_pypy: 'pypy' | |
python-version: '3.11' | |
- python_pypy: 'pypy' | |
python-version: '3.12' | |
steps: | |
- name: Determine Python version string | |
id: set-python-version | |
run: | | |
if ( "${{ matrix.python_pypy }}" -eq "pypy" ) { | |
Add-Content -Path $env:GITHUB_ENV -Value "PYTHON_VERSION=pypy-${{ matrix.python-version }}" | |
} else { | |
Add-Content -Path $env:GITHUB_ENV -Value "PYTHON_VERSION=${{ matrix.python-version }}" | |
} | |
shell: pwsh | |
- uses: actions/checkout@v4 | |
- name: Set up ${{ matrix.python_pypy }} ${{ matrix.python-version }} | |
if: ${{ matrix.python_pypy == 'pypy' }} | |
uses: actions/setup-python@v5 | |
with: | |
python-version: ${{ env.PYTHON_VERSION }} | |
architecture: ${{ matrix.architecture }} | |
- name: Install development packages | |
if: ${{ success() || failure() }} | |
env: | |
MATRIX_ARCH: ${{ matrix.architecture }} | |
run: | | |
. ./install_python_venv.ps1 -noprompt -venv_name .venv_${{ matrix.os }}_${{ matrix.python_pypy }}_${{ matrix.python-version }}_${{ matrix.architecture }} -force_python_version ${{ matrix.python-version }} | |
& $pythonExePath -m pip install --upgrade pip | |
if ("${{ matrix.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_pypy }}_${{ matrix.python-version }}_${{ matrix.architecture }} | |
& $pythonExePath -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_pypy }}_${{ matrix.python-version }}_${{ matrix.architecture }} | |
path: | | |
pytest_report.html | |
pytest_report.xml | |
retention-days: 90 | |
add-test-result-status-badges: | |
needs: runtests | |
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 | |
$INTERPRETERS = '${{ env.INTERPRETERS_JSON }}' | ConvertFrom-Json | |
$artifact_reports_dir = "./all_pytest_reports" | |
New-Item -ItemType Directory -Force -Path $artifact_reports_dir -ErrorAction SilentlyContinue | |
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 | |
$ReadmePath = "./README.md" | |
$ReadmeContent = Get-Content $ReadmePath -Raw | |
# Remove the readme temporarily to prevent merge conflicts | |
Remove-Item -Path $ReadmePath -Force -ErrorAction Stop | |
git fetch origin $branchName | |
git add $ReadmePath --force | |
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, prepare README" | |
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 { | |
$os, $python_or_pypy, $pythonVersion, $architecture = $_.BaseName -replace '^publish_', '' -split '_' | |
[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 | |
} | |
} | |
$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 ($INTERPRETER in $INTERPRETERS) { | |
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-$INTERPRETER-$PYTHON_VERSION-$ARCH" | |
$shortKey = "$INTERPRETER-$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" } | |
"windows-2022" { $WindowsBadgeContent += $BadgeMarkdown + "`n" } | |
"windows-latest" { $WindowsBadgeContent += $BadgeMarkdown + "`n" } | |
"ubuntu-20.04" { $LinuxBadgeContent += $BadgeMarkdown + "`n" } | |
"ubuntu-22.04" { $LinuxBadgeContent += $BadgeMarkdown + "`n" } | |
"ubuntu-latest" { $LinuxBadgeContent += $BadgeMarkdown + "`n" } | |
"macos-10" { $MacOSBadgeContent += $BadgeMarkdown + "`n" } | |
"macos-12" { $MacOSBadgeContent += $BadgeMarkdown + "`n" } | |
"macos-latest" { $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 }} |