From 360030c4fd4be48e086642af43f4a5a4f33582c5 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 8 Aug 2026 06:35:06 +0000 Subject: [PATCH] ci: read NUGET_USER from a repository variable, not a secret MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The nuget.org account name the trusted-publishing login authenticates as is a public profile name, not a credential: the API key it exchanges the OIDC token for is what must stay secret, and that key never leaves the job. Storing the username in Actions secrets bought no confidentiality and cost diagnosability — GitHub masks a secret in the logs, so a wrong or missing value showed up as an opaque login failure with the offending value redacted from the very output that would explain it. The value now lives in the repository variables, so the step reads it from the vars context. Documentation follows in both languages, in the release and dry-run pages, and the workflow comment records why it is a variable so it is not "hardened" back into a secret. --- .github/workflows/release.yml | 8 +++++--- doc/handwritten/for-maintainers/ReleaseDryRun.en.md | 6 +++--- doc/handwritten/for-maintainers/ReleaseDryRun.fr.md | 6 +++--- doc/handwritten/for-maintainers/workflows/release.en.md | 6 ++++-- doc/handwritten/for-maintainers/workflows/release.fr.md | 7 +++++-- 5 files changed, 20 insertions(+), 13 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f58e09b5..288e4178 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -226,8 +226,10 @@ jobs: # Trusted publishing: exchange the job's OIDC token for a short-lived, single-use NuGet API key. # Requires a trusted publishing policy on nuget.org (owner Reefact / repo first-class-errors / - # workflow release.yml) for each published package, and the NUGET_USER secret set to the nuget.org - # account username (profile name, not the email address). + # workflow release.yml) for each published package, and the NUGET_USER repository variable set to the + # nuget.org account username (profile name, not the email address). It is a variable, not a secret: a + # nuget.org profile name is public, and masking it as a secret only hid it from the logs that would + # tell you when it is wrong. # # This login runs on EVERY trigger, INCLUDING a dry run: the OIDC token exchange is what validates the # trusted-publishing policy, so rehearsing it here makes a dry run fail red when the policy or @@ -241,7 +243,7 @@ jobs: id: nuget-login uses: NuGet/login@8d196754b4036150537f80ac539e15c2f1028841 # v1 with: - user: ${{ secrets.NUGET_USER }} + user: ${{ vars.NUGET_USER }} - name: Push to NuGet if: github.event_name == 'push' || inputs.dry_run == false diff --git a/doc/handwritten/for-maintainers/ReleaseDryRun.en.md b/doc/handwritten/for-maintainers/ReleaseDryRun.en.md index fd1159da..01a53d3e 100644 --- a/doc/handwritten/for-maintainers/ReleaseDryRun.en.md +++ b/doc/handwritten/for-maintainers/ReleaseDryRun.en.md @@ -73,9 +73,9 @@ A dry run is *almost* free of side effects, with two things to be aware of: - **It performs the real NuGet OIDC login.** The trusted-publishing token exchange runs in a dry run — that is the point: it validates the nuget.org policy, so a dry run **fails red** if the trusted-publishing policy or the - `NUGET_USER` secret is missing or misconfigured. It mints a short-lived, - single-use API key that the dry run never spends (the push is skipped), so - nothing is published. + `NUGET_USER` repository variable is missing or misconfigured. It mints a + short-lived, single-use API key that the dry run never spends (the push is + skipped), so nothing is published. - **Nothing is published.** No package reaches nuget.org, and no GitHub Release or git tag is created. - **The packed `.nupkg` / `.snupkg` are uploaded as workflow-run artifacts**, diff --git a/doc/handwritten/for-maintainers/ReleaseDryRun.fr.md b/doc/handwritten/for-maintainers/ReleaseDryRun.fr.md index 6fd401c5..a3095fc8 100644 --- a/doc/handwritten/for-maintainers/ReleaseDryRun.fr.md +++ b/doc/handwritten/for-maintainers/ReleaseDryRun.fr.md @@ -77,9 +77,9 @@ Un dry run est *presque* sans effet de bord, avec deux points à connaître : - **Il effectue le vrai login OIDC NuGet.** L'échange de jeton du trusted publishing s'exécute pendant un dry run — c'est le but : il valide la policy nuget.org, donc un dry run **échoue (rouge)** si la policy trusted-publishing - ou le secret `NUGET_USER` est absent ou mal configuré. Il génère une clé API - éphémère à usage unique que le dry run ne dépense jamais (le push est sauté), - donc rien n'est publié. + ou la variable de dépôt `NUGET_USER` est absente ou mal configurée. Il génère + une clé API éphémère à usage unique que le dry run ne dépense jamais (le push + est sauté), donc rien n'est publié. - **Rien n'est publié.** Aucun package n'atteint nuget.org, et aucune GitHub Release ni aucun tag Git n'est créé. - **Les `.nupkg` / `.snupkg` produits sont uploadés en artefacts de run**, que diff --git a/doc/handwritten/for-maintainers/workflows/release.en.md b/doc/handwritten/for-maintainers/workflows/release.en.md index d9ef2daa..5e4b1a88 100644 --- a/doc/handwritten/for-maintainers/workflows/release.en.md +++ b/doc/handwritten/for-maintainers/workflows/release.en.md @@ -115,8 +115,10 @@ deliberate: gated.** The token exchange is what validates the trusted-publishing policy, so a dry run fails red when the policy or `NUGET_USER` is missing. It mints a single-use key the dry run never spends. - Requires a trusted-publishing policy on nuget.org and the `NUGET_USER` secret - (the profile **username**, not the email). + Requires a trusted-publishing policy on nuget.org and the `NUGET_USER` + repository **variable** (the profile **username**, not the email). It is a + variable, not a secret: a nuget.org profile name is public, and masking it + only hid it from the logs that would tell you when it is wrong. - **The Release step pins `--target "$GITHUB_SHA"`.** On `workflow_dispatch` the tag does not exist yet and `gh` would otherwise create it from the default branch's latest state; pinning the SHA ties the tag, source archive and diff --git a/doc/handwritten/for-maintainers/workflows/release.fr.md b/doc/handwritten/for-maintainers/workflows/release.fr.md index b08dd19d..31775484 100644 --- a/doc/handwritten/for-maintainers/workflows/release.fr.md +++ b/doc/handwritten/for-maintainers/workflows/release.fr.md @@ -123,8 +123,11 @@ suivants est délibéré : cli) sont conditionnées.** L'échange de token est ce qui valide la policy trusted-publishing, donc un dry run échoue (rouge) quand la policy ou `NUGET_USER` est absent. Il génère une clé à usage unique que le dry run ne - dépense jamais. Nécessite une policy trusted-publishing sur nuget.org et le - secret `NUGET_USER` (le **nom d'utilisateur** du profil, pas l'e-mail). + dépense jamais. Nécessite une policy trusted-publishing sur nuget.org et la + **variable** de dépôt `NUGET_USER` (le **nom d'utilisateur** du profil, pas + l'e-mail). C'est une variable, pas un secret : un nom de profil nuget.org est + public, et le masquer ne faisait que le cacher des logs qui vous diraient + qu'il est faux. - **L'étape Release épingle `--target "$GITHUB_SHA"`.** Sur `workflow_dispatch` le tag n'existe pas encore et `gh` le créerait sinon depuis le dernier état de la branche par défaut ; épingler le SHA lie le tag, l'archive source et les