Deploying Nuget Packages (GitHub Actions)
Nuget packages are great and allow developers to make use of useful libraries put together by other developers. The version number is often important to developers many use the common pattern of Major.Minor.Patch
in order to communicate changes to a package with other developers.
Major
version increase usually represents a change to the public API surface that would likely require a developer to update their code that uses the API provided. Minor
version increases, maybe a new feature to the package that maybe adds to the API surface, while maintaining backward compatibility with the previous versions. Patch
usually contains a set of bug fixes that again are backward compatible.
Integrating with CI/CD
There are many, many nuget packages which are open source, and authors often encourage contributors to help maintain & evolve their packages. The most common way for this to be done currently is usually via GitHub. This allows developers, who are using a package, to see the source code, raise issues, fix bugs, and also fork the repository to later be merged back into the main repository with any changes they might make.
GitHub also offers Actions that allow you to perform continuous integration (CI) & continuous delivery (CD) of a piece of software. We can make use of GitHub tools to help us manage, deploying a package to nuget.org.
The Deployment Plan
So in order to do this, we want to really be creating a release of the package and as this happens and the version is specified by the individual making the release along with, detailing what that release includes. Then a workflow is triggered to build, test & deploy the package to nuget.org. GitHub offers this in the form of releases which at the same time tags a commit on a branch usually main/master
with the version number specified. This article details how to create a release in GitHub here is a sneak peek at what that form looks like and what information it can include. The final step once we have created this release we want a GitHub Actions workflow to run, build, test, and pack our source code, apply for the version number and push it up to nuget.org which we will do in the next section.
Note where the version is specified.
Putting the Plan into (GitHub) Action(s)
So in order to implement this, we need a new pipeline within the repository that contains the library we want to package up. This is triggered when a tag is applied to a branch, more than likely the main branch. A sample pipeline is shown below, each step is explained after this snippet.
name: Release Package Version
on:
push:
tags:
- v*
jobs:
build:
if: github.event.base_ref == 'refs/heads/main'
runs-on: ubuntu-latest
steps:
- name: Print Tag Ref
run: echo $GITHUB_REF
- name: Extract Version Number
uses: actions-ecosystem/action-regex-match@v2
id: regex-match
with:
text: ${{ github.ref }}
regex: '[0-9.]+'
- name: Print Version Number
run: echo '${{ steps.regex-match.outputs.match }}'
- uses: actions/checkout@v2
- name: Setup .NET
uses: actions/setup-dotnet@v1
with:
dotnet-version: 5.0.x
- name: Restore dependencies
run: dotnet restore ./Fruitless/Fruitless.csproj
- name: Build
run: dotnet build ./Fruitless/Fruitless.csproj
- name: Pack
run: dotnet pack ./Fruitless/Fruitless.csproj -p:PackageVersion='${{ steps.regex-match.outputs.match }}' --output packages
- name: Publish Package
run: nuget push **\*.nupkg -NoSymbols -Source 'https://api.nuget.org/v3/index.json' -ApiKey ${{secrets.NUGET_API_KEY}}
- name: Upload Package
uses: actions/upload-artifact@v2
with:
name: fruitless-pkg-v${{ steps.regex-match.outputs.match }}
path: packages/
The pipeline explained
So the initial section of this pipeline is stating that this pipeline will only be triggered when a tag is applied to a branch that starts with a
v
.The next section details a single job to run called
build
and this build has an if the condition that means if the branch is notmain
then this job will not run and will be skipped. This contains a set of steps detailed below.It then prints the tag name that has triggered this pipeline.
It is then using a community provided regex tool to match the
Major.Minor.Patch
section from a string. (A tag ofv1.2.3
would extract1.2.3
).It then simply prints out the extracted version number extract via the match step.
Next it is just setting up the dotnet sdk version to use.
Then the project is built using
dotnet build
(tests can also be run at this point usingdotnet test
).Then the package is packed using the package version extracted from step 4.
Last but not least, the package is pushed using an API key for nuget.org stored as a secret for this GitHub repository.
Finally, the
.nupkg
file/files are uploaded as an artifact for that build inside of GitHub actions.
Final Words
This is a simple way to get started with versioning packages and having them published via GitHub actions. The repository that this sample comes from is here. You can also see my trial and error here (everything doesn't always work the first time).
This can be expanded on in order to remove the regex part of the pipeline is to make use of the nuget package MinVer. This approach has been taken on one of my OSS projects and can be found here.
There are a few concepts glossed over here in terms of how GitHub Actions works and some of its syntax the documentation can be found here.