Skip to content

Publish NuGet Packages #2

Publish NuGet Packages

Publish NuGet Packages #2

Workflow file for this run

name: Publish NuGet Packages
on:
release:
types: [published]
workflow_dispatch:
inputs:
tag_name:
description: Release tag to publish, e.g. v4.0.0
required: true
type: string
permissions:
contents: read
concurrency:
group: publish-nuget-${{ github.event.release.tag_name || github.event.inputs.tag_name }}
cancel-in-progress: false
env:
DOTNET_NOLOGO: true
NUGET_OUTPUT: ${{ github.workspace }}/artifacts/nuget
NUGET_SOURCE: https://api.nuget.org/v3/index.json
jobs:
publish:
name: Build, validate, and publish NuGet packages
runs-on: ubuntu-latest
steps:
- name: Resolve release tag
id: release
shell: bash
env:
RELEASE_TAG: ${{ github.event.release.tag_name || github.event.inputs.tag_name }}
run: |
set -euo pipefail
if [[ -z "$RELEASE_TAG" ]]; then
echo "::error::Release tag could not be resolved."
exit 1
fi
if [[ ! "$RELEASE_TAG" =~ ^[vV][0-9]+(\.[0-9]+){2}([-.+][0-9A-Za-z.-]+)?$ ]]; then
echo "::error::Release tag '$RELEASE_TAG' must look like v4.0.0."
exit 1
fi
echo "tag=$RELEASE_TAG" >> "$GITHUB_OUTPUT"
echo "Publishing release tag: $RELEASE_TAG"
- name: Checkout repository
uses: actions/checkout@v6
with:
ref: ${{ steps.release.outputs.tag }}
fetch-depth: 0
persist-credentials: false
- name: Setup .NET
id: setup-dotnet
uses: actions/setup-dotnet@v5
with:
dotnet-version: 10.0.x
dotnet-quality: ga
- name: Pin workflow .NET SDK
shell: bash
run: |
set -euo pipefail
if [[ ! -f global.json ]]; then
printf '{"sdk":{"version":"%s","rollForward":"latestFeature"}}\n' '${{ steps.setup-dotnet.outputs.dotnet-version }}' > global.json
fi
- name: Setup GitVersion
uses: gittools/actions/gitversion/setup@v4
with:
versionSpec: '6.4.x'
- name: Execute GitVersion
id: gitversion
uses: gittools/actions/gitversion/execute@v4.5.0
- name: Validate release tag version
shell: bash
env:
RELEASE_TAG: ${{ steps.release.outputs.tag }}
PACKAGE_VERSION: ${{ steps.gitversion.outputs.majorMinorPatch }}
run: |
set -euo pipefail
release_version="${RELEASE_TAG#v}"
release_version="${release_version#V}"
if [[ "$release_version" != "$PACKAGE_VERSION" ]]; then
echo "::error::Release tag '$RELEASE_TAG' resolves to '$release_version', but GitVersion produced package version '$PACKAGE_VERSION'."
exit 1
fi
echo "Release version validated: $release_version"
- name: Cache NuGet packages
uses: actions/cache@v5
with:
path: ~/.nuget/packages
key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj', '**/*.slnx') }}
restore-keys: |
${{ runner.os }}-nuget-
- name: Prepare package output
shell: bash
run: |
set -euo pipefail
rm -rf "$NUGET_OUTPUT"
mkdir -p "$NUGET_OUTPUT"
- name: Build Release packages
shell: bash
env:
PACKAGE_VERSION: ${{ steps.gitversion.outputs.majorMinorPatch }}
ASSEMBLY_VERSION: ${{ steps.gitversion.outputs.assemblySemVer }}
FILE_VERSION: ${{ steps.gitversion.outputs.assemblySemFileVer }}
INFORMATIONAL_VERSION: ${{ steps.gitversion.outputs.informationalVersion }}
run: |
set -euo pipefail
echo "Package Version: $PACKAGE_VERSION"
echo "Assembly Version: $ASSEMBLY_VERSION"
dotnet build FixedMathSharp.slnx \
--configuration Release \
-p:GitVersion_FullSemVer="$PACKAGE_VERSION" \
-p:GitVersion_AssemblySemVer="$ASSEMBLY_VERSION" \
-p:GitVersion_AssemblySemFileVer="$FILE_VERSION" \
-p:GitVersion_InformationalVersion="$INFORMATIONAL_VERSION" \
-p:PackageOutputPath="$NUGET_OUTPUT"
- name: Run Release tests
run: dotnet test FixedMathSharp.slnx --configuration Release --no-build --verbosity normal
- name: Build ReleaseLean packages
shell: bash
env:
PACKAGE_VERSION: ${{ steps.gitversion.outputs.majorMinorPatch }}
ASSEMBLY_VERSION: ${{ steps.gitversion.outputs.assemblySemVer }}
FILE_VERSION: ${{ steps.gitversion.outputs.assemblySemFileVer }}
INFORMATIONAL_VERSION: ${{ steps.gitversion.outputs.informationalVersion }}
run: |
set -euo pipefail
dotnet build FixedMathSharp.slnx \
--configuration ReleaseLean \
-p:GitVersion_FullSemVer="$PACKAGE_VERSION" \
-p:GitVersion_AssemblySemVer="$ASSEMBLY_VERSION" \
-p:GitVersion_AssemblySemFileVer="$FILE_VERSION" \
-p:GitVersion_InformationalVersion="$INFORMATIONAL_VERSION" \
-p:PackageOutputPath="$NUGET_OUTPUT"
- name: Validate package artifacts
shell: bash
env:
PACKAGE_VERSION: ${{ steps.gitversion.outputs.majorMinorPatch }}
run: |
set -euo pipefail
expected_ids=(
"FixedMathSharp"
"FixedMathSharp.Lean"
"FixedMathSharp.FluentAssertions"
"FixedMathSharp.FluentAssertions.Lean"
)
for package_id in "${expected_ids[@]}"; do
for extension in nupkg snupkg; do
package_path="$NUGET_OUTPUT/${package_id}.${PACKAGE_VERSION}.${extension}"
if [[ ! -f "$package_path" ]]; then
echo "::error::Expected package artifact was not produced: $package_path"
exit 1
fi
done
done
mapfile -t packages < <(find "$NUGET_OUTPUT" -maxdepth 1 -type f \( -name "*.nupkg" -o -name "*.snupkg" \) -printf "%f\n" | sort)
expected_artifact_count=$(( ${#expected_ids[@]} * 2 ))
if [[ "${#packages[@]}" -ne "$expected_artifact_count" ]]; then
echo "::error::Expected $expected_artifact_count NuGet package artifacts, but found ${#packages[@]}."
printf '%s\n' "${packages[@]}"
exit 1
fi
for package_file in "${packages[@]}"; do
if [[ "$package_file" != *".${PACKAGE_VERSION}.nupkg" && "$package_file" != *".${PACKAGE_VERSION}.snupkg" ]]; then
echo "::error::Package artifact '$package_file' does not match release version '$PACKAGE_VERSION'."
exit 1
fi
done
printf 'Validated package artifacts:\n'
printf ' %s\n' "${packages[@]}"
- name: Upload package artifact
uses: actions/upload-artifact@v7
with:
name: nuget-packages-${{ steps.gitversion.outputs.majorMinorPatch }}
path: ${{ env.NUGET_OUTPUT }}/*
if-no-files-found: error
- name: Publish packages to NuGet
shell: bash
env:
NUGET_API_KEY: ${{ secrets.NUGET_API_KEY }}
run: |
set -euo pipefail
if [[ -z "$NUGET_API_KEY" ]]; then
echo "::error::NUGET_API_KEY secret is not configured."
exit 1
fi
cd "$NUGET_OUTPUT"
dotnet nuget push "*.nupkg" \
--api-key "$NUGET_API_KEY" \
--source "$NUGET_SOURCE" \
--skip-duplicate