Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 92 additions & 13 deletions .github/actions/push-nuget-packages/action.yml
Original file line number Diff line number Diff line change
@@ -1,49 +1,128 @@
name: Push NuGet Packages
description: Downloads .nupkg release assets and publishes them to a NuGet feed using OIDC trusted publishing
description: Downloads .nupkg release assets and publishes them to a NuGet feed using OIDC trusted publishing or a static API key
inputs:
version:
required: true
description: Release tag to download package assets from
description: >
Release to download package assets from.
release-state:
required: true
description: Whether version identifies a 'published' release (by tag) or a 'draft' release (by name). One of 'published' or 'draft'
exclude-package-pattern:
required: false
default: ''
description: Glob pattern of package filenames to exclude from the push
nuget-user:
publish-mode:
required: true
description: NuGet trusted publishing user
nuget-source-domain:
description: How to authenticate the push. One of 'trusted-publishing' or 'api-key'
nuget-user:
required: false
default: ''
description: NuGet trusted publishing user (required when publish-mode is trusted-publishing)
feed-url:
required: true
description: NuGet feed domain used for both the trusted-publishing token service and the package source
description: >
NuGet package source URL, used as the push source. For publish-mode
'trusted-publishing' its domain also extracted to build the token
service URL and audience.
api-key:
required: false
default: ''
description: API key used to authenticate the push (required when publish-mode is api-key)
runs:
using: composite
steps:
- name: Validate inputs
shell: pwsh
run: |
$publishMode = "${{ inputs.publish-mode }}"
$validModes = @('trusted-publishing', 'api-key')
if ($validModes -notcontains $publishMode) {
throw "publish-mode must be one of: $($validModes -join ', '). Got '$publishMode'."
}
$releaseState = "${{ inputs.release-state }}"
$validReleaseStates = @('published', 'draft')
if ($validReleaseStates -notcontains $releaseState) {
throw "release-state must be one of: $($validReleaseStates -join ', '). Got '$releaseState'."
}
if ($publishMode -eq 'trusted-publishing' -and -not "${{ inputs.nuget-user }}") {
throw "nuget-user is required when publish-mode is 'trusted-publishing'."
}
if ($publishMode -eq 'trusted-publishing' -and "${{ inputs.api-key }}") {
throw "api-key must not be set when publish-mode is 'trusted-publishing'."
}
if ($publishMode -eq 'api-key' -and -not "${{ inputs.api-key }}") {
throw "api-key is required when publish-mode is 'api-key'."
}
$feedUrl = "${{ inputs.feed-url }}"
$parsedUri = $null
if (-not [Uri]::TryCreate($feedUrl, [UriKind]::Absolute, [ref]$parsedUri)) {
throw "feed-url must be a valid absolute URL. Got '$feedUrl'."
}
- name: Setup .NET SDK
uses: actions/setup-dotnet@v6
with:
dotnet-version: 'latest'
- name: Download assets from release
- name: Download assets from published release
if: inputs.release-state == 'published'
shell: pwsh
run: |
$version = "${{ inputs.version }}"
gh release download $version --repo "${{ github.repository }}" --pattern "*.nupkg" --dir nugets
if ($LASTEXITCODE -ne 0) {
throw "Failed to download release assets for tag '$version'"
}
env:
GH_TOKEN: ${{ github.token }}
- name: Download assets from draft release
if: inputs.release-state == 'draft'
shell: pwsh
run: |
gh release download "${{ inputs.version }}" --repo "${{ github.repository }}" --pattern "*.nupkg" --dir nugets
$name = "${{ inputs.version }}"
$releases = gh api "repos/${{ github.repository }}/releases" | ConvertFrom-Json
$release = $releases | Where-Object { $_.name -eq $name } | Select-Object -First 1
if (-not $release) {
throw "Could not find a draft release named '$name'"
}
Write-Output "Found draft release id $($release.id) with tag '$($release.tag_name)' for '$name'"
gh release download $release.tag_name --repo "${{ github.repository }}" --pattern "*.nupkg" --dir nugets
if ($LASTEXITCODE -ne 0) {
throw "Failed to download release assets for tag '$($release.tag_name)'"
}
env:
GH_TOKEN: ${{ github.token }}
- name: Resolve NuGet feed domain
id: resolve
if: inputs.publish-mode == 'trusted-publishing'
shell: pwsh
run: |
$domain = ([Uri]"${{ inputs.feed-url }}").GetLeftPart([System.UriPartial]::Authority)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Praise: fancy!

Write-Output "domain=$domain" >> $env:GITHUB_OUTPUT
- name: Get NuGet trusted publishing API key
if: inputs.publish-mode == 'trusted-publishing'
uses: NuGet/login@v1
id: login
with:
user: ${{ inputs.nuget-user }}
token-service-url: ${{ inputs.nuget-source-domain }}/api/v2/token
audience: ${{ inputs.nuget-source-domain }}
- name: Push NuGet packages to production feed
token-service-url: ${{ steps.resolve.outputs.domain }}/api/v2/token

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question: I don't recall if I asked this, but why do we use v2 instead of v3?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was just going off of what the default was: https://github.com/NuGet/login#-inputs

audience: ${{ steps.resolve.outputs.domain }}
- name: Push NuGet packages
shell: pwsh
run: |
$getChildItemParams = @{ Path = 'nugets/*'; Include = '*.nupkg' }
$excludePattern = "${{ inputs.exclude-package-pattern }}"
if ($excludePattern) { $getChildItemParams.Exclude = $excludePattern }
$packages = Get-ChildItem @getChildItemParams
Write-Output "Pushing $($packages.Count) NuGet package(s) to ${{ inputs.nuget-source-domain }}:"
$source = "${{ inputs.feed-url }}"
$apiKey = if ("${{ inputs.publish-mode }}" -eq 'trusted-publishing') { "${{ steps.login.outputs.NUGET_API_KEY }}" } else { "${{ inputs.api-key }}" }
Write-Output "Pushing $($packages.Count) NuGet package(s) to $source`:"
$packages | ForEach-Object { Write-Output " - $($_.Name)" }
$failed = @()
foreach ($package in $packages)
{
dotnet nuget push $package.FullName --source ${{ inputs.nuget-source-domain }}/api/v2/package --api-key ${{ steps.login.outputs.NUGET_API_KEY }}
dotnet nuget push $package.FullName --source $source --api-key $apiKey --skip-duplicate
if ($LASTEXITCODE -ne 0) { $failed += $package.Name }
}
if ($failed.Count -gt 0) {
throw "Failed to push $($failed.Count) package(s): $($failed -join ', ')"
}
50 changes: 0 additions & 50 deletions .github/workflows/push-nuget-packages-staging.yml

This file was deleted.

Loading