From b98589d3c7230836e4fb4b9d5f1db83f2ffc7776 Mon Sep 17 00:00:00 2001 From: Laurent Trinques Date: Fri, 4 Sep 2026 10:57:10 +0200 Subject: [PATCH] CI(windows): drop Qt5 build, sign Qt6 MSI, freeze legacy Qt5 nightly assets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit windows-build.yml: - Remove the Qt5 build job (build-windows). Qt6 is now the sole Windows track built in CI. - publish-nightly-assets: only delete/replace .exe and .zip assets tagged "qt6" on the nightly release. Assets without "qt6" in the name (the last Qt5 build ever published) are left untouched and stay downloadable indefinitely as a frozen legacy build. - Release notes: drop the "Try Qt6 — soon the only track" notice, add a line explaining the Qt5 (frozen) vs Qt6 (maintained) split. windows-msi.yml: - Matrix reduced to the single "qt6" entry (flavor string kept as "qt6", not renamed, since it seeds the MSI ProductCode and a rename would break upgrade detection for existing installs). - Sign the Qt6 MSI via SignPath (previously Qt5-only). Guard is now just the upstream-repo fork check; no per-flavor exclusion left. - deploy-pages: also detect legacy (non-"qt6") release assets and pass them to generate-page.py as LEGACY_INSTALLER_URL / LEGACY_PORTABLE_URL / LEGACY_MSI_URL. generate-page.py: - Drop the old Qt5/Qt6 dual-track rendering; INSTALLER_URL / PORTABLE_URL / MSI_URL now point at the Qt6 build directly. - Add an optional "Windows — x86_64 — Qt5 (legacy, unmaintained)" card, rendered only when LEGACY_* URLs are set, with a frozen/ no-longer-updated notice. Before merging: manually trigger the current (pre-merge) "Windows Build" + "Windows MSI" workflows once to publish an up-to-date, signed Qt5 snapshot — that run becomes the frozen legacy reference, since the Qt5 job won't exist to re-run afterwards. No changes to QElectroTech.wxs (Qt5/Qt6-agnostic, only QtPlatformArgs varies and is already handled at the CI level). --- .github/workflows/windows-build.yml | 384 ++-------------------------- .github/workflows/windows-msi.yml | 89 +++---- build-aux/generate-page.py | 75 +++--- 3 files changed, 104 insertions(+), 444 deletions(-) diff --git a/.github/workflows/windows-build.yml b/.github/workflows/windows-build.yml index 9b19a262b..f037f982c 100644 --- a/.github/workflows/windows-build.yml +++ b/.github/workflows/windows-build.yml @@ -11,356 +11,7 @@ concurrency: jobs: # ============================================================================= - # Job 1: Qt5 build (stable track) - # ============================================================================= - build-windows: - runs-on: windows-latest - steps: - - name: Checkout code - uses: actions/checkout@v7 - with: - submodules: recursive - fetch-depth: 0 - - - name: Install MSYS2 - uses: msys2/setup-msys2@v2 - with: - msystem: UCRT64 - update: true - cache: true - install: >- - git - mingw-w64-ucrt-x86_64-ccache - mingw-w64-ucrt-x86_64-gcc - mingw-w64-ucrt-x86_64-cmake - mingw-w64-ucrt-x86_64-ninja - mingw-w64-ucrt-x86_64-qt5-base - mingw-w64-ucrt-x86_64-qt5-svg - mingw-w64-ucrt-x86_64-qt5-tools - mingw-w64-ucrt-x86_64-qt5-translations - mingw-w64-ucrt-x86_64-qt5-pdf - mingw-w64-ucrt-x86_64-sqlite3 - mingw-w64-ucrt-x86_64-pkg-config - mingw-w64-ucrt-x86_64-kwidgetsaddons - mingw-w64-ucrt-x86_64-kcoreaddons - mingw-w64-ucrt-x86_64-extra-cmake-modules - mingw-w64-ucrt-x86_64-nsis - mingw-w64-ucrt-x86_64-angleproject - - - name: Cache ccache - uses: actions/cache@v5 - with: - path: C:\Users\runneradmin\AppData\Local\ccache - key: ccache-windows-${{ github.ref_name }}-${{ github.sha }} - restore-keys: | - ccache-windows-${{ github.ref_name }}- - ccache-windows- - - - name: Configure ccache - shell: msys2 {0} - run: | - /ucrt64/bin/ccache --set-config=max_size=500M - /ucrt64/bin/ccache --set-config=compression=true - /ucrt64/bin/ccache -z - echo "=== ccache config ===" - /ucrt64/bin/ccache -p - - - name: Patch NSIS Welcome page — fix title font size - shell: msys2 {0} - run: | - set -euo pipefail - WELCOME_NSH=$(find /ucrt64 -path "*/Modern UI 2/Pages/Welcome.nsh" | head -1) - if [ -z "$WELCOME_NSH" ]; then - echo "WARNING: Welcome.nsh not found, skipping font patch" - else - echo "Patching: $WELCOME_NSH" - sed -i '/WelcomePage\.Title\.Font/s/"[0-9]\+" "700"/"10" "700"/' "$WELCOME_NSH" - grep 'WelcomePage.Title.Font' "$WELCOME_NSH" - echo " OK font size patched to 10" - fi - - FINISH_NSH=$(find /ucrt64 -path "*/Modern UI 2/Pages/Finish.nsh" | head -1) - if [ -z "$FINISH_NSH" ]; then - echo "WARNING: Finish.nsh not found, skipping font patch" - else - echo "Patching: $FINISH_NSH" - sed -i '/FinishPage\.Title\.Font/s/"[0-9]\+" "700"/"10" "700"/' "$FINISH_NSH" - grep 'FinishPage.Title.Font' "$FINISH_NSH" - echo " OK font size patched to 10" - fi - - - name: Force Qt5 — remove Qt6 cmake + tools - shell: msys2 {0} - run: | - set -euo pipefail - rm -rf /ucrt64/lib/cmake/Qt6 - pacman -R --noconfirm mingw-w64-ucrt-x86_64-qt6-tools 2>/dev/null || true - echo "=== windeployqt binaries ===" - ls /ucrt64/bin/windeployqt* || echo "NO windeployqt found!" - - - name: Build with cmake - shell: msys2 {0} - run: | - set -euo pipefail - cd "$GITHUB_WORKSPACE" - mkdir build && cd build - NPROC=$(nproc) - echo "Available CPUs: $NPROC" - - cmake -G Ninja \ - -DCMAKE_BUILD_TYPE=Release \ - -DCMAKE_PREFIX_PATH=/ucrt64 \ - -DQt5_DIR=/ucrt64/lib/cmake/Qt5 \ - -DQT_VERSION_MAJOR=5 \ - -DCMAKE_DISABLE_FIND_PACKAGE_Qt6=ON \ - -DPACKAGE_TESTS=OFF \ - -DCMAKE_POLICY_DEFAULT_CMP0077=NEW \ - -DCMAKE_POLICY_VERSION_MINIMUM=3.5 \ - -DQET_EXPORT_PROJECT_DB=ON \ - -DCMAKE_C_COMPILER_LAUNCHER=/ucrt64/bin/ccache \ - -DCMAKE_CXX_COMPILER_LAUNCHER=/ucrt64/bin/ccache \ - -DSQLite3_INCLUDE_DIR=/ucrt64/include \ - -DSQLite3_LIBRARY=/ucrt64/lib/libsqlite3.dll.a \ - .. - ninja -j"$NPROC" - - - name: Show ccache stats - shell: msys2 {0} - run: | - echo "=== ccache statistics ===" - /ucrt64/bin/ccache -s - - - name: Verify exe was built - shell: msys2 {0} - run: | - set -euo pipefail - EXE=$(find "$GITHUB_WORKSPACE/build" -maxdepth 3 -iname "qelectrotech.exe" | head -1) - if [ -z "$EXE" ]; then - echo "ERROR: no qelectrotech.exe found in build/" - find "$GITHUB_WORKSPACE/build" -maxdepth 3 -name "*.exe" || true - exit 1 - fi - SIZE=$(stat -c%s "$EXE") - echo "Exe found: $EXE ($SIZE bytes)" - [ "$SIZE" -gt 100000 ] || { echo "ERROR: exe too small"; exit 1; } - - - name: Deploy — copy exe + windeployqt + DLLs - shell: msys2 {0} - run: | - set -euo pipefail - NSIS_ROOT="$GITHUB_WORKSPACE/nsis_root" - FILES="$NSIS_ROOT/files" - BIN="$FILES/bin" - mkdir -p "$BIN" - - EXE=$(find "$GITHUB_WORKSPACE/build" -maxdepth 3 -iname "qelectrotech.exe" | head -1) - echo "Copying exe: $EXE -> $BIN/QElectroTech.exe" - cp "$EXE" "$BIN/QElectroTech.exe" - - cd "$BIN" - /ucrt64/bin/windeployqt-qt5 \ - --release \ - --no-translations \ - --no-compiler-runtime \ - ./QElectroTech.exe || true - - echo "=== 3-pass transitive DLL scan ===" - set +e - for PASS in 1 2 3; do - echo "-- Pass $PASS --" - for bin_file in "$BIN"/*.dll "$BIN"/*.exe "$BIN"/sqldrivers/*.dll "$BIN"/platforms/*.dll "$BIN"/imageformats/*.dll; do - [ -f "$bin_file" ] || continue - while IFS= read -r line; do - dll_path=$(echo "$line" | awk '{print $3}') - [ -f "$dll_path" ] || continue - dll_name=$(basename "$dll_path") - dst="$BIN/$dll_name" - if [ ! -f "$dst" ]; then - cp "$dll_path" "$dst" - echo " Copied (pass $PASS): $dll_name" - fi - done < <(ldd "$bin_file" 2>/dev/null | grep -i '/ucrt64/bin/') - done - done - set -e - - DLL_COUNT=$(find "$BIN" -name "*.dll" | wc -l) - echo "=== $DLL_COUNT DLLs present after scan ===" - ls -lh "$BIN/QElectroTech.exe" || { echo "ERROR: exe missing from bin/"; exit 1; } - [ "$DLL_COUNT" -gt 5 ] || { echo "ERROR: too few DLLs"; exit 1; } - cd "$GITHUB_WORKSPACE" - - cp /ucrt64/bin/libgcc_s_seh-1.dll "$BIN/" - cp /ucrt64/bin/libstdc++-6.dll "$BIN/" - cp /ucrt64/bin/libwinpthread-1.dll "$BIN/" - SQLITE=$(find /ucrt64/bin -name "libsqlite3*.dll" | head -1) - if [ -n "$SQLITE" ]; then - cp "$SQLITE" "$BIN/" - echo "SQLite3 copied: $(basename $SQLITE)" - else - echo "WARNING: libsqlite3 not found in /ucrt64/bin/" - fi - - cp "$GITHUB_WORKSPACE/build-aux/windows/QET64.nsi" "$NSIS_ROOT/" - cp "$GITHUB_WORKSPACE/build-aux/windows/lang_extra.nsh" "$NSIS_ROOT/" - cp "$GITHUB_WORKSPACE/build-aux/windows/lang_extra_fr.nsh" "$NSIS_ROOT/" - cp "$GITHUB_WORKSPACE/build-aux/windows/lang_extra_missing.nsh" "$NSIS_ROOT/" - cp -r "$GITHUB_WORKSPACE/build-aux/windows/nsis_base/." "$NSIS_ROOT/" - - curl -fsSL \ - "https://raw.githubusercontent.com/qelectrotech/qelectrotech-source-mirror/refs/heads/master/misc/Lancer%20QET.bat" \ - -o "$FILES/Lancer QET.bat" - cp -r "$GITHUB_WORKSPACE/elements" "$FILES/elements" || true - cp -r "$GITHUB_WORKSPACE/titleblocks" "$FILES/titleblocks" || true - cp -r "$GITHUB_WORKSPACE/examples" "$FILES/examples" || true - cp -r "$GITHUB_WORKSPACE/fonts" "$FILES/fonts" || true - - cp -r "$GITHUB_WORKSPACE/lang" "$FILES/lang" || true - find "$GITHUB_WORKSPACE/build" -name "*.qm" -exec cp {} "$FILES/lang/" \; 2>/dev/null || true - echo "=== .qm files in files/lang/ ===" - ls "$FILES/lang/"*.qm 2>/dev/null | wc -l || echo "0 .qm files" - - for f in LICENSE ChangeLog CREDIT README ELEMENTS.LICENSE; do - cp "$GITHUB_WORKSPACE/$f" "$FILES/$f" 2>/dev/null || true - done - - echo "=== Verification of key files in files/ ===" - for f in LICENSE ChangeLog CREDIT README ELEMENTS.LICENSE \ - qet_uninstall_file_associations.reg register_filetypes.bat "Lancer QET.bat"; do - [ -f "$FILES/$f" ] \ - && echo " OK : $f" \ - || echo " MISSING: $f" - done - for d in ico elements lang titleblocks fonts examples bin; do - [ -d "$FILES/$d" ] \ - && echo " OK : $d/" \ - || echo " MISSING: $d/" - done - - - name: Extract version for installer name - shell: msys2 {0} - id: qet_version - run: | - set -euo pipefail - - GITCOMMIT=$(git -C "$GITHUB_WORKSPACE" rev-parse --short HEAD) - - A=$(git -C "$GITHUB_WORKSPACE" rev-list HEAD --count) - HEAD=$(( A + 473 )) - - VERSION=$(grep 'return QVersionNumber{' "$GITHUB_WORKSPACE/sources/qetversion.cpp" \ - | head -1 \ - | awk -F '{' '{ print $2 }' \ - | awk -F '}' '{ print $1 }' \ - | sed -e 's/,/./g' -e 's/ //g') - [ -z "$VERSION" ] && VERSION="dev" - - FULL_VERSION="${VERSION}-r${HEAD}-${GITCOMMIT}_x86_64-win64" - echo "version=$FULL_VERSION" >> "$GITHUB_OUTPUT" - echo "base_version=$VERSION" >> "$GITHUB_OUTPUT" - echo "gitcommit=$GITCOMMIT" >> "$GITHUB_OUTPUT" - echo "head=$HEAD" >> "$GITHUB_OUTPUT" - echo "VERSION : $VERSION" - echo "GITCOMMIT : $GITCOMMIT" - echo "HEAD (rev) : $HEAD" - echo "FULL : $FULL_VERSION" - - - name: Patch QET64.nsi — version + exe name + absolute paths - shell: msys2 {0} - run: | - set -euo pipefail - VERSION="${{ steps.qet_version.outputs.version }}" - NSI="$GITHUB_WORKSPACE/nsis_root/QET64.nsi" - FILES_WIN=$(cygpath -w "$GITHUB_WORKSPACE/nsis_root/files") - SCRIPT="$GITHUB_WORKSPACE/build-aux/windows/patch_nsi.py" - - python3 "$SCRIPT" "$NSI" "$VERSION" "$FILES_WIN" - - echo "=== Verification ===" - grep 'SOFT_VERSION' "$NSI" | head -1 - grep -m2 'nsis_root' "$NSI" | head -2 - echo "=== Contents of nsis_root/files/ ===" - ls "$GITHUB_WORKSPACE/nsis_root/files/" - - - name: Build NSIS installer - shell: msys2 {0} - run: | - set -euo pipefail - NSIS_ROOT="$GITHUB_WORKSPACE/nsis_root" - cd "$NSIS_ROOT" - echo "=== CWD : $(pwd) ===" - MSYS2_ARG_CONV_EXCL="*" makensis /V4 QET64.nsi - RC=$? - echo "=== Contents of nsis_root after makensis ===" - ls "$NSIS_ROOT/" - [ $RC -eq 0 ] || { echo "ERROR: makensis failed (exit $RC)"; exit 1; } - - - name: Move installer to dist/ - shell: msys2 {0} - run: | - set -euo pipefail - mkdir -p "$GITHUB_WORKSPACE/dist" - INSTALLER=$(find "$GITHUB_WORKSPACE/nsis_root" -maxdepth 1 -iname "installer_*.exe" | head -1) - if [ -z "$INSTALLER" ]; then - echo "ERROR: no installer .exe found in nsis_root/" - ls "$GITHUB_WORKSPACE/nsis_root/" - exit 1 - fi - echo "Moving: $INSTALLER -> dist/" - mv "$INSTALLER" "$GITHUB_WORKSPACE/dist/" - - - name: Upload build logs on failure - if: failure() - uses: actions/upload-artifact@v7 - with: - name: build-logs - path: | - build/CMakeFiles/*.log - nsis_root/files/bin/ - if-no-files-found: warn - - - name: Zip portable (readytouse) - id: zip_portable - shell: pwsh - run: | - $version = "${{ steps.qet_version.outputs.base_version }}" - $head = "${{ steps.qet_version.outputs.head }}" - $zipName = "qelectrotech-${version}+git${head}-x86-win64-readytouse.zip" - $src = "$env:GITHUB_WORKSPACE\nsis_root\files" - $dst = "$env:GITHUB_WORKSPACE\dist\$zipName" - $7z = "C:\Program Files\7-Zip\7z.exe" - - New-Item -ItemType Directory -Force -Path "$env:GITHUB_WORKSPACE\dist" | Out-Null - & $7z a -tzip -mx=5 -mmt=on $dst "$src\*" - if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } - - $sizeMB = [math]::Round((Get-Item $dst).Length / 1MB, 1) - Write-Output "ZIP created: $zipName ($sizeMB MB)" - "zip_name=$zipName" | Out-File -FilePath $env:GITHUB_OUTPUT -Encoding utf8 -Append - - - name: Upload portable (files/ without installer) - uses: actions/upload-artifact@v7 - with: - name: qelectrotech-${{ steps.qet_version.outputs.base_version }}+git${{ steps.qet_version.outputs.head }}-x86-win64-readytouse - path: dist/${{ steps.zip_portable.outputs.zip_name }} - retention-days: 40 - - - name: Upload NSIS installer - uses: actions/upload-artifact@v7 - with: - name: qelectrotech-windows-installer - path: dist/Installer_*.exe - retention-days: 40 - - - name: Upload portable (nom fixe pour le workflow MSI) - uses: actions/upload-artifact@v7 - with: - name: qelectrotech-windows-portable - path: nsis_root/files/ - retention-days: 40 - - # ============================================================================= - # Job 2: Qt6 build (EXPERIMENTAL track) + # Job 1: Windows build (Qt6/KF6 — sole track since the Qt5 track was removed) # # Version label: the C++ source (sources/qetversion.cpp) intentionally stays # Qt-agnostic — QT_VERSION-based detection inside the binary proved unreliable @@ -368,6 +19,9 @@ jobs: # certainty which Qt major version it configured (-DQT_VERSION_MAJOR=6), so it # is safe to label the *package* "0.200.1" at this level, without touching # QetVersion::currentVersion() or making the binary self-detect its Qt build. + # + # Job id kept as "build-windows-qt6" (not renamed to "build-windows") in case + # branch protection / required status checks reference this exact job name. # ============================================================================= build-windows-qt6: runs-on: windows-latest @@ -598,8 +252,8 @@ jobs: # Deliberately NOT parsed from sources/qetversion.cpp: the CI job # already knows for certain it configured Qt6 (-DQT_VERSION_MAJOR=6), - # so the "0.200.1" experimental-track label is set here explicitly - # rather than relying on in-binary Qt version detection. + # so the "0.200.1" version label is set here explicitly rather than + # relying on in-binary Qt version detection. VERSION="0.200.1" FULL_VERSION="${VERSION}-qt6-r${HEAD}-${GITCOMMIT}_x86_64-win64" @@ -708,43 +362,47 @@ jobs: retention-days: 40 # --------------------------------------------------------------------------- - # Job 3 : Publie les assets nightly (exe + zip, Qt5 et Qt6) sur la release + # Job 2 : Publie les assets nightly (exe + zip) sur la release # Ne tourne que sur push master (pas sur les PRs) # --------------------------------------------------------------------------- publish-nightly-assets: - needs: [build-windows, build-windows-qt6] + needs: [build-windows-qt6] runs-on: ubuntu-latest if: github.event_name != 'pull_request' permissions: contents: write steps: - - name: Download installer artifacts (Qt5 + Qt6) + - name: Download installer artifact uses: actions/download-artifact@v8 with: pattern: qelectrotech-windows-installer* path: downloaded/installer/ merge-multiple: true - - name: Download portable artifacts (Qt5 + Qt6) + - name: Download portable artifact uses: actions/download-artifact@v8 with: pattern: qelectrotech-*-readytouse path: downloaded/portable/ merge-multiple: true - - name: Delete old nightly assets (.exe and .zip) + # Only Qt6-tagged assets are deleted/replaced here. Assets without "qt6" + # in the name are the frozen Qt5 legacy build (last one ever published, + # before the Qt5 CI job was removed) — deliberately left untouched so + # they stay downloadable indefinitely instead of disappearing. + - name: Delete old nightly Qt6 assets (.exe and .zip) env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} REPO: ${{ github.repository }} run: | gh release view nightly --repo "$REPO" --json assets \ - --jq '.assets[] | select(.name | test("\\.(exe|zip)$")) | .name' \ + --jq '.assets[] | select(.name | test("qt6")) | select(.name | test("\\.(exe|zip)$")) | .name' \ | while read -r name; do - echo "Deleting old asset: $name" + echo "Deleting old Qt6 asset: $name" gh release delete-asset nightly "$name" --repo "$REPO" --yes done - echo "Old .exe and .zip assets deleted." + echo "Old Qt6 .exe and .zip assets deleted (legacy Qt5 assets left untouched)." - name: Update nightly release uses: softprops/action-gh-release@v3 @@ -763,9 +421,7 @@ jobs: > ⚠️ This is a development version; it introduces new features you want, but may cause bugs that have not yet been identified yet. > For stable releases, see the [Releases page](https://github.com/${{ github.repository }}/releases). - > 🧪 **Try Qt6 — soon the only track.** Files tagged `-qt6-` are built against Qt6. The Qt5 - > builds above will be removed from Windows CI soon; please switch and test Qt6 now — report - > any issue clearly labelled "Qt6". + > 🗄️ Files without `-qt6-` in the name are the last Qt5 build ever published (frozen, unmaintained). Files tagged `-qt6-` are the actively maintained build. prerelease: true make_latest: false files: | @@ -774,4 +430,4 @@ jobs: token: ${{ secrets.GITHUB_TOKEN }} # GitHub Pages is generated and deployed by windows-msi.yml - # after the MSI upload, so that all URLs (exe/zip/msi, Qt5+Qt6) are known. + # after the MSI upload, so that all URLs (exe/zip/msi) are known. diff --git a/.github/workflows/windows-msi.yml b/.github/workflows/windows-msi.yml index b88d277a7..73d57e7ab 100644 --- a/.github/workflows/windows-msi.yml +++ b/.github/workflows/windows-msi.yml @@ -25,18 +25,17 @@ jobs: strategy: fail-fast: false + # Single-entry matrix kept on purpose (rather than flattening the job): + # matrix.flavor is used as part of the MSI ProductCode seed, so changing + # it would generate a new ProductCode and break upgrade detection for + # existing installs. Keeping "qt6" here preserves continuity. matrix: include: - - flavor: qt5 - portable_artifact: qelectrotech-windows-portable - version_source: qetversion # parsed from sources/qetversion.cpp - label_suffix: "" - experimental: false - flavor: qt6 portable_artifact: qelectrotech-windows-portable-qt6 version_source: hardcoded # see note in "Extract version" step label_suffix: "-qt6" - experimental: true + experimental: false permissions: contents: write @@ -44,7 +43,6 @@ jobs: id-token: write # Required by SignPath outputs: - qt5_msi: ${{ steps.export.outputs.msi_name_qt5 }} qt6_msi: ${{ steps.export.outputs.msi_name_qt6 }} steps: @@ -72,14 +70,12 @@ jobs: # ---------------------------------------------------------------- # 3. Extract version - # Qt5: parsed from sources/qetversion.cpp (single source of truth - # for the software's own reported version). # Qt6: hardcoded "0.200.1" here — deliberately NOT parsed from the # binary/source, since Qt-version self-detection inside the # compiled code proved unreliable (see commit e1aa65f). The CI # matrix entry already knows for certain which flavor it is - # packaging, so the experimental-track label is set explicitly - # at the packaging level instead. + # packaging, so the version label is set explicitly at the + # packaging level instead. # ---------------------------------------------------------------- - name: Extract version id: version @@ -125,10 +121,9 @@ jobs: Write-Host "Version MSI : $verMsi" Write-Host "Version display : $verDisplay" - # Qt platform argument: only Qt5 needs the QTBUG-83161 font-rendering - # workaround (GDI backend). Qt6 uses DirectWrite by default and does - # not need it. Computed once here so both the bundled "Lancer QET.bat" - # and the MSI shortcuts (wix build -d QtPlatformArgs=...) stay in sync. + # Qt platform argument: kept from the Qt5 era (QTBUG-83161 + # font-rendering workaround, GDI backend). Qt6 uses DirectWrite by + # default and does not need it, so this always resolves empty now. if ("${{ matrix.flavor }}" -eq "qt5") { $qtArgs = "-platform windows:fontengine=freetype" } else { @@ -228,10 +223,10 @@ jobs: Write-Host "Lancer QET.bat replaced for MSI installation (qtArgs: '$qtArgs')." # ---------------------------------------------------------------- - # 9. Build the MSI (unsigned) - # Qt6 (experimental) uses a distinct ProductCode seed so it never - # collides with / upgrades over the Qt5 MSI — they must be able to - # coexist as clearly separate installs. + # 9. Build the MSI (unsigned at this stage — signing happens below) + # Qt6 keeps its own ProductCode seed (distinct from the retired Qt5 + # MSI), so a machine that still has the old Qt5 MSI installed gets a + # separate, coexisting install rather than an unexpected upgrade. # ---------------------------------------------------------------- - name: Build MSI shell: pwsh @@ -289,12 +284,13 @@ jobs: retention-days: 1 if-no-files-found: error - # Qt6 stays on the unsigned/experimental track (no signing request for - # this flavor), and forks never have the SignPath secrets, so guard on - # both: only qt5, and only in the upstream repo. + # Qt6 is now signed too (previously excluded while Qt5 was the stable + # track and Qt6 was experimental-only). The remaining guard is the fork + # check: forks never have the SignPath secrets, and a fork-originated + # PR/run must never attempt a signing request. # (cf. DieterMayerOSS:fix/msi-signing-fork-guard, d3f60c88) - name: Sign MSI via SignPath - if: matrix.flavor == 'qt5' && github.repository == 'qelectrotech/qelectrotech-source-mirror' + if: github.repository == 'qelectrotech/qelectrotech-source-mirror' uses: signpath/github-action-submit-signing-request@v2 with: api-token: ${{ secrets.SIGNPATH_API_TOKEN }} @@ -314,15 +310,14 @@ jobs: retention-days: 40 if-no-files-found: error - - name: Delete old nightly .msi asset for this flavor + - name: Delete old nightly .msi asset env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} REPO: ${{ github.repository }} run: | - $pattern = if ("${{ matrix.flavor }}" -eq "qt6") { "-qt6.*\\.msi$" } else { "\\.msi$" } + $pattern = "-qt6.*\\.msi$" $names = gh release view nightly --repo $env:REPO --json assets --jq ".assets[] | select(.name | test(`"$pattern`")) | .name" foreach ($name in ($names -split "`n" | Where-Object { $_ })) { - if ("${{ matrix.flavor }}" -eq "qt5" -and $name -match "-qt6") { continue } Write-Host "Deleting old asset: $name" gh release delete-asset nightly $name --repo $env:REPO --yes } @@ -342,11 +337,7 @@ jobs: shell: pwsh run: | $name = "$env:MSI_NAME" - if ("${{ matrix.flavor }}" -eq "qt6") { - echo "msi_name_qt6=$name" >> $env:GITHUB_OUTPUT - } else { - echo "msi_name_qt5=$name" >> $env:GITHUB_OUTPUT - } + echo "msi_name_qt6=$name" >> $env:GITHUB_OUTPUT - name: Summary if: always() @@ -354,11 +345,11 @@ jobs: run: | Write-Host "=== MSI build summary (${{ matrix.flavor }}) ===" Write-Host "Version : ${{ steps.version.outputs.VERSION_DISPLAY }}" - Write-Host "Experimental : ${{ matrix.experimental }}" + Write-Host "Signed : true" # --------------------------------------------------------------------------- - # Job 2 : Génère et déploie la page GitHub Pages une fois les DEUX MSI - # (Qt5 + Qt6) publiés, pour que toutes les URLs soient connues. + # Job 2 : Génère et déploie la page GitHub Pages une fois le MSI publié, + # pour que toutes les URLs soient connues. # --------------------------------------------------------------------------- deploy-pages: needs: build-msi @@ -388,13 +379,17 @@ jobs: ASSETS=$(gh release view nightly --repo "$REPO" --json assets --jq '.assets[].name') - EXE_NAME=$(echo "$ASSETS" | grep -v 'qt6' | grep '\.exe$' | head -1) - ZIP_NAME=$(echo "$ASSETS" | grep -v 'qt6' | grep '\.zip$' | head -1) - MSI_NAME=$(echo "$ASSETS" | grep -v 'qt6' | grep '\.msi$' | head -1 || echo "") + EXE_NAME=$(echo "$ASSETS" | grep 'qt6' | grep '\.exe$' | head -1) + ZIP_NAME=$(echo "$ASSETS" | grep 'qt6' | grep '\.zip$' | head -1) + MSI_NAME=$(echo "$ASSETS" | grep 'qt6' | grep '\.msi$' | head -1 || echo "") - EXE_QT6_NAME=$(echo "$ASSETS" | grep 'qt6' | grep '\.exe$' | head -1 || echo "") - ZIP_QT6_NAME=$(echo "$ASSETS" | grep 'qt6' | grep '\.zip$' | head -1 || echo "") - MSI_QT6_NAME=$(echo "$ASSETS" | grep 'qt6' | grep '\.msi$' | head -1 || echo "") + # Legacy Qt5 assets (no "qt6" in the name): last ever published, + # frozen — windows-build.yml/windows-msi.yml no longer delete or + # replace these, so they keep pointing at the same files release + # after release. Rendered as a separate "legacy" section if present. + LEGACY_EXE_NAME=$(echo "$ASSETS" | grep -v 'qt6' | grep '\.exe$' | head -1 || echo "") + LEGACY_ZIP_NAME=$(echo "$ASSETS" | grep -v 'qt6' | grep '\.zip$' | head -1 || echo "") + LEGACY_MSI_NAME=$(echo "$ASSETS" | grep -v 'qt6' | grep '\.msi$' | head -1 || echo "") BASE="https://github.com/$REPO/releases/download/nightly" INSTALLER_URL="$BASE/$EXE_NAME" @@ -402,12 +397,12 @@ jobs: MSI_URL="" [ -n "$MSI_NAME" ] && MSI_URL="$BASE/$MSI_NAME" - INSTALLER_QT6_URL="" - PORTABLE_QT6_URL="" - MSI_QT6_URL="" - [ -n "$EXE_QT6_NAME" ] && INSTALLER_QT6_URL="$BASE/$EXE_QT6_NAME" - [ -n "$ZIP_QT6_NAME" ] && PORTABLE_QT6_URL="$BASE/$ZIP_QT6_NAME" - [ -n "$MSI_QT6_NAME" ] && MSI_QT6_URL="$BASE/$MSI_QT6_NAME" + LEGACY_INSTALLER_URL="" + LEGACY_PORTABLE_URL="" + LEGACY_MSI_URL="" + [ -n "$LEGACY_EXE_NAME" ] && LEGACY_INSTALLER_URL="$BASE/$LEGACY_EXE_NAME" + [ -n "$LEGACY_ZIP_NAME" ] && LEGACY_PORTABLE_URL="$BASE/$LEGACY_ZIP_NAME" + [ -n "$LEGACY_MSI_NAME" ] && LEGACY_MSI_URL="$BASE/$LEGACY_MSI_NAME" SHA="${{ github.event.workflow_run.head_sha || github.sha }}" SHORT="${SHA:0:7}" @@ -417,7 +412,7 @@ jobs: export DATE SHORT REPO SHA RUN_URL RUN_NUMBER export INSTALLER_URL PORTABLE_URL MSI_URL - export INSTALLER_QT6_URL PORTABLE_QT6_URL MSI_QT6_URL + export LEGACY_INSTALLER_URL LEGACY_PORTABLE_URL LEGACY_MSI_URL python3 source/build-aux/generate-page.py diff --git a/build-aux/generate-page.py b/build-aux/generate-page.py index 6ba06636b..4e0ec98ab 100644 --- a/build-aux/generate-page.py +++ b/build-aux/generate-page.py @@ -2,11 +2,19 @@ """ generate-page.py — Generates gh-pages/index.html for QElectroTech nightly builds. Called from windows-msi.yml deploy-pages job. + Environment variables required: DATE, SHORT, REPO, SHA, RUN_URL, RUN_NUMBER, INSTALLER_URL, PORTABLE_URL, MSI_URL (optional) -Optional (Qt6 track — omitted entirely if empty): - INSTALLER_QT6_URL, PORTABLE_QT6_URL, MSI_QT6_URL + +Optional (frozen Qt5 legacy build — omitted entirely if empty): + LEGACY_INSTALLER_URL, LEGACY_PORTABLE_URL, LEGACY_MSI_URL + +NOTE: Windows Qt5 CI build removed (Qt6 is now the sole track built and +signed going forward). INSTALLER_URL / PORTABLE_URL / MSI_URL point to the +Qt6 build artifacts. The LEGACY_* variables, when set, point to the last +Qt5 nightly assets that were ever published — kept downloadable but frozen +(windows-build.yml no longer regenerates or deletes them). """ import os @@ -20,9 +28,9 @@ installer_url = os.environ.get("INSTALLER_URL", "") portable_url = os.environ.get("PORTABLE_URL", "") msi_url = os.environ.get("MSI_URL", "") -installer_qt6_url = os.environ.get("INSTALLER_QT6_URL", "") -portable_qt6_url = os.environ.get("PORTABLE_QT6_URL", "") -msi_qt6_url = os.environ.get("MSI_QT6_URL", "") +legacy_installer_url = os.environ.get("LEGACY_INSTALLER_URL", "") +legacy_portable_url = os.environ.get("LEGACY_PORTABLE_URL", "") +legacy_msi_url = os.environ.get("LEGACY_MSI_URL", "") msi_block = "" if msi_url: @@ -32,43 +40,44 @@ if msi_url: Windows Installer .msi.msi — for enterprise / GPO deployment """ -# Qt6 section — only rendered if at least one Qt6 asset exists. -qt6_block = "" -if installer_qt6_url or portable_qt6_url or msi_qt6_url: - qt6_msi_btn = "" - if msi_qt6_url: - qt6_msi_btn = f""" - +# Legacy Qt5 section — only rendered if at least one legacy asset exists. +legacy_block = "" +if legacy_installer_url or legacy_portable_url or legacy_msi_url: + legacy_installer_btn = "" + if legacy_installer_url: + legacy_installer_btn = f""" + ⬇ -Windows Installer .msi (Qt6).msi — for enterprise / GPO deployment +Windows Installer (Qt5, legacy).exe — frozen, no longer updated """ - qt6_installer_btn = "" - if installer_qt6_url: - qt6_installer_btn = f""" - + legacy_msi_btn = "" + if legacy_msi_url: + legacy_msi_btn = f""" + ⬇ -Windows Installer (Qt6).exe — includes all dependencies +Windows Installer .msi (Qt5, legacy).msi — frozen, no longer updated """ - qt6_portable_btn = "" - if portable_qt6_url: - qt6_portable_btn = f""" - + legacy_portable_btn = "" + if legacy_portable_url: + legacy_portable_btn = f""" + 📦 -Windows Portable (Qt6).zip — no installation required +Windows Portable (Qt5, legacy).zip — frozen, no longer updated """ - qt6_block = f""" + legacy_block = f"""
-

🧪 Windows — x86_64 — Qt6 track

+

🗃 Windows — x86_64 — Qt5 (legacy, unmaintained)

-🧪 Try Qt6 — soon the only track. The Qt5 builds above -will be removed from Windows CI soon; Qt6 is becoming the sole supported track. -Please switch and test it now — report any issue and mention "Qt6" explicitly. +🗃 Legacy build — frozen, no longer updated. This is the +last Qt5 build published before Windows CI switched to Qt6 only. Kept available for +anyone who still needs it, but it will not receive further fixes or security updates. +Please migrate to the Qt6 build above when you can.
-{qt6_installer_btn} -{qt6_msi_btn} -{qt6_portable_btn} +{legacy_installer_btn} +{legacy_msi_btn} +{legacy_portable_btn}
""" @@ -126,7 +135,7 @@ For production use, download a stab
-

🏹 Windows — x86_64 — Qt5 track

+

🏹 Windows — x86_64

-{qt6_block} +{legacy_block}