This guide covers how the ElBruno.LocalLLMs NuGet package is published using OIDC Trusted Publishing — no long-lived API keys required.
| Package | Project | NuGet |
|---|---|---|
ElBruno.LocalLLMs |
src/ElBruno.LocalLLMs/ElBruno.LocalLLMs.csproj |
nuget.org/packages/ElBruno.LocalLLMs |
-
Go to nuget.org → Manage Packages → ElBruno.LocalLLMs → Trusted publishers
-
Click Add trusted publisher and fill in:
Field Value Repository owner elbrunoRepository name ElBruno.LocalLLMsWorkflow file publish.ymlEnvironment release -
Save the policy.
- Go to Settings → Environments → New environment
- Name it:
release - (Optional) Add deployment protection rules:
- Required reviewers — recommended for production packages
- Limit to
mainbranch only
-
Go to Settings → Secrets and variables → Actions
-
Add a new repository secret:
Name Value NUGET_USERYour NuGet.org profile username
Note: No
NUGET_API_KEYsecret is needed. OIDC Trusted Publishing replaces long-lived API keys with short-lived tokens issued per workflow run.
The automated flow:
- Update
<Version>insrc/ElBruno.LocalLLMs/ElBruno.LocalLLMs.csproj - Update
docs/CHANGELOG.mdwith a## [X.Y.Z]entry - Push to
main squad-release.ymlautomatically creates a GitHub Release with tagvX.Y.Z- The release event triggers
publish.yml→ package is built and pushed to NuGet.org
For ad-hoc or pre-release publishes:
- Go to Actions → Publish to NuGet → Run workflow
- Optionally enter a version (e.g.,
1.2.0-beta.1) - If left empty, the version from the
.csprojis used
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ Push to │────▶│ squad-release│────▶│ GitHub │
│ main │ │ creates tag │ │ Release │
└──────────────┘ │ + release │ │ (published) │
└──────────────┘ └──────┬───────┘
│
▼
┌──────────────────────────────────────┐
│ publish.yml │
│ │
│ 1. Checkout code │
│ 2. Setup .NET 8 + 10 │
│ 3. Determine version (tag/input/ │
│ csproj) │
│ 4. Restore → Build → Test → Pack │
│ 5. NuGet/login@v1 (OIDC exchange) │
│ ┌─────────────────────────────┐ │
│ │ GitHub OIDC token │ │
│ │ → NuGet validates: │ │
│ │ • repo owner │ │
│ │ • repo name │ │
│ │ • workflow file │ │
│ │ • environment │ │
│ │ → Returns temp API key │ │
│ └─────────────────────────────┘ │
│ 6. Push .nupkg to NuGet.org │
│ 7. Upload artifact │
└──────────────────────────────────────┘
- GitHub Actions mints a short-lived OIDC token (JWT) containing claims about the workflow run (repo, workflow file, environment, etc.)
NuGet/login@v1sends this token to NuGet.org- NuGet.org validates the token against the Trusted Publishing policy you configured
- If all claims match, NuGet.org returns a temporary API key scoped only to the packages in the policy
- The workflow uses this temp key to push — it expires after the run
Benefits over API keys:
- No secrets to rotate or leak
- Scoped to specific repo + workflow + environment
- Audit trail tied to GitHub Actions run ID
The workflow determines the package version using a 3-tier priority:
| Priority | Source | When |
|---|---|---|
| 1 | Release tag | release event — strips v prefix (e.g., v1.2.3 → 1.2.3) |
| 2 | Manual input | workflow_dispatch with version input provided |
| 3 | csproj fallback | workflow_dispatch with no version — reads <Version> from csproj |
All versions are validated against the regex ^[0-9]+\.[0-9]+\.[0-9]+ — the workflow fails fast on invalid versions.
| Symptom | Cause | Fix |
|---|---|---|
403 Forbidden on NuGet push |
Trusted Publishing policy mismatch | Verify repo owner, repo name, workflow file name, and environment all match exactly |
OIDC token exchange failed |
Missing id-token: write permission |
Ensure the job has permissions: id-token: write |
Invalid version error |
Tag format unexpected | Ensure tags are vX.Y.Z (e.g., v1.2.3). Leading v and . are stripped |
NUGET_USER not found |
Secret not configured | Add NUGET_USER repository secret with your NuGet.org username |
Environment 'release' not found |
GitHub environment not created | Create the release environment in repo Settings → Environments |
| Package pushed but not visible | NuGet.org indexing delay | Packages can take 5–15 minutes to appear. Check nuget.org status page |
| Tests fail in publish workflow | Build config mismatch | Ensure tests pass locally with dotnet test -c Release |