From eae88295fc23922c7f604141aba53101bc362db2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Muhammet=20=C5=9Eafak?= Date: Wed, 4 Nov 2026 19:58:40 +0300 Subject: [PATCH 1/3] ci(release): publish to NuGet via Trusted Publishing Replace the long-lived NUGET_API_KEY secret with a short-lived key issued by NuGet/login@v1 over GitHub OIDC. Add a workflow_dispatch dry run (verify-trusted-publishing job) that only exercises the login, so the nuget.org policy can be verified without publishing a version. --- .github/workflows/release.yml | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 4a95646..0f72903 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -3,12 +3,15 @@ name: Release on: push: tags: [ "v*" ] + workflow_dispatch: permissions: contents: write + id-token: write jobs: release: + if: github.event_name == 'push' runs-on: ubuntu-latest steps: - uses: actions/checkout@v5 @@ -23,10 +26,16 @@ jobs: - name: Pack run: dotnet pack src/BabelQueue.Artemis/BabelQueue.Artemis.csproj -c Release -o artifacts + - name: NuGet login (Trusted Publishing) + id: nuget-login + uses: NuGet/login@v1 + with: + user: ${{ vars.NUGET_USER }} + - name: Publish to NuGet run: > dotnet nuget push "artifacts/*.nupkg" - --api-key "${{ secrets.NUGET_API_KEY }}" + --api-key "${{ steps.nuget-login.outputs.NUGET_API_KEY }}" --source https://api.nuget.org/v3/index.json --skip-duplicate @@ -34,3 +43,26 @@ jobs: uses: softprops/action-gh-release@v2 with: generate_release_notes: true + + # Manual dry run: exercises only the NuGet Trusted Publishing login (no test, pack, push + # or GitHub release), so the nuget.org policy can be verified without cutting a version. + verify-trusted-publishing: + if: github.event_name == 'workflow_dispatch' + runs-on: ubuntu-latest + steps: + - name: NuGet login (Trusted Publishing) + id: nuget-login + uses: NuGet/login@v1 + with: + user: ${{ vars.NUGET_USER }} + + - name: Verify the short-lived API key was issued + env: + KEY: ${{ steps.nuget-login.outputs.NUGET_API_KEY }} + run: | + if [ -n "$KEY" ]; then + echo "Trusted Publishing login OK" + else + echo "Trusted Publishing login returned an empty API key" >&2 + exit 1 + fi From e8efc074744386874f68deccaf6ca45eee77aa99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Muhammet=20=C5=9Eafak?= Date: Wed, 4 Nov 2026 20:01:56 +0300 Subject: [PATCH 2/3] ci(release): split NuGet publish into a least-privilege job Build, test and pack now run with read-only permissions and no OIDC token. The .nupkg files (and the conformance results asset where used) are handed over as artifacts to a separate publish job that needs the build job and alone holds id-token: write and contents: write for NuGet/login, the push and the GitHub release. The dry-run job only gets id-token: write. --- .github/workflows/release.yml | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 0f72903..c4faadb 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -6,8 +6,7 @@ on: workflow_dispatch: permissions: - contents: write - id-token: write + contents: read jobs: release: @@ -26,6 +25,30 @@ jobs: - name: Pack run: dotnet pack src/BabelQueue.Artemis/BabelQueue.Artemis.csproj -c Release -o artifacts + - name: Upload packages + uses: actions/upload-artifact@v4 + with: + name: nupkg + path: artifacts/*.nupkg + if-no-files-found: error + retention-days: 1 + + # Runs only after build + tests passed. The only job (besides the dry run) that can request + # an OIDC token, so third-party build/test code never sees ACTIONS_ID_TOKEN_REQUEST_*. + publish: + if: github.event_name == 'push' + needs: release + runs-on: ubuntu-latest + permissions: + contents: write + id-token: write + steps: + - name: Download packages + uses: actions/download-artifact@v4 + with: + name: nupkg + path: artifacts + - name: NuGet login (Trusted Publishing) id: nuget-login uses: NuGet/login@v1 @@ -49,6 +72,8 @@ jobs: verify-trusted-publishing: if: github.event_name == 'workflow_dispatch' runs-on: ubuntu-latest + permissions: + id-token: write steps: - name: NuGet login (Trusted Publishing) id: nuget-login From 97a59bacdda4a300dfd33a89714310d2d91273c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Muhammet=20=C5=9Eafak?= Date: Wed, 4 Nov 2026 20:03:56 +0300 Subject: [PATCH 3/3] ci(release): hand symbol packages to the publish job All csproj files emit a .snupkg next to the .nupkg, and dotnet nuget push picks up the sibling symbol package. Upload both globs so the publish job pushes the same set the old single job did. --- .github/workflows/release.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c4faadb..21b4c67 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -29,7 +29,10 @@ jobs: uses: actions/upload-artifact@v4 with: name: nupkg - path: artifacts/*.nupkg + # Same set the old inline push sent: dotnet nuget push also picks up the sibling .snupkg. + path: | + artifacts/*.nupkg + artifacts/*.snupkg if-no-files-found: error retention-days: 1