From b089f3f1ff74f2dd513f255f1e18d80a28957455 Mon Sep 17 00:00:00 2001 From: John Korsnes Date: Sun, 13 Sep 2026 14:06:29 +0200 Subject: [PATCH 1/3] Switch NuGet publishing to OIDC trusted publishing Replaces the long-lived NUGETORGAPIKEY secret with NuGet.org's Trusted Publishing flow: the workflow requests a GitHub OIDC token and exchanges it for a short-lived NuGet API key via NuGet/login@v1 right before pushing. --- .github/workflows/PreRelease.yml | 9 ++++++++- .github/workflows/Release.yml | 10 +++++++++- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/.github/workflows/PreRelease.yml b/.github/workflows/PreRelease.yml index 790d6f5..9e6d532 100644 --- a/.github/workflows/PreRelease.yml +++ b/.github/workflows/PreRelease.yml @@ -6,6 +6,8 @@ on: jobs: build: runs-on: ubuntu-latest + permissions: + id-token: write steps: - uses: actions/checkout@v7 @@ -27,10 +29,15 @@ jobs: uses: gittools/actions/gitversion/execute@v0 with: useConfigFile: true + - name: NuGet login (OIDC → temp API key) + uses: NuGet/login@v1 + id: nuget_login + with: + user: ${{ secrets.NUGET_USER }} - name: Pack & Publish run: dotnet run --project build -- publish env: BUILD_VERSION: ${{ steps.gitversion.outputs.NuGetVersionV2 }}-${{ steps.gitversion.outputs.ShortSha }} BUILD_INFORMATIONAL_VERSION: ${{ steps.gitversion.outputs.informationalVersion }} BUILD_RELEASE_NOTES: https://github.com/${{ github.repository }}/releases/tag/${{ steps.gitversion.outputs.NuGetVersionV2 }} - NUGET_API_KEY: ${{ secrets.NUGETORGAPIKEY }} + NUGET_API_KEY: ${{ steps.nuget_login.outputs.NUGET_API_KEY }} diff --git a/.github/workflows/Release.yml b/.github/workflows/Release.yml index f74abb5..cd7b3ac 100644 --- a/.github/workflows/Release.yml +++ b/.github/workflows/Release.yml @@ -6,6 +6,9 @@ on: jobs: build: runs-on: ubuntu-latest + permissions: + contents: write + id-token: write steps: - uses: actions/checkout@v7 @@ -27,13 +30,18 @@ jobs: uses: gittools/actions/gitversion/execute@v0 with: useConfigFile: true + - name: NuGet login (OIDC → temp API key) + uses: NuGet/login@v1 + id: nuget_login + with: + user: ${{ secrets.NUGET_USER }} - name: Pack & Publish run: dotnet run --project build -- publish env: BUILD_VERSION: ${{ steps.gitversion.outputs.majorMinorPatch }} BUILD_INFORMATIONAL_VERSION: ${{ steps.gitversion.outputs.informationalVersion }} BUILD_RELEASE_NOTES: https://github.com/${{ github.repository }}/releases/tag/${{ steps.gitversion.outputs.majorMinorPatch }} - NUGET_API_KEY: ${{ secrets.NUGETORGAPIKEY }} + NUGET_API_KEY: ${{ steps.nuget_login.outputs.NUGET_API_KEY }} - run: git log $(git describe --tags --abbrev=0)..HEAD --oneline - name: Generate CHANGELOG.md id: releasenotes From d82571ba9e0c17c93a5efc38f4625259ca91089e Mon Sep 17 00:00:00 2001 From: John Korsnes Date: Sun, 13 Sep 2026 14:08:30 +0200 Subject: [PATCH 2/3] Extract shared publish steps into a reusable workflow Release.yml and PreRelease.yml duplicated the checkout/gitversion/ setup-dotnet/NuGet-login/pack-and-publish steps. Move that into publish.yml (workflow_call) so each caller only supplies a prerelease flag and consumes the resulting version outputs. --- .github/workflows/PreRelease.yml | 40 +++----------------- .github/workflows/Release.yml | 46 +++++++---------------- .github/workflows/publish.yml | 63 ++++++++++++++++++++++++++++++++ 3 files changed, 81 insertions(+), 68 deletions(-) create mode 100644 .github/workflows/publish.yml diff --git a/.github/workflows/PreRelease.yml b/.github/workflows/PreRelease.yml index 9e6d532..a14899c 100644 --- a/.github/workflows/PreRelease.yml +++ b/.github/workflows/PreRelease.yml @@ -4,40 +4,10 @@ on: workflow_dispatch: jobs: - build: - runs-on: ubuntu-latest + publish: permissions: id-token: write - - steps: - - uses: actions/checkout@v7 - with: - fetch-depth: 0 - - run: echo "ACTIONS_ALLOW_UNSECURE_COMMANDS=true" >> $GITHUB_ENV - - name: Install GitVersion - uses: gittools/actions/gitversion/setup@v0 - with: - versionSpec: "5.x" - - name: Setup .NET - uses: actions/setup-dotnet@v6 - with: - dotnet-version: | - 10.0.x - 11.0.x - - name: Determine Version - id: gitversion - uses: gittools/actions/gitversion/execute@v0 - with: - useConfigFile: true - - name: NuGet login (OIDC → temp API key) - uses: NuGet/login@v1 - id: nuget_login - with: - user: ${{ secrets.NUGET_USER }} - - name: Pack & Publish - run: dotnet run --project build -- publish - env: - BUILD_VERSION: ${{ steps.gitversion.outputs.NuGetVersionV2 }}-${{ steps.gitversion.outputs.ShortSha }} - BUILD_INFORMATIONAL_VERSION: ${{ steps.gitversion.outputs.informationalVersion }} - BUILD_RELEASE_NOTES: https://github.com/${{ github.repository }}/releases/tag/${{ steps.gitversion.outputs.NuGetVersionV2 }} - NUGET_API_KEY: ${{ steps.nuget_login.outputs.NUGET_API_KEY }} + uses: ./.github/workflows/publish.yml + with: + prerelease: true + secrets: inherit diff --git a/.github/workflows/Release.yml b/.github/workflows/Release.yml index cd7b3ac..57118d1 100644 --- a/.github/workflows/Release.yml +++ b/.github/workflows/Release.yml @@ -4,50 +4,30 @@ on: workflow_dispatch: jobs: - build: + publish: + permissions: + id-token: write + uses: ./.github/workflows/publish.yml + with: + prerelease: false + secrets: inherit + + release: + needs: publish runs-on: ubuntu-latest permissions: contents: write - id-token: write steps: - uses: actions/checkout@v7 with: fetch-depth: 0 - - run: echo "ACTIONS_ALLOW_UNSECURE_COMMANDS=true" >> $GITHUB_ENV - - name: Install GitVersion - uses: gittools/actions/gitversion/setup@v0 - with: - versionSpec: "5.x" - - name: Setup .NET - uses: actions/setup-dotnet@v6 - with: - dotnet-version: | - 10.0.x - 11.0.x - - name: Determine Version - id: gitversion - uses: gittools/actions/gitversion/execute@v0 - with: - useConfigFile: true - - name: NuGet login (OIDC → temp API key) - uses: NuGet/login@v1 - id: nuget_login - with: - user: ${{ secrets.NUGET_USER }} - - name: Pack & Publish - run: dotnet run --project build -- publish - env: - BUILD_VERSION: ${{ steps.gitversion.outputs.majorMinorPatch }} - BUILD_INFORMATIONAL_VERSION: ${{ steps.gitversion.outputs.informationalVersion }} - BUILD_RELEASE_NOTES: https://github.com/${{ github.repository }}/releases/tag/${{ steps.gitversion.outputs.majorMinorPatch }} - NUGET_API_KEY: ${{ steps.nuget_login.outputs.NUGET_API_KEY }} - run: git log $(git describe --tags --abbrev=0)..HEAD --oneline - name: Generate CHANGELOG.md id: releasenotes run: | gh api repos/$GITHUB_REPOSITORY/releases/generate-notes \ - -f tag_name="${{ steps.gitversion.outputs.majorMinorPatch }}" \ + -f tag_name="${{ needs.publish.outputs.version }}" \ -q .body > CHANGELOG.md echo -e "\n\n" >> CHANGELOG.md git log $(git describe --tags --abbrev=0)..HEAD --oneline >> CHANGELOG.md @@ -59,8 +39,8 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: - tag_name: ${{ steps.gitversion.outputs.majorMinorPatch }} - release_name: Release ${{ steps.gitversion.outputs.majorMinorPatch }} + tag_name: ${{ needs.publish.outputs.version }} + release_name: Release ${{ needs.publish.outputs.version }} body_path: CHANGELOG.md draft: false prerelease: false diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 0000000..a40183a --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,63 @@ +name: Publish + +on: + workflow_call: + inputs: + prerelease: + required: true + type: boolean + outputs: + version: + description: The published package version (tag-friendly) + value: ${{ jobs.build.outputs.version }} + informational_version: + value: ${{ jobs.build.outputs.informational_version }} + +jobs: + build: + runs-on: ubuntu-latest + + outputs: + version: ${{ steps.version.outputs.value }} + informational_version: ${{ steps.gitversion.outputs.informationalVersion }} + + steps: + - uses: actions/checkout@v7 + with: + fetch-depth: 0 + - run: echo "ACTIONS_ALLOW_UNSECURE_COMMANDS=true" >> $GITHUB_ENV + - name: Install GitVersion + uses: gittools/actions/gitversion/setup@v0 + with: + versionSpec: "5.x" + - name: Setup .NET + uses: actions/setup-dotnet@v6 + with: + dotnet-version: | + 10.0.x + 11.0.x + - name: Determine Version + id: gitversion + uses: gittools/actions/gitversion/execute@v0 + with: + useConfigFile: true + - name: Resolve package version + id: version + run: | + if [ "${{ inputs.prerelease }}" = "true" ]; then + echo "value=${{ steps.gitversion.outputs.NuGetVersionV2 }}-${{ steps.gitversion.outputs.ShortSha }}" >> "$GITHUB_OUTPUT" + else + echo "value=${{ steps.gitversion.outputs.majorMinorPatch }}" >> "$GITHUB_OUTPUT" + fi + - name: NuGet login (OIDC → temp API key) + uses: NuGet/login@v1 + id: nuget_login + with: + user: ${{ secrets.NUGET_USER }} + - name: Pack & Publish + run: dotnet run --project build -- publish + env: + BUILD_VERSION: ${{ steps.version.outputs.value }} + BUILD_INFORMATIONAL_VERSION: ${{ steps.gitversion.outputs.informationalVersion }} + BUILD_RELEASE_NOTES: https://github.com/${{ github.repository }}/releases/tag/${{ steps.version.outputs.value }} + NUGET_API_KEY: ${{ steps.nuget_login.outputs.NUGET_API_KEY }} From 7d10a2cd3c51ef9ed7a8065ba90bd74d2d68eb02 Mon Sep 17 00:00:00 2001 From: John Korsnes Date: Sun, 13 Sep 2026 14:10:41 +0200 Subject: [PATCH 3/3] Merge Release/PreRelease into a single workflow file NuGet Trusted Publishing policies are keyed off the top-level triggering workflow file, so having Release.yml and PreRelease.yml as separate files meant configuring two trusted publishing policies on nuget.org. Collapse them into one Release.yml with a `prerelease` workflow_dispatch input; the GitHub release job is skipped when prerelease is true. Only one nuget.org policy is needed now. --- .github/workflows/PreRelease.yml | 13 ------- .github/workflows/Release.yml | 66 +++++++++++++++++++++++++++----- .github/workflows/publish.yml | 63 ------------------------------ 3 files changed, 57 insertions(+), 85 deletions(-) delete mode 100644 .github/workflows/PreRelease.yml delete mode 100644 .github/workflows/publish.yml diff --git a/.github/workflows/PreRelease.yml b/.github/workflows/PreRelease.yml deleted file mode 100644 index a14899c..0000000 --- a/.github/workflows/PreRelease.yml +++ /dev/null @@ -1,13 +0,0 @@ -name: PreRelease - -on: - workflow_dispatch: - -jobs: - publish: - permissions: - id-token: write - uses: ./.github/workflows/publish.yml - with: - prerelease: true - secrets: inherit diff --git a/.github/workflows/Release.yml b/.github/workflows/Release.yml index 57118d1..821bea0 100644 --- a/.github/workflows/Release.yml +++ b/.github/workflows/Release.yml @@ -2,18 +2,66 @@ name: Release on: workflow_dispatch: + inputs: + prerelease: + description: Publish a prerelease package (skips creating a GitHub release) + type: boolean + default: false jobs: - publish: + build: + runs-on: ubuntu-latest permissions: + contents: write id-token: write - uses: ./.github/workflows/publish.yml - with: - prerelease: false - secrets: inherit + + outputs: + version: ${{ steps.version.outputs.value }} + + steps: + - uses: actions/checkout@v7 + with: + fetch-depth: 0 + - run: echo "ACTIONS_ALLOW_UNSECURE_COMMANDS=true" >> $GITHUB_ENV + - name: Install GitVersion + uses: gittools/actions/gitversion/setup@v0 + with: + versionSpec: "5.x" + - name: Setup .NET + uses: actions/setup-dotnet@v6 + with: + dotnet-version: | + 10.0.x + 11.0.x + - name: Determine Version + id: gitversion + uses: gittools/actions/gitversion/execute@v0 + with: + useConfigFile: true + - name: Resolve package version + id: version + run: | + if [ "${{ inputs.prerelease }}" = "true" ]; then + echo "value=${{ steps.gitversion.outputs.NuGetVersionV2 }}-${{ steps.gitversion.outputs.ShortSha }}" >> "$GITHUB_OUTPUT" + else + echo "value=${{ steps.gitversion.outputs.majorMinorPatch }}" >> "$GITHUB_OUTPUT" + fi + - name: NuGet login (OIDC → temp API key) + uses: NuGet/login@v1 + id: nuget_login + with: + user: ${{ secrets.NUGET_USER }} + - name: Pack & Publish + run: dotnet run --project build -- publish + env: + BUILD_VERSION: ${{ steps.version.outputs.value }} + BUILD_INFORMATIONAL_VERSION: ${{ steps.gitversion.outputs.informationalVersion }} + BUILD_RELEASE_NOTES: https://github.com/${{ github.repository }}/releases/tag/${{ steps.version.outputs.value }} + NUGET_API_KEY: ${{ steps.nuget_login.outputs.NUGET_API_KEY }} release: - needs: publish + needs: build + if: ${{ !inputs.prerelease }} runs-on: ubuntu-latest permissions: contents: write @@ -27,7 +75,7 @@ jobs: id: releasenotes run: | gh api repos/$GITHUB_REPOSITORY/releases/generate-notes \ - -f tag_name="${{ needs.publish.outputs.version }}" \ + -f tag_name="${{ needs.build.outputs.version }}" \ -q .body > CHANGELOG.md echo -e "\n\n" >> CHANGELOG.md git log $(git describe --tags --abbrev=0)..HEAD --oneline >> CHANGELOG.md @@ -39,8 +87,8 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: - tag_name: ${{ needs.publish.outputs.version }} - release_name: Release ${{ needs.publish.outputs.version }} + tag_name: ${{ needs.build.outputs.version }} + release_name: Release ${{ needs.build.outputs.version }} body_path: CHANGELOG.md draft: false prerelease: false diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml deleted file mode 100644 index a40183a..0000000 --- a/.github/workflows/publish.yml +++ /dev/null @@ -1,63 +0,0 @@ -name: Publish - -on: - workflow_call: - inputs: - prerelease: - required: true - type: boolean - outputs: - version: - description: The published package version (tag-friendly) - value: ${{ jobs.build.outputs.version }} - informational_version: - value: ${{ jobs.build.outputs.informational_version }} - -jobs: - build: - runs-on: ubuntu-latest - - outputs: - version: ${{ steps.version.outputs.value }} - informational_version: ${{ steps.gitversion.outputs.informationalVersion }} - - steps: - - uses: actions/checkout@v7 - with: - fetch-depth: 0 - - run: echo "ACTIONS_ALLOW_UNSECURE_COMMANDS=true" >> $GITHUB_ENV - - name: Install GitVersion - uses: gittools/actions/gitversion/setup@v0 - with: - versionSpec: "5.x" - - name: Setup .NET - uses: actions/setup-dotnet@v6 - with: - dotnet-version: | - 10.0.x - 11.0.x - - name: Determine Version - id: gitversion - uses: gittools/actions/gitversion/execute@v0 - with: - useConfigFile: true - - name: Resolve package version - id: version - run: | - if [ "${{ inputs.prerelease }}" = "true" ]; then - echo "value=${{ steps.gitversion.outputs.NuGetVersionV2 }}-${{ steps.gitversion.outputs.ShortSha }}" >> "$GITHUB_OUTPUT" - else - echo "value=${{ steps.gitversion.outputs.majorMinorPatch }}" >> "$GITHUB_OUTPUT" - fi - - name: NuGet login (OIDC → temp API key) - uses: NuGet/login@v1 - id: nuget_login - with: - user: ${{ secrets.NUGET_USER }} - - name: Pack & Publish - run: dotnet run --project build -- publish - env: - BUILD_VERSION: ${{ steps.version.outputs.value }} - BUILD_INFORMATIONAL_VERSION: ${{ steps.gitversion.outputs.informationalVersion }} - BUILD_RELEASE_NOTES: https://github.com/${{ github.repository }}/releases/tag/${{ steps.version.outputs.value }} - NUGET_API_KEY: ${{ steps.nuget_login.outputs.NUGET_API_KEY }}