diff --git a/.github/workflows/windows-build.yml b/.github/workflows/windows-build.yml index 22d41c27c..725c9cf6e 100644 --- a/.github/workflows/windows-build.yml +++ b/.github/workflows/windows-build.yml @@ -509,6 +509,21 @@ jobs: # --------------------------------------------------------------------------- # Job 2 : Publie les assets nightly (exe + zip) sur la release # Ne tourne que sur push master (pas sur les PRs) + # + # Release tag: "nightly-qt6" (was "nightly"). + # The old "nightly" release became IMMUTABLE after it was republished by + # this job while GitHub's "Release immutability" repo setting was briefly + # enabled (Oct. 2026). Turning the setting off again does not revert a + # release that was published while it was on: assets could no longer be + # added or deleted there, and its tag can never host a new release, even + # after the release itself was deleted. Hence a fresh tag. The old + # "nightly" release (with the frozen Qt5 legacy build) was deleted at the + # same time; its tag stays locked and must not be reused. + # + # Do NOT re-enable "Release immutability" while this single, rolling + # nightly release model is used: the next republish would lock + # "nightly-qt6" the same way. Immutability requires one new release per + # build (draft -> upload assets -> publish). # --------------------------------------------------------------------------- publish-nightly-assets: needs: [build-windows-qt6] @@ -516,6 +531,8 @@ jobs: if: github.event_name != 'pull_request' permissions: contents: write + env: + NIGHTLY_TAG: nightly-qt6 steps: - name: Download installer artifact @@ -532,27 +549,33 @@ jobs: path: downloaded/portable/ merge-multiple: true - # 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) + # Remove the previous .exe/.zip before uploading the new ones. + # On the very first run the release does not exist yet: skip cleanly + # (the default bash shell runs with pipefail, so an unguarded + # `gh release view` on a missing release would fail the step). + - name: Delete old nightly 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("qt6")) | select(.name | test("\\.(exe|zip)$")) | .name' \ + if ! gh release view "$NIGHTLY_TAG" --repo "$REPO" > /dev/null 2>&1; then + echo "Release '$NIGHTLY_TAG' does not exist yet — it will be created by the next step." + exit 0 + fi + gh release view "$NIGHTLY_TAG" --repo "$REPO" --json assets \ + --jq '.assets[] | select(.name | test("\\.(exe|zip)$")) | .name' \ | while read -r name; do - echo "Deleting old Qt6 asset: $name" - gh release delete-asset nightly "$name" --repo "$REPO" --yes + echo "Deleting old asset: $name" + gh release delete-asset "$NIGHTLY_TAG" "$name" --repo "$REPO" --yes done - echo "Old Qt6 .exe and .zip assets deleted (legacy Qt5 assets left untouched)." + echo "Old .exe and .zip assets deleted." - name: Update nightly release uses: softprops/action-gh-release@v3 with: - tag_name: nightly + tag_name: ${{ env.NIGHTLY_TAG }} + # Only used when the tag does not exist yet (first run). + target_commitish: ${{ github.sha }} name: "Nightly Build – Windows" body: | 🔧 **Automated nightly build** — may be unstable. @@ -565,8 +588,6 @@ 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). - - > 🗄️ 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: | diff --git a/.github/workflows/windows-msi.yml b/.github/workflows/windows-msi.yml index 1a9751c4e..0c1bbeb41 100644 --- a/.github/workflows/windows-msi.yml +++ b/.github/workflows/windows-msi.yml @@ -14,6 +14,13 @@ on: required: false default: "" +# Rolling Qt6 nightly release, updated on every build. Replaces the old +# "nightly" tag, which became immutable while the "Release immutability" +# setting was briefly enabled (Oct. 2026) — see the note on +# publish-nightly-assets in windows-build.yml. +env: + NIGHTLY_TAG: nightly-qt6 + jobs: build-msi: name: Build MSI with WiX v7 (${{ matrix.flavor }}) @@ -371,19 +378,32 @@ jobs: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} REPO: ${{ github.repository }} run: | + # The release may not exist yet (e.g. a manual MSI run before + # windows-build.yml has published anything on NIGHTLY_TAG): + # nothing to delete then. + gh release view $env:NIGHTLY_TAG --repo $env:REPO *> $null + if ($LASTEXITCODE -ne 0) { + Write-Host "Release '$env:NIGHTLY_TAG' does not exist yet — nothing to delete." + exit 0 + } $pattern = "-qt6.*\\.msi$" - $names = gh release view nightly --repo $env:REPO --json assets --jq ".assets[] | select(.name | test(`"$pattern`")) | .name" + $names = gh release view $env:NIGHTLY_TAG --repo $env:REPO --json assets --jq ".assets[] | select(.name | test(`"$pattern`")) | .name" foreach ($name in ($names -split "`n" | Where-Object { $_ })) { Write-Host "Deleting old asset: $name" - gh release delete-asset nightly $name --repo $env:REPO --yes + gh release delete-asset $env:NIGHTLY_TAG $name --repo $env:REPO --yes } shell: pwsh + # prerelease / make_latest set explicitly: if this step happens to + # create the release (it does not exist yet), it must not become the + # repository's "Latest" release. - name: Upload MSI to nightly release - if: always() + if: always() && hashFiles('dist/*.msi') != '' uses: softprops/action-gh-release@v3 with: - tag_name: nightly + tag_name: ${{ env.NIGHTLY_TAG }} + prerelease: true + make_latest: false files: dist/*.msi fail_on_unmatched_files: true env: @@ -413,11 +433,11 @@ jobs: runs-on: ubuntu-latest # Regenerate the page whenever the MSI job actually ran (success OR # packaging/signing failure) — not only on full success. - # generate-page.py queries the published assets on the "nightly" - # release itself (line 407: `gh release view nightly --json assets`), - # so it already handles a missing MSI natively (empty MSI_NAME -> no - # MSI button on the page). The page only needs the exe/zip already - # published by windows-build.yml, independent of the MSI's fate. + # The step below queries the published assets on the NIGHTLY_TAG + # release itself, so it already handles a missing MSI natively (empty + # MSI_NAME -> no MSI button on the page). The page only needs the + # exe/zip already published by windows-build.yml, independent of the + # MSI's fate. # Only "skipped"/"cancelled" are excluded: if build-msi never ran at # all (e.g. Windows Build itself failed), there is nothing new to # publish and regenerating the page would be pointless. @@ -447,33 +467,23 @@ jobs: set -euo pipefail REPO="${{ github.repository }}" - ASSETS=$(gh release view nightly --repo "$REPO" --json assets --jq '.assets[].name') + ASSETS=$(gh release view "$NIGHTLY_TAG" --repo "$REPO" --json assets --jq '.assets[].name') - EXE_NAME=$(echo "$ASSETS" | grep 'qt6' | grep '\.exe$' | head -1) - ZIP_NAME=$(echo "$ASSETS" | grep 'qt6' | grep '\.zip$' | head -1) + EXE_NAME=$(echo "$ASSETS" | grep 'qt6' | grep '\.exe$' | head -1 || echo "") + ZIP_NAME=$(echo "$ASSETS" | grep 'qt6' | grep '\.zip$' | head -1 || echo "") MSI_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 "") + if [ -z "$EXE_NAME" ] || [ -z "$ZIP_NAME" ]; then + echo "::error::No Qt6 .exe/.zip found on release '$NIGHTLY_TAG' — refusing to publish a page with dead links." + exit 1 + fi - BASE="https://github.com/$REPO/releases/download/nightly" + BASE="https://github.com/$REPO/releases/download/$NIGHTLY_TAG" INSTALLER_URL="$BASE/$EXE_NAME" PORTABLE_URL="$BASE/$ZIP_NAME" MSI_URL="" [ -n "$MSI_NAME" ] && MSI_URL="$BASE/$MSI_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}" DATE=$(date -u '+%Y-%m-%d %H:%M UTC') @@ -482,7 +492,7 @@ jobs: export DATE SHORT REPO SHA RUN_URL RUN_NUMBER export INSTALLER_URL PORTABLE_URL MSI_URL - export LEGACY_INSTALLER_URL LEGACY_PORTABLE_URL LEGACY_MSI_URL + export NIGHTLY_TAG python3 source/build-aux/generate-page.py diff --git a/build-aux/generate-page.py b/build-aux/generate-page.py index 4e0ec98ab..a268229aa 100644 --- a/build-aux/generate-page.py +++ b/build-aux/generate-page.py @@ -7,30 +7,25 @@ Environment variables required: DATE, SHORT, REPO, SHA, RUN_URL, RUN_NUMBER, INSTALLER_URL, PORTABLE_URL, MSI_URL (optional) -Optional (frozen Qt5 legacy build — omitted entirely if empty): - LEGACY_INSTALLER_URL, LEGACY_PORTABLE_URL, LEGACY_MSI_URL +Optional: + NIGHTLY_TAG tag of the rolling nightly release (default: "nightly-qt6") -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). +NOTE: Qt6 is the sole Windows track. The frozen Qt5 legacy build and the +old "nightly" release that held it were removed in Oct. 2026, after that +release became immutable (see windows-build.yml, publish-nightly-assets). """ import os -date = os.environ.get("DATE", "") -short = os.environ.get("SHORT", "") -repo = os.environ.get("REPO", "") -sha = os.environ.get("SHA", "") -run_url = os.environ.get("RUN_URL", "") -run_number = os.environ.get("RUN_NUMBER", "") +date = os.environ.get("DATE", "") +short = os.environ.get("SHORT", "") +repo = os.environ.get("REPO", "") +sha = os.environ.get("SHA", "") +run_url = os.environ.get("RUN_URL", "") +run_number = os.environ.get("RUN_NUMBER", "") installer_url = os.environ.get("INSTALLER_URL", "") portable_url = os.environ.get("PORTABLE_URL", "") msi_url = os.environ.get("MSI_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", "") +nightly_tag = os.environ.get("NIGHTLY_TAG", "") or "nightly-qt6" msi_block = "" if msi_url: @@ -40,47 +35,6 @@ if msi_url: Windows Installer .msi.msi — for enterprise / GPO deployment """ -# 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 (Qt5, legacy).exe — frozen, no longer updated -""" - legacy_msi_btn = "" - if legacy_msi_url: - legacy_msi_btn = f""" - -⬇ -Windows Installer .msi (Qt5, legacy).msi — frozen, no longer updated -""" - legacy_portable_btn = "" - if legacy_portable_url: - legacy_portable_btn = f""" - -📦 -Windows Portable (Qt5, legacy).zip — frozen, no longer updated -""" - - legacy_block = f""" -
-

🗃 Windows — x86_64 — Qt5 (legacy, unmaintained)

-
-🗃 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. -
-
-{legacy_installer_btn} -{legacy_msi_btn} -{legacy_portable_btn} -
-
""" - html = f""" @@ -146,13 +100,12 @@ For production use, download a stab 📦 Windows Portable.zip — no installation required, extract and run "Lancer QET.bat" - + 📦 All nightly files on GitHubRelease page with checksums -{legacy_block}