Visitar URL original
feat(nuget): Support nuget.org trusted publishing with GitHub OIDC by sentry-junior[bot] · Pull Request #972 · getsentry/craft · GitHub
Skip to content

feat(nuget): Support nuget.org trusted publishing with GitHub OIDC - #972

Merged
ric-oliv merged 3 commits into
masterfrom
feat/nuget-trusted-publishing
Nov 11, 2026
Merged

ric-oliv merged 3 commits into
masterfrom
feat/nuget-trusted-publishing

Conversation

@sentry-junior

@sentry-junior sentry-junior Bot commented Nov 11, 2026 •

Copy link
Copy Markdown
Contributor

Adds nuget.org trusted publishing to the nuget target. Publishing no longer needs a long-lived NUGET_API_TOKEN, so there is no key to rotate. The sentry-dotnet 6.13.0 release (getsentry/publish#10356) failed because that key expired.

Refs getsentry/publish#6754

What changes

  • trustedPublishingUser turns it on. Set it on the nuget target in .craft.yml, or set the NUGET_USER env var; the config value wins. When it is set, the target requests a GitHub Actions OIDC token with audience https://www.nuget.org, exchanges it at https://www.nuget.org/api/v2/token for a temporary API key, and pushes with that key. This is the same exchange NuGet/login does. Without it, the target uses NUGET_API_TOKEN exactly as before. Workspace expansion copies the option to every package target.
  • The key is fetched when the target first pushes, not when it is created. Craft creates every target before publishing anything. sentry-dotnet expands into 24 nuget targets that run one after another, so a key fetched at startup could expire partway through. Each target gets its own key, its packages share it, and Craft fetches a new one once it is 45 minutes old. nuget.org keys last 1 hour.
  • Errors show up early:
    • With trusted publishing enabled but no OIDC available (the job lacks id-token: write), the target fails when it is created instead of falling back to the token without telling anyone.
    • Trusted publishing with a custom serverUrl fails, so a nuget.org key is never sent to another feed.
    • When nuget.org rejects the exchange, the error includes its response and a reminder that the user must be the person who created the policy, not the org.
  • The key stays out of logs. It is passed to dotnet nuget push as the ${NUGET_API_KEY} placeholder. spawnProcess fills it in after logging, as it already did for ${NUGET_API_TOKEN}. On GitHub Actions the temporary key is also masked with ::add-mask::.
  • The GitHub OIDC token request moves from mcpRegistry.ts to src/utils/githubOidc.ts, so both targets use the same code. The mcp-registry target behaves the same as before.
  • Docs: targets/nuget.md gets the new option and a Trusted Publishing section.

Rollout (needs people, not code)

  1. nuget.org: a member of the sentry nuget.org owner (ideally a bot or shared account, not a person who might leave) adds a Trusted Publishing policy:
    • Repository owner getsentry, repository publish, workflow file publish.yml, environment production
    • Owner: sentry. The policy covers every package that owner owns. You can narrow it with a Sentry* package glob.
  2. This PR: merge and release Craft. getsentry/publish runs getsentry/craft:latest, so no pin needs bumping, and publish.yml already grants id-token: write.
  3. getsentry/sentry-dotnet: add trustedPublishingUser: <nuget.org profile name of the policy creator> to the nuget target in .craft.yml, and bump minVersion to the Craft release from step 2.
  4. Publish a sentry-dotnet release and check the log for Requesting a temporary NuGet API key.
  5. Once a release has succeeded: delete the NUGET_API_TOKEN secret from getsentry/publish and revoke the key on nuget.org.

With the config option, getsentry/publish#10366 (passing NUGET_USER from a repository variable) is no longer needed.

Rollback: remove trustedPublishingUser from .craft.yml. Craft goes back to NUGET_API_TOKEN as long as that secret is still there.

Testing

  • New tests in nuget.test.ts cover:
    • token mode, including the placeholder
    • the config errors
    • enabling it from the target config (which wins over NUGET_USER), and workspace expansion carrying the option
    • the OIDC audience and the exchange request
    • one key shared by the packages of a target
    • getting a new key after 45 minutes
    • masking
    • a rejected exchange
  • mcpRegistry.test.ts passes unchanged.
  • pnpm lint, tsc --noEmit and pnpm build pass.
  • Nothing has been tested against nuget.org yet. That needs the policy from step 1.
  • In the Junior sandbox, 24 prepare-dry-run e2e tests and one git test fail because of the sandbox's git identity settings. They fail the same way on master.

via Ricardo Oliveira.

--

View Session [Sentry]

When NUGET_USER is set, the nuget target exchanges a GitHub Actions OIDC
token for a temporary nuget.org API key instead of using NUGET_API_TOKEN,
so there is no long-lived key to rotate. nuget.org's temporary keys last
an hour, so Craft gets a new one after 45 minutes.

The GitHub OIDC token request moves from the mcp-registry target to a shared
util so both targets use it.

Refs getsentry/publish#6754

Co-Authored-By: Ricardo Oliveira <ricardo.oliveira@sentry.io>
So they show up in publish logs at the default level.

Co-Authored-By: Ricardo Oliveira <ricardo.oliveira@sentry.io>
@github-actions

github-actions Bot commented Nov 11, 2026 •

Copy link
Copy Markdown
Contributor
PR Preview Action v1.8.1
Preview removed because the pull request was closed.
2026-11-11 16:06 UTC

Adds a trustedPublishingUser option to the nuget target, so each repo
opts into trusted publishing in its .craft.yml instead of the publishing
workflow setting NUGET_USER for every release. NUGET_USER still works
as a fallback. Workspace expansion carries the option to every package
target.

Refs getsentry/publish#6754
@ric-oliv
ric-oliv marked this pull request as ready for review November 11, 2026 15:39
@ric-oliv
ric-oliv merged commit b99396f into master Nov 11, 2026
30 checks passed
@ric-oliv
ric-oliv deleted the feat/nuget-trusted-publishing branch November 11, 2026 16:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants