From 6bd9da143004c0229cb043e6ff8fe5b3efc2c370 Mon Sep 17 00:00:00 2001 From: Kieron Lanning Date: Mon, 2 Nov 2026 22:36:17 +0000 Subject: [PATCH] ci: publish with nuget.org Trusted Publishing nuget.org is retiring long-lived API keys in favour of Trusted Publishing, so the release job now mints a short-lived key from its OIDC token instead of reading a stored secret. The release pipeline moved from the reusable workflow to `purview-dev/build/actions/purview-release`, a composite action. That is required: nuget.org validates the OIDC `job_workflow_ref` claim, which inside a called workflow names the called repository rather than this one (NuGet/NuGetGallery#11000). `id-token: write` is granted on the job because GitHub withholds it by default. --- .github/workflows/release.yml | 32 ++++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3a95f9a..337caa8 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -6,10 +6,11 @@ on: permissions: contents: write - # The shared release workflow pushes the same packages to nuget.org and, when - # PURVIEW_PUBLISH_GITHUB_PACKAGES=true, to GitHub Packages. A reusable workflow cannot grant - # more than its caller, so the permission is declared here. + # Neither a reusable workflow nor a composite action can grant a scope, so the caller owns these: + # `packages: write` for the GitHub Packages mirror, `id-token: write` for the nuget.org Trusted + # Publishing exchange, which GitHub withholds by default. packages: write + id-token: write concurrency: group: ${{ github.workflow }}-${{ github.ref }} @@ -18,8 +19,23 @@ concurrency: jobs: release: name: Release packages - uses: purview-dev/build/.github/workflows/purview-release.yml@main - with: - release-mode: NuGet - release-branch: main - secrets: inherit + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + # A composite action, not a reusable workflow: nuget.org validates the OIDC `job_workflow_ref` + # claim against the registered Trusted Publishing policy, and inside a called workflow that + # claim names the called repository. Running it here keeps the claim on this file, which is the + # file name the policy is registered against. + - uses: purview-dev/build/actions/purview-release@main + with: + release-mode: NuGet + trusted-publishing: true + # The nuget.org profile name, not the sign-in email. + nuget-user: kieronlanning + # A composite action has no `secrets` context, so the read token is passed explicitly. + packages-read-token: ${{ secrets.PURVIEW_PACKAGES_READ_TOKEN }} + # A composite action has no `vars` context, so repository variables are passed here. + source-mapping: ${{ vars.PURVIEW_NUGET_SOURCE_MAPPING }} + github-packages: ${{ vars.PURVIEW_PUBLISH_GITHUB_PACKAGES == 'true' }} + github-packages-keep-versions: ${{ vars.PURVIEW_GITHUB_PACKAGES_KEEP_VERSIONS || '1' }} + github-packages-retention-hours: ${{ vars.PURVIEW_GITHUB_PACKAGES_RETENTION_HOURS || '24' }}