Skip to content

feat: add centralized reusable GitHub workflows #1

feat: add centralized reusable GitHub workflows

feat: add centralized reusable GitHub workflows #1

# Reusable workflow for publishing .NET NuGet packages

Check failure on line 1 in .github/workflows/dotnet-nuget-publish.yml

View workflow run for this annotation

GitHub Actions / .github/workflows/dotnet-nuget-publish.yml

Invalid workflow file

(Line: 235, Col: 11): Unrecognized named-value: 'secrets'. Located at position 25 within expression: inputs.push-to-nuget && secrets.nuget-api-key != ''
# This workflow handles package creation, versioning, and publishing to NuGet.org
name: Publish NuGet Package
on:
workflow_call:
inputs:
# Project files to pack (semicolon-separated for multiple)
project-paths:
description: 'Project file paths to pack (semicolon-separated)'
required: true
type: string
# .NET SDK version
dotnet-version:
description: '.NET SDK version'
required: false
type: string
default: '8.0.x'
# Build configuration
build-configuration:
description: 'Build configuration'
required: false
type: string
default: 'Release'
# Package version (if not specified, uses version from project)
package-version:
description: 'Package version override'
required: false
type: string
default: ''
# Whether to include symbols
include-symbols:
description: 'Include symbol packages'
required: false
type: boolean
default: true
# Whether to include source
include-source:
description: 'Include source in packages'
required: false
type: boolean
default: true
# NuGet source URL
nuget-source:
description: 'NuGet source URL'
required: false
type: string
default: 'https://api.nuget.org/v3/index.json'
# Whether to push to NuGet (false for dry run)
push-to-nuget:
description: 'Push packages to NuGet'
required: false
type: boolean
default: true
# Whether to validate package before push
validate-package:
description: 'Validate package before publishing'
required: false
type: boolean
default: true
secrets:
# NuGet API key for publishing
nuget-api-key:
description: 'NuGet API key'
required: false
jobs:
publish-nuget:
runs-on: ubuntu-latest
outputs:
package-version: ${{ steps.version.outputs.version }}
packages-created: ${{ steps.pack.outputs.packages }}
steps:
# Checkout repository code
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0 # Full history for version calculation
# Setup .NET SDK
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: ${{ inputs.dotnet-version }}
# Cache NuGet packages
- name: Cache NuGet packages
uses: actions/cache@v4
with:
path: ~/.nuget/packages
key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj', '**/*.props', '**/*.targets') }}
restore-keys: |
${{ runner.os }}-nuget-
# Determine package version
- name: Determine package version
id: version
run: |
# Use provided version or extract from tag/project
if [ -n "${{ inputs.package-version }}" ]; then
VERSION="${{ inputs.package-version }}"
elif [ -n "${{ github.ref_name }}" ] && [[ "${{ github.ref_name }}" =~ ^v?[0-9]+\.[0-9]+\.[0-9]+ ]]; then
# Extract version from tag (remove 'v' prefix if present)
VERSION="${{ github.ref_name }}"
VERSION="${VERSION#v}"
else
# Try to extract version from first project file
FIRST_PROJECT=$(echo "${{ inputs.project-paths }}" | cut -d';' -f1)
if [ -f "$FIRST_PROJECT" ]; then
VERSION=$(grep -oPm1 "(?<=<Version>)[^<]+" "$FIRST_PROJECT" || echo "1.0.0")
else
VERSION="1.0.0"
fi
fi
echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "Package version: $VERSION"
# Restore dependencies
- name: Restore dependencies
run: |
# Split project paths and restore each
IFS=';' read -ra PROJECTS <<< "${{ inputs.project-paths }}"
for project in "${PROJECTS[@]}"; do
if [ -f "$project" ]; then
echo "Restoring $project"
dotnet restore "$project"
else
echo "Warning: Project file not found: $project"
fi
done
# Build projects
- name: Build projects
run: |
# Split project paths and build each
IFS=';' read -ra PROJECTS <<< "${{ inputs.project-paths }}"
for project in "${PROJECTS[@]}"; do
if [ -f "$project" ]; then
echo "Building $project"
dotnet build "$project" \
--configuration ${{ inputs.build-configuration }} \
--no-restore \
-p:Version=${{ steps.version.outputs.version }}
fi
done
# Pack NuGet packages
- name: Pack NuGet packages
id: pack
run: |
# Create output directory
mkdir -p ./nupkgs
# Track created packages
PACKAGES=""
# Split project paths and pack each
IFS=';' read -ra PROJECTS <<< "${{ inputs.project-paths }}"
for project in "${PROJECTS[@]}"; do
if [ -f "$project" ]; then
echo "Packing $project"
# Base pack command
PACK_CMD="dotnet pack \"$project\" \
--configuration ${{ inputs.build-configuration }} \
--no-build \
--output ./nupkgs \
-p:PackageVersion=${{ steps.version.outputs.version }}"
# Add symbol package options if requested
if [ "${{ inputs.include-symbols }}" = "true" ]; then
PACK_CMD="$PACK_CMD -p:IncludeSymbols=true -p:SymbolPackageFormat=snupkg"
fi
# Add source inclusion if requested
if [ "${{ inputs.include-source }}" = "true" ]; then
PACK_CMD="$PACK_CMD -p:IncludeSource=true"
fi
# Execute pack command
eval $PACK_CMD
# Track package name
PROJECT_NAME=$(basename "$project" .csproj)
PACKAGES="$PACKAGES $PROJECT_NAME"
fi
done
# List created packages
echo "Created packages:"
ls -la ./nupkgs/
echo "packages=$PACKAGES" >> $GITHUB_OUTPUT
# Validate packages if requested
- name: Validate NuGet packages
if: inputs.validate-package
run: |
# Install NuGet Package Explorer CLI or use dotnet validate
for package in ./nupkgs/*.nupkg; do
if [[ ! "$package" =~ \.symbols\.nupkg$ ]] && [[ ! "$package" =~ \.snupkg$ ]]; then
echo "Validating $package"
# Basic validation - check package can be read
dotnet nuget locals all --clear
dotnet nuget push "$package" --source local --skip-duplicate || true
# Check package metadata
unzip -l "$package" | head -20
fi
done
# Upload packages as artifacts
- name: Upload NuGet packages
uses: actions/upload-artifact@v4
with:
name: nuget-packages
path: ./nupkgs/
retention-days: 7
# Push to NuGet if requested and API key is provided
- name: Push to NuGet
if: inputs.push-to-nuget && secrets.nuget-api-key != ''
run: |
# Push each package to NuGet
for package in ./nupkgs/*.nupkg; do
# Skip symbol packages (they're pushed automatically with main package)
if [[ ! "$package" =~ \.symbols\.nupkg$ ]] && [[ ! "$package" =~ \.snupkg$ ]]; then
echo "Pushing $package to NuGet"
dotnet nuget push "$package" \
--api-key "${{ secrets.nuget-api-key }}" \
--source "${{ inputs.nuget-source }}" \
--skip-duplicate
# Give NuGet time to process between pushes
sleep 2
fi
done
# Summary
- name: Package summary
run: |
echo "## 📦 NuGet Package Publishing Summary" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "**Version:** ${{ steps.version.outputs.version }}" >> $GITHUB_STEP_SUMMARY
echo "**Configuration:** ${{ inputs.build-configuration }}" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### Packages Created:" >> $GITHUB_STEP_SUMMARY
for package in ./nupkgs/*.nupkg; do
if [[ ! "$package" =~ \.symbols\.nupkg$ ]] && [[ ! "$package" =~ \.snupkg$ ]]; then
echo "- $(basename $package)" >> $GITHUB_STEP_SUMMARY
fi
done
if [ "${{ inputs.push-to-nuget }}" = "true" ] && [ -n "${{ secrets.nuget-api-key }}" ]; then
echo "" >> $GITHUB_STEP_SUMMARY
echo "✅ Packages published to NuGet.org" >> $GITHUB_STEP_SUMMARY
else
echo "" >> $GITHUB_STEP_SUMMARY
echo "⚠️ Packages created but not published (dry run or missing API key)" >> $GITHUB_STEP_SUMMARY
fi