From 1f7f2e4fa35e37bf3e9a1e91367c42f985eed17d Mon Sep 17 00:00:00 2001 From: Philipp Verpoort Date: Tue, 1 Sep 2026 10:35:58 +0200 Subject: [PATCH 1/6] Attempt to fix --- .github/workflows/build.yml | 133 +++++++++++++++++++++++++++++++++++- 1 file changed, 131 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8cc740b..8fa08bb 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -13,9 +13,26 @@ jobs: runs-on: ${{ matrix.os }} + env: + # Nuitka needs to download a pinned MinGW64 gcc on Windows (and may need + # other helper tools like ccache on other platforms). It normally asks for + # interactive confirmation before doing so, which hangs/fails on CI's + # non-interactive stdin. This makes it assume "yes" everywhere instead of + # only passing --assume-yes-for-downloads for one platform. + NUITKA_ASSUME_YES_FOR_DOWNLOADS: "1" + steps: - uses: actions/checkout@v4 + ### DEBUG: SHOW CHECKED-OUT SOURCE TREE + - name: "Debug: list checked-out repo contents" + shell: bash + run: | + echo "--- repo root (top 2 levels) ---" + find . -maxdepth 2 -not -path '*/.git*' | sort + echo "--- app/ (full) ---" + find app -not -path '*/.git*' | sort + ### INSTALL UV - name: Install uv (Unix) if: runner.os != 'Windows' @@ -52,18 +69,98 @@ jobs: -OutFile "$dest\depends.zip" Expand-Archive -Path "$dest\depends.zip" -DestinationPath $dest -Force + ### GENERATE A FRESH pysidedeploy.spec, WITHOUT NUITKA'S --quiet FLAG + # pysidedeploy.spec is intentionally not committed (see AGENTS.md), so pyside6-deploy + # would otherwise regenerate one from its own template on every run, which bakes in + # "extra_args = --quiet --noinclude-qt-translations". --quiet hides Nuitka's own + # compiler/Scons output -- which is exactly the output that revealed the Windows build's + # real failure (an unanswerable interactive MinGW64-download prompt) after "Build app" + # below had already reported success. --init only writes the config file, it does not + # invoke Nuitka, so this is safe/fast to run separately first. + - name: Generate deployment spec (verbose Nuitka output) + shell: bash + run: | + uv run pyside6-deploy --init --name groupselect2 app/main.py + sed -i.bak 's/^extra_args = .*/extra_args = --noinclude-qt-translations/' app/pysidedeploy.spec + rm -f app/pysidedeploy.spec.bak + echo "--- generated app/pysidedeploy.spec ---" + cat app/pysidedeploy.spec + ### BUILD APP WITH PYSIDE6-DEPLOY AND UPLOAD AS ARTIFACT - name: Build app run: uv run pyside6-deploy --mode standalone --name groupselect2 app/main.py + ### DEBUG: SHOW WHAT pyside6-deploy ACTUALLY PRODUCED + - name: "Debug: list build output" + shell: bash + run: | + echo "--- app/ (top level) ---" + ls -la app + for d in app/groupselect2.dist app/groupselect2.app app/deployment; do + if [ -e "$d" ]; then + echo "--- $d exists: $(find "$d" -type f | wc -l) files, $(du -sh "$d" | cut -f1) ---" + find "$d" -maxdepth 2 | sort + else + echo "--- $d does not exist ---" + fi + done + + ### HARD FAILURE GATE: pyside6-deploy can exit 0 even when the Nuitka compile it wraps + # failed (it catches the exception, logs it, and still reports "Executed file created" + # -- this is exactly how the Windows build silently shipped an empty package to the + # Store before). Fail the job loudly here instead of packaging whatever partial output + # (if any) exists. + - name: Verify build output exists + shell: bash + run: | + if [ "${{ runner.os }}" = "macOS" ]; then + dir="app/groupselect2.app/Contents/MacOS" + if [ ! -d "$dir" ] || [ -z "$(ls -A "$dir" 2>/dev/null)" ]; then + echo "::error::app/groupselect2.app/Contents/MacOS is missing or empty -- pyside6-deploy did not produce a real app bundle. Check the 'Build app' step's log above for a Nuitka error." + exit 1 + fi + echo "Found app bundle contents: $(ls "$dir")" + else + if [ "${{ runner.os }}" = "Windows" ]; then + exe="app/groupselect2.dist/main.exe" + else + exe="app/groupselect2.dist/main.bin" + fi + if [ ! -f "$exe" ]; then + echo "::error::Expected built executable not found at $exe -- pyside6-deploy did not produce a real build. Check the 'Build app' step's log above for a Nuitka error (e.g. an unanswered interactive download prompt)." + exit 1 + fi + echo "Found built executable: $exe ($(du -h "$exe" | cut -f1))" + fi + - name: Copy resources - run: python -c "import shutil; shutil.copytree('app/resources', 'app/groupselect2.dist/resources')" + shell: bash + run: | + if [ "${{ runner.os }}" = "macOS" ]; then + dest="app/groupselect2.app/Contents/Resources/resources" + else + dest="app/groupselect2.dist/resources" + fi + python -c "import shutil,sys; shutil.copytree(sys.argv[1], sys.argv[2])" app/resources "$dest" + echo "Copied app/resources into $dest" + + ### DEBUG: SHOW FINAL DIST/BUNDLE CONTENTS BEFORE UPLOAD + - name: "Debug: list final dist contents before upload" + shell: bash + run: | + if [ "${{ runner.os }}" = "macOS" ]; then + root="app/groupselect2.app" + else + root="app/groupselect2.dist" + fi + echo "--- $root: $(find "$root" -type f | wc -l) files, $(du -sh "$root" | cut -f1) ---" + find "$root" -maxdepth 2 | sort - name: Upload artifact uses: actions/upload-artifact@v4 with: name: groupselect2-dist-${{ matrix.os }} - path: app/groupselect2.dist + path: ${{ runner.os == 'macOS' && 'app/groupselect2.app' || 'app/groupselect2.dist' }} ### BUILD MSIX FOR WINDOWS - name: Set version in manifest @@ -98,6 +195,32 @@ jobs: Copy-Item "packaging\licenses\PYSIDE6_LICENSES.txt" "$pkg\" Copy-Item "packaging\licenses\QT_LIBRARY_REPLACEMENT.txt" "$pkg\" + ### DEBUG: SHOW WHAT ENDED UP IN THE MSIX STAGING FOLDER + - name: "Debug: list MSIX staging contents" + if: runner.os == 'Windows' + shell: pwsh + run: | + Write-Host "--- msix_staging contents ---" + Get-ChildItem -Recurse msix_staging | + Select-Object FullName, Length | Format-Table -AutoSize + $totalSize = (Get-ChildItem -Recurse -File msix_staging | + Measure-Object -Property Length -Sum).Sum + Write-Host "Total size: $totalSize bytes" + + ### HARD FAILURE GATE: this is exactly the folder that got zipped into the .msix that + # shipped to the Store without the app in it -- re-check right here, closest to the + # actual packaging step, in addition to the "Verify build output exists" check above. + - name: Verify MSIX staging contains executable + if: runner.os == 'Windows' + shell: pwsh + run: | + $exe = "msix_staging\main.exe" + if (-not (Test-Path $exe)) { + Write-Error "$exe not found in msix_staging -- the MSIX would ship without the app executable (this is exactly the bug that broke the Store submission). Check the 'Build app' and 'Verify build output exists' steps' logs above." + exit 1 + } + Write-Host "Found $exe ($((Get-Item $exe).Length) bytes)" + - name: Build MSIX with makeappx if: runner.os == 'Windows' shell: pwsh @@ -109,6 +232,12 @@ jobs: & $makeappx pack /d msix_staging /p groupselect2.msix /nv + ### DEBUG: SHOW FINAL MSIX FILE SIZE + - name: "Debug: show final MSIX size" + if: runner.os == 'Windows' + shell: pwsh + run: Get-Item groupselect2.msix | Select-Object FullName, Length + - name: Upload MSIX artifact if: runner.os == 'Windows' uses: actions/upload-artifact@v4 From debf8a55907fd947cf41899f9ade2de4e2a099f4 Mon Sep 17 00:00:00 2001 From: Philipp Verpoort Date: Tue, 1 Sep 2026 11:21:36 +0200 Subject: [PATCH 2/6] Fixed MinGW64 issue for NUITKA --- .github/workflows/build.yml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8fa08bb..fe77690 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -86,6 +86,20 @@ jobs: echo "--- generated app/pysidedeploy.spec ---" cat app/pysidedeploy.spec + ### REMOVE PRE-INSTALLED MINGW64 FROM PATH ON WINDOWS + # windows-latest runners ship their own MinGW64 gcc at C:\mingw64\bin, already on PATH. + # Nuitka detects this pre-existing, non-Nuitka-managed gcc.exe first, refuses it because + # it isn't the exact pinned winlibs build it requires, and hits a rough edge in its own + # Scons version-probe logic that turns this into a hard FATAL/crash instead of cleanly + # falling through to downloading its own copy (see nuitka/Nuitka#3309). Hiding it from + # PATH avoids the conflict entirely, letting Nuitka download and use its own pinned gcc. + - name: Remove pre-installed MinGW64 from PATH (Windows) + if: runner.os == 'Windows' + shell: pwsh + run: | + $newPath = ($env:PATH -split ';' | Where-Object { $_ -notlike 'C:\mingw64*' }) -join ';' + echo "PATH=$newPath" >> $env:GITHUB_ENV + ### BUILD APP WITH PYSIDE6-DEPLOY AND UPLOAD AS ARTIFACT - name: Build app run: uv run pyside6-deploy --mode standalone --name groupselect2 app/main.py From a942af1b288f29665b4e43b8b3372ea453181821 Mon Sep 17 00:00:00 2001 From: Philipp Verpoort Date: Tue, 1 Sep 2026 11:29:48 +0200 Subject: [PATCH 3/6] Reduced retention days --- .github/workflows/build.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index fe77690..5648116 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -175,6 +175,7 @@ jobs: with: name: groupselect2-dist-${{ matrix.os }} path: ${{ runner.os == 'macOS' && 'app/groupselect2.app' || 'app/groupselect2.dist' }} + retention-days: 15 ### BUILD MSIX FOR WINDOWS - name: Set version in manifest @@ -258,6 +259,7 @@ jobs: with: name: groupselect2-msix path: groupselect2.msix + retention-days: 15 ### ATTACH BUILDS TO THE GITHUB RELEASE AS DOWNLOADABLE ASSETS publish-release-assets: From c77db566ccff2160ac9edd09270317f2631e4119 Mon Sep 17 00:00:00 2001 From: Philipp Verpoort Date: Tue, 1 Sep 2026 11:31:36 +0200 Subject: [PATCH 4/6] Test PATH for GCC --- .github/workflows/build.yml | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 5648116..d0fd934 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -86,18 +86,22 @@ jobs: echo "--- generated app/pysidedeploy.spec ---" cat app/pysidedeploy.spec - ### REMOVE PRE-INSTALLED MINGW64 FROM PATH ON WINDOWS - # windows-latest runners ship their own MinGW64 gcc at C:\mingw64\bin, already on PATH. - # Nuitka detects this pre-existing, non-Nuitka-managed gcc.exe first, refuses it because - # it isn't the exact pinned winlibs build it requires, and hits a rough edge in its own - # Scons version-probe logic that turns this into a hard FATAL/crash instead of cleanly - # falling through to downloading its own copy (see nuitka/Nuitka#3309). Hiding it from - # PATH avoids the conflict entirely, letting Nuitka download and use its own pinned gcc. - - name: Remove pre-installed MinGW64 from PATH (Windows) + ### REMOVE ANY PRE-INSTALLED GCC FROM PATH ON WINDOWS + # windows-latest runners ship several unrelated MinGW/gcc toolchains pre-installed and on + # PATH (e.g. C:\mingw64, and Strawberry Perl's C:\Strawberry\c\bin -- there may be others). + # Nuitka detects whichever pre-existing, non-Nuitka-managed gcc.exe it finds on PATH first, + # refuses it because it isn't the exact pinned winlibs build it requires, and hits a rough + # edge in its own Scons version-probe logic that turns this into a hard FATAL/crash instead + # of cleanly falling through to downloading its own copy (see nuitka/Nuitka#3309). Rather + # than hardcode one offending path (which just surfaces the next one), strip every PATH + # directory that actually contains a gcc.exe, so Nuitka only ever finds its own cached copy. + - name: Remove pre-installed gcc from PATH (Windows) if: runner.os == 'Windows' shell: pwsh run: | - $newPath = ($env:PATH -split ';' | Where-Object { $_ -notlike 'C:\mingw64*' }) -join ';' + $newPath = ($env:PATH -split ';' | Where-Object { + $_ -and -not (Test-Path (Join-Path $_ 'gcc.exe')) + }) -join ';' echo "PATH=$newPath" >> $env:GITHUB_ENV ### BUILD APP WITH PYSIDE6-DEPLOY AND UPLOAD AS ARTIFACT From 2d0195340b31ab7a4bb4a47d8589465cf830de46 Mon Sep 17 00:00:00 2001 From: Philipp Verpoort Date: Tue, 1 Sep 2026 11:43:06 +0200 Subject: [PATCH 5/6] Switch Windows build to MSVC, drop gcc-PATH workaround Nuitka's MinGW64 auto-download is broken on Windows CI right now (FATAL: Only this specific gcc is supported with Nuitka / Failed unexpectedly in Scons C backend compilation), and this happens even with zero gcc anywhere on PATH -- ruling out the pre-installed- toolchain-conflict theory from the previous two commits. Matches the open upstream regression nuitka/Nuitka#3309. windows-latest already ships Visual Studio Build Tools, so force --msvc=latest on Windows instead of fighting the MinGW64 download path. Removes the now-pointless "Remove pre-installed gcc from PATH" step. --- .github/workflows/build.yml | 49 +++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 27 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d0fd934..17363f7 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -14,11 +14,12 @@ jobs: runs-on: ${{ matrix.os }} env: - # Nuitka needs to download a pinned MinGW64 gcc on Windows (and may need - # other helper tools like ccache on other platforms). It normally asks for - # interactive confirmation before doing so, which hangs/fails on CI's - # non-interactive stdin. This makes it assume "yes" everywhere instead of - # only passing --assume-yes-for-downloads for one platform. + # Nuitka may need to download small helper tools on some platforms (e.g. ccache). + # It normally asks for interactive confirmation before doing so, which hangs/fails + # on CI's non-interactive stdin. This makes it assume "yes" everywhere. Windows no + # longer needs this for its compiler specifically -- it's forced onto the pre- + # installed MSVC via --msvc=latest below, since Nuitka's own MinGW64 auto-download + # is currently broken on Windows CI (see the "Generate deployment spec" step). NUITKA_ASSUME_YES_FOR_DOWNLOADS: "1" steps: @@ -74,36 +75,30 @@ jobs: # would otherwise regenerate one from its own template on every run, which bakes in # "extra_args = --quiet --noinclude-qt-translations". --quiet hides Nuitka's own # compiler/Scons output -- which is exactly the output that revealed the Windows build's - # real failure (an unanswerable interactive MinGW64-download prompt) after "Build app" - # below had already reported success. --init only writes the config file, it does not - # invoke Nuitka, so this is safe/fast to run separately first. + # real failure after "Build app" below had already reported success. --init only writes + # the config file, it does not invoke Nuitka, so this is safe/fast to run separately first. + # + # On Windows we also force --msvc=latest. Nuitka's default Windows backend auto-downloads + # a pinned MinGW64 gcc, but that download/retry flow is currently broken on Windows CI + # (confirmed independently of any PATH conflict -- fails with "FATAL: Only this specific + # gcc is supported with Nuitka" / "Failed unexpectedly in Scons C backend compilation" even + # with zero gcc anywhere on PATH; matches the open regression nuitka/Nuitka#3309). windows- + # latest runners ship Visual Studio Build Tools already, so --msvc=latest sidesteps the + # whole MinGW64 download path and uses the compiler that's already there. - name: Generate deployment spec (verbose Nuitka output) shell: bash run: | uv run pyside6-deploy --init --name groupselect2 app/main.py - sed -i.bak 's/^extra_args = .*/extra_args = --noinclude-qt-translations/' app/pysidedeploy.spec + if [ "${{ runner.os }}" = "Windows" ]; then + extra_args="--msvc=latest --noinclude-qt-translations" + else + extra_args="--noinclude-qt-translations" + fi + sed -i.bak "s/^extra_args = .*/extra_args = $extra_args/" app/pysidedeploy.spec rm -f app/pysidedeploy.spec.bak echo "--- generated app/pysidedeploy.spec ---" cat app/pysidedeploy.spec - ### REMOVE ANY PRE-INSTALLED GCC FROM PATH ON WINDOWS - # windows-latest runners ship several unrelated MinGW/gcc toolchains pre-installed and on - # PATH (e.g. C:\mingw64, and Strawberry Perl's C:\Strawberry\c\bin -- there may be others). - # Nuitka detects whichever pre-existing, non-Nuitka-managed gcc.exe it finds on PATH first, - # refuses it because it isn't the exact pinned winlibs build it requires, and hits a rough - # edge in its own Scons version-probe logic that turns this into a hard FATAL/crash instead - # of cleanly falling through to downloading its own copy (see nuitka/Nuitka#3309). Rather - # than hardcode one offending path (which just surfaces the next one), strip every PATH - # directory that actually contains a gcc.exe, so Nuitka only ever finds its own cached copy. - - name: Remove pre-installed gcc from PATH (Windows) - if: runner.os == 'Windows' - shell: pwsh - run: | - $newPath = ($env:PATH -split ';' | Where-Object { - $_ -and -not (Test-Path (Join-Path $_ 'gcc.exe')) - }) -join ';' - echo "PATH=$newPath" >> $env:GITHUB_ENV - ### BUILD APP WITH PYSIDE6-DEPLOY AND UPLOAD AS ARTIFACT - name: Build app run: uv run pyside6-deploy --mode standalone --name groupselect2 app/main.py From 06c6891040b1a8fb85900c3576e38b80eb513b4a Mon Sep 17 00:00:00 2001 From: Philipp Verpoort Date: Tue, 1 Sep 2026 11:53:44 +0200 Subject: [PATCH 6/6] Pin Windows build to windows-2022 (VS2022), not windows-latest windows-latest moved to Windows Server 2025 + Visual Studio 2026 in June 2026 (github.blog changelog / actions/runner-images#14016). Nuitka 2.7.11's MSVC auto-detection doesn't recognize the VS2026 toolset yet: "Nuitka-Scons: MSVC version resolved to None" -> "FATAL: Error, cannot locate suitable C compiler", even with --msvc=latest. This -- not any gcc/PATH conflict or MinGW64-download issue -- was the actual root cause of every Windows build failure investigated so far in this branch: the runner image moved out from under Nuitka. Pin to windows-2022, GitHub's still-maintained image carrying the older, Nuitka-supported VS2022 toolset. --- .github/workflows/build.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 17363f7..f750a94 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -9,7 +9,13 @@ jobs: build: strategy: matrix: - os: [ubuntu-latest, macos-latest, windows-latest] + # windows-latest pinned to windows-2022 (Visual Studio 2022): GitHub moved + # windows-latest to Windows Server 2025 + Visual Studio 2026 in June 2026, and + # Nuitka 2.7.11's MSVC auto-detection doesn't recognize the VS2026 toolset yet + # ("Nuitka-Scons: MSVC version resolved to None" -> "cannot locate suitable C + # compiler"). windows-2022 is a GitHub-maintained stable image with the older, + # Nuitka-supported VS2022 toolset. Revisit once Nuitka supports VS2026. + os: [ubuntu-latest, macos-latest, windows-2022] runs-on: ${{ matrix.os }}