Build Pipeline - rpapub/WatchfulAnvil GitHub Wiki

📖 Writing a CI/CD Pipeline Task for .NET Library Versioning Using GitVersion

📌 Overview

When designing a CI/CD pipeline task (e.g., GitHub Action, Azure DevOps Pipeline, GitLab CI) for building and versioning a .NET library, you must consider:

  • How GitVersion will be used to generate consistent versions.
  • Ensuring correct version propagation across dotnet build and dotnet pack.
  • Handling branches, tags, and pre-releases.
  • Publishing NuGet packages with correct versioning.

🔹 Key Considerations

1. Ensure GitVersion Runs Early

  • GitVersion must run before dotnet build.
  • Extract versioning information once per run.
  • Store it in environment variables or pipeline artifacts for reuse.

💡 Example: Running GitVersion in a Pipeline

- name: Install GitVersion
  run: dotnet tool install --global GitVersion.Tool

- name: Determine Version
  run: echo "VERSION=$(dotnet-gitversion /output json | jq -r '.FullSemVer')" >> $GITHUB_ENV

2. Pass Version Information to dotnet build

GitVersion provides:

  • FullSemVer → Used for assembly and package versioning.
  • NuGetVersionV2 → Used for NuGet package versions.
  • AssemblySemFileVer → Used for FileVersion.
  • AssemblySemVer → Used for AssemblyVersion.

💡 Example: Injecting Versions into dotnet build

- name: Build .NET Project
  run: |
    dotnet build --configuration Release \
      /p:Version=${{ env.VERSION }} \
      /p:FileVersion=${{ env.VERSION }} \
      /p:AssemblyVersion=${{ env.VERSION }}

3. Use Correct Versioning for NuGet Packages

When running dotnet pack, ensure:

  • PackageVersion matches GitVersion.NuGetVersionV2.
  • No stale .nupkg files exist.

💡 Example: Running dotnet pack with Correct Versioning

- name: Pack NuGet Package
  run: |
    dotnet pack --configuration Release --no-build \
      /p:PackageVersion=${{ env.VERSION }} \
      /p:Version=${{ env.VERSION }} \
      /p:FileVersion=${{ env.VERSION }}

4. Handle develop, release/*, and main Differently

Each branch should produce the correct versioning:

Branch Expected Version Notes
develop X.Y.Z-alpha.N CI builds, unstable pre-releases
feature/* X.Y.Z-feature.BranchName.N Feature builds, not for release
release/* X.Y.Z-beta.N Release candidates
main X.Y.Z Stable versions only when tagged
hotfix/* X.Y.Z-hotfix.N Urgent fixes for releases

💡 Example: Detecting Branches and Handling Versions

- name: Determine Git Branch
  run: echo "GIT_BRANCH=$(git rev-parse --abbrev-ref HEAD)" >> $GITHUB_ENV

- name: Set Pre-Release Label for `develop`
  if: startsWith(env.GIT_BRANCH, 'develop')
  run: echo "VERSION=${{ env.VERSION }}-alpha.${{ github.run_number }}" >> $GITHUB_ENV

5. Publish to NuGet Only on Stable Releases

  • Push develop builds to a pre-release NuGet feed.
  • Only push main builds when a tag is present (vX.Y.Z).

💡 Example: Conditional NuGet Push

- name: Publish NuGet Package (Pre-release)
  if: github.ref == 'refs/heads/develop'
  run: dotnet nuget push "**/*.nupkg" --source https://api.nuget.org/v3/index.json --api-key ${{ secrets.NUGET_API_KEY }}

- name: Publish NuGet Package (Stable)
  if: startsWith(github.ref, 'refs/tags/v')
  run: dotnet nuget push "**/*.nupkg" --source https://api.nuget.org/v3/index.json --api-key ${{ secrets.NUGET_API_KEY }}

🚀 Full Example: GitHub Actions Workflow

name: Build and Publish .NET Package

on:
  push:
    branches:
      - main
      - develop
      - release/*
      - hotfix/*
    tags:
      - 'v*'  # Run on version tags

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout Code
        uses: actions/checkout@v3
        with:
          fetch-depth: 0  # Required for GitVersion

      - name: Install GitVersion
        run: dotnet tool install --global GitVersion.Tool

      - name: Determine Version
        run: echo "VERSION=$(dotnet-gitversion /output json | jq -r '.FullSemVer')" >> $GITHUB_ENV

      - name: Build .NET Project
        run: |
          dotnet build --configuration Release \
            /p:Version=${{ env.VERSION }} \
            /p:FileVersion=${{ env.VERSION }} \
            /p:AssemblyVersion=${{ env.VERSION }}

      - name: Pack NuGet Package
        run: |
          dotnet pack --configuration Release --no-build \
            /p:PackageVersion=${{ env.VERSION }} \
            /p:Version=${{ env.VERSION }} \
            /p:FileVersion=${{ env.VERSION }}

      - name: Publish NuGet Package (Pre-release)
        if: github.ref == 'refs/heads/develop'
        run: dotnet nuget push "**/*.nupkg" --source https://api.nuget.org/v3/index.json --api-key ${{ secrets.NUGET_API_KEY }}

      - name: Publish NuGet Package (Stable)
        if: startsWith(github.ref, 'refs/tags/v')
        run: dotnet nuget push "**/*.nupkg" --source https://api.nuget.org/v3/index.json --api-key ${{ secrets.NUGET_API_KEY }}

🔥 Final Checklist for Versioning in CI/CD

Run dotnet-gitversion at the beginning of the pipeline.Pass Version, FileVersion, AssemblyVersion to dotnet build.Ensure PackageVersion matches NuGetVersionV2.Use different versioning for develop, main, release/*.Publish pre-release versions (alpha, beta) only from develop and release/*.Publish stable versions only on main when a tag (vX.Y.Z) exists.


🚀 Follow this guide to ensure your .NET library always has the correct versioning in GitHub Actions, Azure DevOps, or any CI/CD system! 🎯