Repository navigation
feat(publish): Pass NUGET_USER to Craft for nuget.org trusted publishing - #10366
sentry-junior[bot] wants to merge 1 commit into
Conversation
Craft uses nuget.org trusted publishing (GitHub OIDC) instead of NUGET_API_TOKEN when NUGET_USER is set. Reading it from a repository variable keeps this a no-op until the variable is set, and makes rolling back a matter of deleting it. Refs #6754 Co-Authored-By: Ricardo Oliveira <ricardo.oliveira@sentry.io>
|
Actually, I think we should go back to Craft and also allow setting this via the config, wdyt? |
|
Agreed, the config is a better place for it. Pushed to getsentry/craft#972 (d9b6db3): the Why this is better: each repo opts in through its own That makes this PR unnecessary. I'm leaving it open for you to close, in case you'd still like the env var path. The sentry-dotnet side becomes |
|
Closing in favor of the trustedPublishingUser config option in getsentry/craft#972. Thanks! |
) Adds [nuget.org trusted publishing](https://learn.microsoft.com/nuget/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`](https://github.com/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`. <!-- junior-request-attribution:start --> via **Ricardo Oliveira**. <!-- junior-request-attribution:end --> <!-- junior-session-footer:start --> <!-- junior-conversation-id:slack%3AC0CACQ70E8Z%3A1794364886.114019 --> -- [View Session](https://junior-prod.sentry.dev/conversations/slack%3AC0CACQ70E8Z%3A1794364886.114019) [[Sentry]](https://sentry.sentry.io/explore/conversations/slack%3AC0CACQ70E8Z%3A1794364886.114019/?project=4510944073809921) <!-- junior-session-footer:end --> --------- Co-authored-by: sentry-junior[bot] <264270552+sentry-junior[bot]@users.noreply.github.com> Co-authored-by: Ricardo Oliveira <ricardo.oliveira@sentry.io>
Companion to getsentry/craft#972. With this PR, NuGet releases can use nuget.org trusted publishing (GitHub OIDC) instead of the long-lived
NUGET_API_TOKEN. That token expiring is what failed #10356.Refs #6754
Change
The Craft step now gets
NUGET_USER: ${{ vars.NUGET_USER }}.id-token: writewas already granted at the workflow level (for mcp-registry), so nothing else is needed.NUGET_USERis empty. Craft then usesNUGET_API_TOKENas it does today.Steps that need people
sentrynuget.org owner adds a Trusted Publishing policy with ownergetsentry, repositorypublish, workflow filepublish.ymland environmentproduction. The policy owner issentry, which covers all of its packages; aSentry*glob is optional. Prefer a bot or shared account, because the policy stops working if the person who created it leaves the org.getsentry/craft:latest.NUGET_USERto the nuget.org profile name of the policy creator from step 1. Use the profile name, not the email.Requesting a temporary NuGet API key.NUGET_API_TOKENsecret and revoke the key on nuget.org.#skip-changelog
via Ricardo Oliveira.
--
View Session [Sentry]