From f813ed32d3fe72a11684deb36fbcd582d375f241 Mon Sep 17 00:00:00 2001 From: Stuart Meeks Date: Wed, 26 Aug 2026 12:29:42 +0800 Subject: [PATCH 1/2] ci: run three macOS versions and split the libsecret tests out (#20, #21) Both changes needed NextIteration.Standards amending first (its PR #23), not a local ci.yml edit -- 3.0.1 is explicit that a repo editing its own workflow has forked it, not fixed it. #20: the matrix names macos-13, macos-14 and macos-15 rather than macos-latest. This is the only repo with a Security.framework P/Invoke layer (~1,170 lines across KeychainCredentialManager and KeychainInterop), and macos-latest tests it against whichever release the alias points at today. The Keychain backend is the one place an OS-version behaviour change surfaces as silently missing credentials rather than a build failure. 3.1.1 now permits broadening the matrix without an exception, since broadening cannot cause the failure that clause guards against. #21: the gnome-keyring-daemon setup moves out of the matrix into a test-linux-keyring job. As a Linux-guarded step inside `test` it made test (ubuntu-latest) report one result for two unrelated things -- the portable tests and the libsecret ones. Now the matrix leg reports the libsecret tests skipped (they self-report since #46) and the new job reports them run, so a libsecret regression names itself instead of hiding in a leg that is green for other reasons. The ci gate names the new job, so it cannot pass while the job fails. Verified against the amended template through the exception machinery before pushing: 111 lines each side. Auth.Providers still reconciles unchanged, so the template amendment does not disturb the repos that were already conformant. Co-Authored-By: Claude Opus 5 --- .github/workflows/ci.yml | 59 ++++++++++++++++++++++++++++++---------- CHANGELOG.md | 14 ++++++++++ 2 files changed, 58 insertions(+), 15 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 979763c..1731a1c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -69,7 +69,7 @@ jobs: strategy: fail-fast: false # one platform failing must not hide another's result matrix: - os: [ ubuntu-latest, windows-latest, macos-latest ] + os: [ ubuntu-latest, windows-latest, macos-13, macos-14, macos-15 ] runs-on: ${{ matrix.os }} timeout-minutes: 20 steps: @@ -87,12 +87,48 @@ jobs: key: nuget-${{ runner.os }}-${{ hashFiles('**/Directory.Packages.props', '**/*.csproj') }} restore-keys: nuget-${{ runner.os }}- - # The libsecret backend exercises the Secret Service D-Bus API, which is not - # running on a bare ubuntu runner. Without this the tests short-circuit through - # the "secret-service-unavailable" probe instead of hitting libsecret. Linux-only; - # the macOS leg uses Security.framework and needs nothing. + # Tests run across every shipped TFM (STANDARD.md 2.3). No --no-build: this job + # does not share a filesystem with `build`, and rebuilding is cheaper and less + # fragile than shipping obj/ between jobs. + # `-- --coverage` passes through to Microsoft.Testing.Platform's coverage + # extension (STANDARD.md 2.6). Referencing a collector without invoking it is + # worse than none: it reads as coverage in the dependency list while producing + # no data, which is exactly what coverlet was doing in a sibling repo. + - name: Test + run: dotnet test --configuration Release --verbosity normal -- --coverage + + - name: Upload coverage + if: always() + uses: actions/upload-artifact@v7 + with: + name: coverage-${{ matrix.os }} + path: '**/TestResults/*.coverage' + if-no-files-found: warn + + # Auth-only fifth job (EXCEPTIONS.md 3.1). The libsecret tests need a Secret Service + # daemon, which only exists on one leg of the matrix — so as a guarded step inside `test` + # it made `test (ubuntu-latest)` report one result for two unrelated things. Split out, + # the matrix leg reports the libsecret tests as skipped (they self-report since #46) and + # this job reports them run, so a libsecret regression names itself. + test-linux-keyring: + runs-on: ubuntu-latest + timeout-minutes: 20 + steps: + - uses: actions/checkout@v7 + + - uses: actions/setup-dotnet@v6 + with: + dotnet-version: | + 8.0.x + 10.0.x + + - uses: actions/cache@v6 + with: + path: ~/.nuget/packages + key: nuget-${{ runner.os }}-${{ hashFiles('**/Directory.Packages.props', '**/*.csproj') }} + restore-keys: nuget-${{ runner.os }}- + - name: Install and start gnome-keyring-daemon - if: runner.os == 'Linux' run: | sudo apt-get update sudo apt-get install -y gnome-keyring dbus-x11 libsecret-1-0 libsecret-1-dev @@ -103,13 +139,6 @@ jobs: gnome-keyring-daemon --start --components=secrets & sleep 2 - # Tests run across every shipped TFM (STANDARD.md 2.3). No --no-build: this job - # does not share a filesystem with `build`, and rebuilding is cheaper and less - # fragile than shipping obj/ between jobs. - # `-- --coverage` passes through to Microsoft.Testing.Platform's coverage - # extension (STANDARD.md 2.6). Referencing a collector without invoking it is - # worse than none: it reads as coverage in the dependency list while producing - # no data, which is exactly what coverlet was doing in a sibling repo. - name: Test run: dotnet test --configuration Release --verbosity normal -- --coverage @@ -117,7 +146,7 @@ jobs: if: always() uses: actions/upload-artifact@v7 with: - name: coverage-${{ matrix.os }} + name: coverage-linux-keyring path: '**/TestResults/*.coverage' if-no-files-found: warn @@ -125,7 +154,7 @@ jobs: # know the matrix shape. `if: always()` is essential — without it the gate is skipped # when a dependency fails, and a skipped check reads as success to branch protection. ci: - needs: [ build, test ] + needs: [ build, test, test-linux-keyring ] if: always() runs-on: ubuntu-latest timeout-minutes: 5 diff --git a/CHANGELOG.md b/CHANGELOG.md index b82616e..163fd50 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Changed + +- **CI now runs three macOS versions and splits the libsecret tests into their own job** + (#20, #21). The `test` matrix names `macos-13`, `macos-14` and `macos-15` instead of + `macos-latest`: this is the only repo with a Security.framework P/Invoke layer, and the + alias only ever exercised whichever release it currently points at — the one place an + OS-version behaviour change would surface as silently missing credentials rather than a + build failure. The `gnome-keyring-daemon` setup moves out of the matrix into a + `test-linux-keyring` job, so `test (ubuntu-latest)` no longer reports one result for both + the portable tests and the libsecret ones; the matrix leg now shows the libsecret tests + skipped and the new job shows them run. Both changes required amending + NextIteration.Standards first (its PR #23) rather than editing this workflow locally, which + §3.0.1 calls forking rather than fixing. + ### Breaking - **`ICredentialManager` members now take a `CancellationToken`** (#74). Every member gains a From 91d6d0ad2321c82d546340e343652c8fa8eec356 Mon Sep 17 00:00:00 2001 From: Stuart Meeks Date: Wed, 26 Aug 2026 13:02:12 +0800 Subject: [PATCH 2/2] ci: drop macos-13, which no longer schedules The first attempt used macos-13/14/15 as #20 suggested. macos-13 has no runner: its job sat queued with runner unassigned for over ten minutes while ubuntu, windows, macos-14, macos-15 and test-linux-keyring all completed. GitHub has retired that label, so including it would hang every CI run rather than add coverage. macos-14 and macos-15 both scheduled and passed, which is still two concrete macOS versions where there was one moving alias. Co-Authored-By: Claude Opus 5 --- .github/workflows/ci.yml | 2 +- CHANGELOG.md | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1731a1c..d8259e2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -69,7 +69,7 @@ jobs: strategy: fail-fast: false # one platform failing must not hide another's result matrix: - os: [ ubuntu-latest, windows-latest, macos-13, macos-14, macos-15 ] + os: [ ubuntu-latest, windows-latest, macos-14, macos-15 ] runs-on: ${{ matrix.os }} timeout-minutes: 20 steps: diff --git a/CHANGELOG.md b/CHANGELOG.md index 163fd50..22a1ada 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,8 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - **CI now runs three macOS versions and splits the libsecret tests into their own job** - (#20, #21). The `test` matrix names `macos-13`, `macos-14` and `macos-15` instead of - `macos-latest`: this is the only repo with a Security.framework P/Invoke layer, and the + (#20, #21). The `test` matrix names `macos-14` and `macos-15` instead of `macos-latest`: this is the only repo with a Security.framework P/Invoke layer, and the alias only ever exercised whichever release it currently points at — the one place an OS-version behaviour change would surface as silently missing credentials rather than a build failure. The `gnome-keyring-daemon` setup moves out of the matrix into a @@ -23,6 +22,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 NextIteration.Standards first (its PR #23) rather than editing this workflow locally, which §3.0.1 calls forking rather than fixing. + `macos-13` was in the first attempt, as #20 suggested, and is not in the result: that + runner label no longer schedules. Its job sat queued with no runner assigned for over ten + minutes while every other leg finished, so it would have hung each CI run indefinitely + rather than adding coverage. + ### Breaking - **`ICredentialManager` members now take a `CancellationToken`** (#74). Every member gains a