Shared GitHub Actions building blocks for RushuiGuan repositories.
Reusable workflows (.github/workflows/) — publish to GitHub Packages using the built-in GITHUB_TOKEN:
| Workflow | Trigger (in caller) | Publishes |
|---|---|---|
nuget-prerelease.yml |
push to rc |
{Version}-rc.{commit count} → GitHub Packages |
nuget-release-github.yml |
vX.Y.Z tag on production |
{Version} → GitHub Packages |
Composite actions (.github/actions/) — publish to nuget.org via Trusted Publishing:
| Action | Used on | Publishes |
|---|---|---|
nuget-prerelease-nugetorg |
vX.Y.Z-rc.N tag on rc |
{Version}-rc.N → nuget.org, then a prerelease GitHub Release from the tag (promotes a selected RC) |
nuget-release-nugetorg |
vX.Y.Z tag on production |
{Version} → nuget.org, then a GitHub Release from the tag (public packages only) |
All of these read the package list from the caller's .projects file (the
# packages section) and the version from the caller's Directory.Build.props
<Version>. The caller's repo is what gets checked out, so these come from the
calling repository, not from devops.
nuget.org Trusted Publishing matches the OIDC job_workflow_ref claim against
your policy, and that claim must be the caller's workflow. A reusable workflow
makes job_workflow_ref point at devops (never matches a caller policy), and
the alternative of minting the key in the caller and passing it to a reusable
workflow fails because GitHub redacts masked secrets written to job outputs.
A composite action runs NuGet/login + push as steps inside the caller's job,
so job_workflow_ref stays the caller's workflow and the masked key never crosses
a job boundary.
| Input | Default | Applies to |
|---|---|---|
dotnet-version |
10.0.x |
all |
nuget-user |
(required) | nuget-prerelease-nugetorg, nuget-release-nugetorg — nuget.org profile name owning the Trusted Publishing policy |
None. GITHUB_TOKEN is provided automatically; nuget.org uses Trusted Publishing
(OIDC), so there's no NUGET_API_KEY.
Add .github/workflows/nuget.yml to the calling repo:
name: nuget
on:
push:
branches:
- rc
tags:
- 'v*'
permissions:
contents: read
packages: write
jobs:
prerelease:
if: github.ref == 'refs/heads/rc'
uses: RushuiGuan/devops/.github/workflows/nuget-prerelease.yml@v1
secrets: inherit
# Stable to GitHub Packages (private packages, or as a mirror). Reusable workflow.
github-release:
if: startsWith(github.ref, 'refs/tags/v')
uses: RushuiGuan/devops/.github/workflows/nuget-release-github.yml@v1
secrets: inherit
# Promote a selected RC to nuget.org. Composite action - runs in this job.
prerelease-nugetorg:
if: startsWith(github.ref, 'refs/tags/v') && contains(github.ref, '-rc.')
runs-on: ubuntu-latest
permissions:
contents: write # create the prerelease GitHub Release
packages: read
id-token: write # required for Trusted Publishing OIDC
steps:
- uses: RushuiGuan/devops/.github/actions/nuget-prerelease-nugetorg@v1
with:
nuget-user: Rushui # your nuget.org profile name
# Stable to nuget.org (public packages only). Composite action - runs in this job.
nuget-release:
if: startsWith(github.ref, 'refs/tags/v') && !contains(github.ref, '-rc.')
runs-on: ubuntu-latest
permissions:
contents: write # create the GitHub Release
packages: read
id-token: write # required for Trusted Publishing OIDC
steps:
- uses: RushuiGuan/devops/.github/actions/nuget-release-nugetorg@v1
with:
nuget-user: Rushui # your nuget.org profile nameUse whichever release path(s) a repo needs — github-release, prerelease-nugetorg,
nuget-release, or any combination. A private repo should use neither nuget.org nor
id-token: write. When both nuget.org jobs are present, gate the stable path on
&& !contains(github.ref, '-rc.') so an RC tag doesn't also trigger a stable release.
- Pin a ref. Reference
@v1(a moving major tag on this repo) or@main. devopsmust be public for public callers — GitHub forbids public repos from consuming a private repo's actions/reusable workflows. It holds no secrets.- Branch model the callers follow:
main(work, publishes nothing) →rc(prereleases) →production(PR-locked; tagvX.Y.Zhere for stable). - Promoting a selected RC to nuget.org: a push to
rcpublishesX.Y.Z-rc.Nto GitHub Packages. To promote a specific one, tag that exact commitvX.Y.Z-rc.Nand push the tag. The action rebuilds from that commit (version taken straight from the tag) and publishes the prerelease to nuget.org. Guards enforce thatX.Y.ZmatchesDirectory.Build.props, thatNequals the commit count (git rev-list --count HEAD, the same formula the prerelease workflow uses), and that the commit is onrc. - Symbol packages (
.snupkg) are not pushed to GitHub Packages (unsupported) but are pushed to nuget.org alongside their.nupkg.
Because the composite action runs in the caller, the OIDC job_workflow_ref is
the caller's workflow — so the policy is per calling repo, not on devops.
On nuget.org → your username → Trusted Publishing, create a policy for each public
repo:
- Repository Owner:
RushuiGuan - Repository: the calling repo (e.g.
exceptions) - Workflow File: the calling workflow (e.g.
nuget.yml) - Environment: (leave empty)
- User (
nuget-user): the profile name that created the policy
A new policy starts in a 7-day pending window; the first successful publish makes it permanent.
One policy per repo covers both nuget.org jobs — the prerelease and stable
paths run in the same nuget.yml, so the job_workflow_ref (and thus the policy
match) is identical. Leaving Environment empty is what lets the RC path
publish from the rc branch's commits and the stable path from production tags
under the same policy.