Skip to content

ci: read NUGET_USER from a repository variable, not a secret - #413

Merged
Reefact merged 1 commit into
mainfrom
claude/nuget-user-secret-to-variable-c1m13v
Aug 8, 2026
Merged

ci: read NUGET_USER from a repository variable, not a secret#413
Reefact merged 1 commit into
mainfrom
claude/nuget-user-secret-to-variable-c1m13v

Conversation

@Reefact

@Reefact Reefact commented Aug 8, 2026

Copy link
Copy Markdown
Owner

Summary

release.yml read the nuget.org account name from secrets.NUGET_USER, but that value now lives in the repository variables — the secret no longer exists. secrets.* does not fall back to vars.*: the expression resolved to an empty string, so NuGet/login would have run with user: "" and failed the OIDC login on the next tag and on the next manual dry run. This points the step at the vars context and fixes the documentation, which described the value as a secret in four places.

The username is a public nuget.org profile name, not a credential — the short-lived API key the OIDC exchange mints is what must stay secret, and it never leaves the job. Keeping the username in Actions secrets bought no confidentiality and cost diagnosability: GitHub masks a secret in the logs, so a wrong value surfaced as an opaque login failure with the offending value redacted from the very output that would explain it.

Type of change

  • Bug fix
  • New feature
  • Breaking change
  • Refactoring
  • Analyzer / diagnostic change
  • Tests
  • Documentation
  • Build / CI / tooling

Changes

  • .github/workflows/release.yml — the NuGet login (OIDC) step reads ${{ vars.NUGET_USER }} instead of ${{ secrets.NUGET_USER }}.
  • .github/workflows/release.yml — the step's comment names the repository variable and records why it is a variable rather than a secret, so it is not "hardened" back.
  • doc/handwritten/for-maintainers/workflows/release.en.md and release.fr.md — the Handle with care entry now requires the repository variable, with the same rationale.
  • doc/handwritten/for-maintainers/ReleaseDryRun.en.md and ReleaseDryRun.fr.md — the dry-run side-effects note names the variable.

No C# is touched, and no runtime, analyzer, or packaging behaviour changes.

Testing

  • dotnet build FirstClassErrors.sln
  • dotnet test FirstClassErrors.sln
  • Analyzer tests pass (FirstClassErrors.Analyzers.UnitTests)

The three boxes above were run on the runner, inside the dry run described below, at -c Release on this exact commit — not locally. FirstClassErrors.Analyzers.UnitTests is part of FirstClassErrors.sln, so the solution-wide dotnet test covers it.

Release dry run — green: release.yml dispatched on this branch (lib, version 0.0.0-dry.1, dry_run ticked) — run 31245059241, 94s, job Pack & push NuGet successful.

  • NuGet login (OIDC) succeeded: Successfully exchanged OIDC token for NuGet API key. That is the end-to-end proof — vars.NUGET_USER resolves, and the nuget.org trusted-publishing policy accepts the exchange.
  • The step's log now prints user: SylvainAurat in clear where it used to print user: ***. That is precisely the diagnosability this PR is about: a wrong value is now readable in the output that reports the failure.
  • The three publish steps — Push to NuGet, Publish GitHub Release, Refresh GenDoc's error-catalog baseline — skipped, as designed. Nothing was published.
  • Require a major bump for GenDoc breaking changes skipped: it is cli-train only, which is why the lib train was chosen for this rehearsal.
  • Expected side effect, per ReleaseDryRun.en.md: the run wrote a real provenance attestation for the throwaway 0.0.0-dry.1 to the attestation store and the public Sigstore log.

Also run: actionlint 1.7.7 — the exact version lint.yml pins — over all workflows: clean, exit 0.

Note for the merge: pack-push declares no environment:, so NUGET_USER must stay a repository (or organization) variable — an environment-scoped variable would not be visible and would reproduce the empty-string failure this PR fixes. The green dry run confirms it is currently scoped correctly.

Documentation

  • Public API / error documentation updated
  • README / doc/ updated
  • French translation (doc/handwritten/for-users/README.fr.md) updated if user-facing behavior changed
  • No documentation change required

The maintainer documentation is updated in both languages (release.{en,fr}.md, ReleaseDryRun.{en,fr}.md). The user-facing README.fr.md is untouched: no user-facing behaviour changes.

Architecture decisions

  • No architectural decision in this pull request
  • New decision recorded — ADR drafted as Proposed: ADR-____
  • Supersedes an existing ADR — successor proposed, status not flipped: ADR-____
  • ⚠️ Conflicts with an existing ADR — flagged for the maintainer: ADR-____

Checked against the ADR base. The lasting decision — trusted publishing with no long-lived NUGET_API_KEY stored — is unchanged; this only corrects the classification of a value that was never a secret. The rationale lives in the workflow comment and the two documentation pages, which is where a future maintainer would look. Happy to draft one if you read it as a security-posture decision worth recording.

Related issues

None.

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.
@Reefact
Reefact merged commit b07a0f6 into main Aug 8, 2026
25 checks passed
Repository owner deleted a comment from chatgpt-codex-connector Bot Aug 8, 2026
@Reefact
Reefact deleted the claude/nuget-user-secret-to-variable-c1m13v branch August 8, 2026 07:44
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