Fix Qt6 source filename casing #120
This file contains hidden or 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: Build PySide6 Windows Release | |
| on: | |
| push: | |
| branches: | |
| - main | |
| - master | |
| - QT6 | |
| - qt6 | |
| tags: | |
| - "v*" | |
| pull_request: | |
| workflow_dispatch: | |
| permissions: | |
| contents: read | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: true | |
| env: | |
| PYTHON_VERSION: "3.11" | |
| PYTHONUTF8: "1" | |
| PYTHONUNBUFFERED: "1" | |
| PYTHONDONTWRITEBYTECODE: "1" | |
| PIP_DISABLE_PIP_VERSION_CHECK: "1" | |
| QT_QPA_PLATFORM: "offscreen" | |
| jobs: | |
| build-windows: | |
| name: Build and verify Windows x64 package | |
| runs-on: windows-latest | |
| timeout-minutes: 60 | |
| outputs: | |
| build_name: ${{ steps.package.outputs.build_name }} | |
| zip_name: ${{ steps.package.outputs.zip_name }} | |
| artifact_name: ${{ steps.package.outputs.artifact_name }} | |
| steps: | |
| - name: Checkout source | |
| uses: actions/checkout@v7 | |
| with: | |
| ref: ${{ github.ref }} | |
| fetch-depth: 0 | |
| persist-credentials: false | |
| - name: Show selected ref and checked-out revision | |
| shell: pwsh | |
| run: | | |
| Write-Host "event_name = ${{ github.event_name }}" | |
| Write-Host "github.ref = ${{ github.ref }}" | |
| Write-Host "github.ref_name = ${{ github.ref_name }}" | |
| Write-Host "github.sha = ${{ github.sha }}" | |
| Write-Host "workspace = $env:GITHUB_WORKSPACE" | |
| git rev-parse --show-toplevel | |
| git rev-parse HEAD | |
| git branch --show-current | |
| git status --short | |
| Write-Host "Tracked root files:" | |
| git ls-files | Sort-Object | |
| - name: Validate exact repository paths | |
| shell: pwsh | |
| run: | | |
| $requiredFiles = @( | |
| "nfo_editor.py", | |
| "nfo_editor_ui.py", | |
| "nfo_editor_events.py", | |
| "nfo_utils.py", | |
| "cg_crop.py", | |
| "cg_dedupe.py", | |
| "cg_photo_wall.py", | |
| "cg_rename.py", | |
| "nfo_editor.spec", | |
| "requirements.txt", | |
| "requirements-build.txt", | |
| "scripts/check_source_layout.py", | |
| "scripts/qt_source_smoke.py", | |
| "scripts/build_windows.ps1", | |
| "scripts/check_windows_artifact.ps1" | |
| ) | |
| $trackedFiles = @(git ls-files | ForEach-Object { $_.Replace("\", "/") }) | |
| $wrongCaseOrMissing = @( | |
| $requiredFiles | Where-Object { -not ($trackedFiles -ccontains $_) } | |
| ) | |
| if ($wrongCaseOrMissing.Count -gt 0) { | |
| Write-Host "Tracked files with similar spelling:" | |
| foreach ($expected in $wrongCaseOrMissing) { | |
| $leaf = Split-Path -Leaf $expected | |
| $trackedFiles | | |
| Where-Object { (Split-Path -Leaf $_) -ieq $leaf } | | |
| ForEach-Object { Write-Host " actual: $_" } | |
| } | |
| Write-Error ( | |
| "Required files are missing or use incorrect filename casing:`n - " + | |
| ($wrongCaseOrMissing -join "`n - ") | |
| ) | |
| throw "Run scripts/fix_source_filename_case.ps1, commit, and push." | |
| } | |
| $rootNames = @(Get-ChildItem -Force | ForEach-Object { $_.Name }) | |
| if (-not ($rootNames -ccontains "img")) { | |
| $actualImg = $rootNames | Where-Object { $_ -ieq "img" } | |
| Write-Error "Expected directory exactly 'img'; actual match: $actualImg" | |
| throw "Directory filename casing is incorrect." | |
| } | |
| Write-Host "All required paths and filename casing are correct." | |
| - name: Set up Python | |
| uses: actions/setup-python@v6 | |
| with: | |
| python-version: ${{ env.PYTHON_VERSION }} | |
| architecture: x64 | |
| cache: pip | |
| cache-dependency-path: | | |
| requirements.txt | |
| requirements-build.txt | |
| - name: Run repository layout validator | |
| shell: pwsh | |
| run: | | |
| python scripts/check_source_layout.py | |
| - name: Install runtime and build dependencies | |
| shell: pwsh | |
| run: | | |
| python -m pip install --upgrade pip setuptools wheel | |
| python -m pip install -r requirements-build.txt | |
| python -m pip check | |
| python -c "import PySide6, qfluentwidgets, PyInstaller; print('PySide6:', PySide6.__version__); print('PyInstaller:', PyInstaller.__version__)" | |
| - name: Reject legacy Qt bindings | |
| shell: pwsh | |
| run: | | |
| @' | |
| import importlib.util | |
| import sys | |
| forbidden = ("PyQt5", "PyQt6", "PySide2") | |
| found = [name for name in forbidden if importlib.util.find_spec(name) is not None] | |
| if found: | |
| raise SystemExit(f"Legacy/foreign Qt bindings installed: {', '.join(found)}") | |
| print("Only the PySide6 Qt binding is installed.") | |
| '@ | python - | |
| - name: Compile and run unit tests | |
| shell: pwsh | |
| run: | | |
| python -m compileall -q . | |
| python -m unittest discover -s tests -q | |
| $lintFiles = @( | |
| "nfo_editor.py", | |
| "nfo_editor_ui.py", | |
| "nfo_editor_events.py", | |
| "nfo_utils.py", | |
| "cg_crop.py", | |
| "cg_dedupe.py", | |
| "cg_photo_wall.py", | |
| "cg_rename.py" | |
| ) | |
| $lintFiles += Get-ChildItem tests -Filter "*.py" -File | ForEach-Object { $_.FullName } | |
| $lintFiles += Get-ChildItem scripts -Filter "*.py" -File | ForEach-Object { $_.FullName } | |
| python -m pyflakes @lintFiles | |
| - name: Run source Qt startup smoke test | |
| shell: pwsh | |
| run: | | |
| python scripts/qt_source_smoke.py | |
| - name: Resolve package name | |
| id: package | |
| shell: pwsh | |
| run: | | |
| $shortSha = "${{ github.sha }}".Substring(0, 7) | |
| if ("${{ github.ref_type }}" -eq "tag") { | |
| $buildName = "${{ github.ref_name }}" | |
| } | |
| else { | |
| $buildName = "dev-${{ github.run_number }}-$shortSha" | |
| } | |
| $zipName = "NFOTools-$buildName-windows-x64.zip" | |
| $artifactName = "NFOTools-$buildName-windows-x64" | |
| "BUILD_NAME=$buildName" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append | |
| "ZIP_NAME=$zipName" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append | |
| "build_name=$buildName" | Out-File -FilePath $env:GITHUB_OUTPUT -Encoding utf8 -Append | |
| "zip_name=$zipName" | Out-File -FilePath $env:GITHUB_OUTPUT -Encoding utf8 -Append | |
| "artifact_name=$artifactName" | Out-File -FilePath $env:GITHUB_OUTPUT -Encoding utf8 -Append | |
| - name: Build PySide6 applications | |
| shell: pwsh | |
| run: | | |
| ./scripts/build_windows.ps1 -OutputRoot "dist/NFOTools" | |
| - name: Check build contents and launch executables | |
| shell: pwsh | |
| run: | | |
| ./scripts/check_windows_artifact.ps1 -Root "dist/NFOTools" -RunSmokeTests | |
| - name: Create release archive and checksum | |
| shell: pwsh | |
| run: | | |
| if (Test-Path $env:ZIP_NAME) { | |
| Remove-Item $env:ZIP_NAME -Force | |
| } | |
| Compress-Archive -Path "dist/NFOTools/*" -DestinationPath $env:ZIP_NAME -CompressionLevel Optimal | |
| $hash = (Get-FileHash -Algorithm SHA256 $env:ZIP_NAME).Hash.ToLowerInvariant() | |
| "$hash $env:ZIP_NAME" | Set-Content -Path "$env:ZIP_NAME.sha256" -Encoding ascii | |
| if ((Get-Item $env:ZIP_NAME).Length -lt 1MB) { | |
| throw "Release archive is unexpectedly small." | |
| } | |
| Write-Host "Archive: $env:ZIP_NAME" | |
| Write-Host "SHA-256: $hash" | |
| - name: Upload verified artifact | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: ${{ steps.package.outputs.artifact_name }} | |
| path: | | |
| ${{ steps.package.outputs.zip_name }} | |
| ${{ steps.package.outputs.zip_name }}.sha256 | |
| if-no-files-found: error | |
| retention-days: 14 | |
| compression-level: 0 | |
| release: | |
| name: Publish tagged release | |
| if: github.event_name == 'push' && github.ref_type == 'tag' | |
| needs: build-windows | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: write | |
| steps: | |
| - name: Download verified build artifact | |
| uses: actions/download-artifact@v8 | |
| with: | |
| name: ${{ needs.build-windows.outputs.artifact_name }} | |
| path: release | |
| digest-mismatch: error | |
| - name: Publish or update GitHub Release | |
| shell: bash | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| GH_REPO: ${{ github.repository }} | |
| TAG_NAME: ${{ github.ref_name }} | |
| ZIP_NAME: ${{ needs.build-windows.outputs.zip_name }} | |
| run: | | |
| set -euo pipefail | |
| cd release | |
| test -s "$ZIP_NAME" | |
| test -s "$ZIP_NAME.sha256" | |
| sha256sum --check "$ZIP_NAME.sha256" | |
| if gh release view "$TAG_NAME" >/dev/null 2>&1; then | |
| gh release upload "$TAG_NAME" "$ZIP_NAME" "$ZIP_NAME.sha256" --clobber | |
| else | |
| gh release create "$TAG_NAME" "$ZIP_NAME" "$ZIP_NAME.sha256" \ | |
| --verify-tag \ | |
| --generate-notes \ | |
| --title "NFOTools $TAG_NAME" | |
| fi |