From 339f9c21803a44d6a31f9a817b1948d95e7424af Mon Sep 17 00:00:00 2001 From: James Maes Date: Sat, 10 Oct 2026 12:48:17 -0500 Subject: [PATCH 1/2] fix(release): keep build and publish on one resource configuration; smoke test what ships v1.0.1 (and the beta.3 rehearsal) crashed at startup with 0xc000027b: the published gclo.pri was 115 KB instead of 2.3 MB and lacked the WinUI theme resources a self-contained unpackaged app must carry. The shared release workflow builds Release (framework-dependent) and then publishes with WindowsAppSDKSelfContained=true into the same obj; the incremental _GenerateProjectPriFileCore target saw its new inputs as older than the index it had already written and skipped, so the stale index shipped. Reproduced from clean with the workflow's exact commands; publish alone, or a Debug build first (the 1.0.0 pipeline), produced the correct index. gclo ships only unpackaged and self-contained, so gclo.csproj now fixes WindowsPackageType=None and WindowsAppSDKSelfContained=true for every build: the Release build and the publish generate the same index (verified from clean with the workflow's sequence; the published app opens). release.yml gains a smoke job between the gates and the shared release: it publishes with the shared workflow's exact command and runs the FlaUI suite against the published gclo.exe (GCLO_UITEST_EXE). The release job needs it, so a build that does not start can no longer be published. RELEASING.md documents the job; v1.0.1 is marked prerelease with a warning per "A broken release". Closes #87 Co-Authored-By: Claude Fable 5.1 Signed-off-by: James Maes --- .github/workflows/release.yml | 57 ++++++++++++++++++++++++++++++++++- docs/RELEASING.md | 5 +++ gclo/gclo.csproj | 7 +++++ 3 files changed, 68 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 467a266..26ec4a3 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -87,9 +87,64 @@ jobs: permissions: contents: read + # The gates test what `dotnet build` produces; this job tests what ships. It + # publishes the app exactly as the shared workflow does (same restore, build + # and publish commands, so the same incremental obj state) and runs the FlaUI + # suite against that published gclo.exe. v1.0.1 passed every gate and crashed + # at startup because its published resource index was stale (#87); nothing is + # published until this passes. + smoke: + name: Smoke test the published app + needs: verify + runs-on: windows-latest + timeout-minutes: 30 + permissions: + contents: read + steps: + - name: Checkout + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + + - name: Set up .NET SDK + uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0 + with: + global-json-file: global.json + cache: true + cache-dependency-path: '**/packages.lock.json' + + - name: Restore (locked) + run: dotnet restore gclo.slnx -p:Platform=x64 --locked-mode + + - name: Build Release (as the shared workflow does before publishing) + run: dotnet build gclo.slnx -c Release -p:Platform=x64 -warnaserror --no-restore + + - name: Publish app (the shared workflow's exact command) + env: + TAG: ${{ github.ref_name }} + run: >- + $version = $env:TAG.Substring(1); + dotnet publish gclo/gclo.csproj -c Release -p:Platform=x64 -r win-x64 --self-contained + "-p:Version=$version" -p:RestoreLockedMode=true -p:WindowsPackageType=None + -p:WindowsAppSDKSelfContained=true -p:PublishTrimmed=false -o publish/app + + - name: Run the UI suite against the published exe + env: + GCLO_UITEST_EXE: ${{ github.workspace }}\publish\app\gclo.exe + GCLO_UITEST_ARTIFACTS: ${{ github.workspace }}\TestResults\smoke + run: dotnet test gclo.UiTests/gclo.UiTests.csproj --logger "trx;LogFileName=smoke.trx" --results-directory TestResults + + - name: Upload smoke test diagnostics + if: failure() + uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2 + with: + name: smoke-test-diagnostics + path: TestResults + if-no-files-found: warn + release: name: Release - needs: [verify, gates] + needs: [verify, gates, smoke] uses: KofTwentyTwo/standards/.github/workflows/release-velopack.yml@e8ab30f8c60df437440ffd786c87678207246f5c # main 2026-10-09 (pre-1.0 draft, standards#5) permissions: contents: write # create and publish the GitHub release diff --git a/docs/RELEASING.md b/docs/RELEASING.md index 3c93e25..a7cfe3a 100644 --- a/docs/RELEASING.md +++ b/docs/RELEASING.md @@ -156,6 +156,11 @@ Both install the same asset, `gclo-cli-win-x64-.zip`, by URL and SHA-25 - **Gates** — the full `ci` workflow, exactly as a pull request runs it, including the FlaUI end-to-end suite. If a gate fails, fix the cause on `main` and cut a new patch version; never reuse the tag. + - **Smoke test the published app** — publishes the app with the shared + workflow's exact commands and runs the FlaUI suite against that published + `gclo.exe`. The gates test what `dotnet build` produces; this tests what + ships (1.0.1 passed every gate and crashed at startup, #87). Nothing is + published until it passes. - **Release** (shared workflow): locked restore, Release build with warnings as errors, tests with the 100% coverage gate, publish, Velopack pack, SBOMs, `SHA256SUMS`, provenance, a **draft** GitHub Release with every asset and diff --git a/gclo/gclo.csproj b/gclo/gclo.csproj index 7aadd78..581e3f4 100644 --- a/gclo/gclo.csproj +++ b/gclo/gclo.csproj @@ -12,6 +12,13 @@ true false true + + None + true enable disable -- 2.54.0 From 074d617351f2f713c95ac2c20ac4340de78d5041 Mon Sep 17 00:00:00 2001 From: James Maes Date: Sat, 10 Oct 2026 12:55:24 -0500 Subject: [PATCH 2/2] ci(release): no NuGet cache in the smoke job (zizmor cache-poisoning) Co-Authored-By: Claude Fable 5.1 Signed-off-by: James Maes --- .github/workflows/release.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 26ec4a3..fa68994 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -106,12 +106,12 @@ jobs: with: persist-credentials: false + # No NuGet cache: a release workflow must not consume runtime-generated + # caches (zizmor cache-poisoning); the locked restore is deterministic. - name: Set up .NET SDK uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0 with: global-json-file: global.json - cache: true - cache-dependency-path: '**/packages.lock.json' - name: Restore (locked) run: dotnet restore gclo.slnx -p:Platform=x64 --locked-mode -- 2.54.0