Publish Your .NET Library with NuGet
What is NuGet?
NuGet is a package manager for .NET platform. By using NuGet tools, we can easilly install and publish libraries. In this article, I'm going to talk about how to initialize a .NET class library with Test Driven Development (TDD) and how to set up Github Actions to achieve Continuous Integration (CI) & Continuous Deployment (CD).
Prerequisites.
- First, we should make sure that .NET Core SDK has been sucessfully installed on our machine.
- And we need to register a free account on nuget.org.
Initialize .NET class library.
Now, we use dotnet CLI to create a class library project.
dotnet new classlib -o SomeProject && cd SomeProjectThen, we should add package metadata to the project file (SomeProject/SomeProject.csproj).
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<PackageId>SomeProject</PackageId>
<Description>A sample .NET library</Description>
<Authors>Grey Wang</Authors>
<RepositoryUrl>https://github.com/aguang-xyz/some-project-dotnet</RepositoryUrl>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<EnableDefaultCompileItems>false</EnableDefaultCompileItems>
</PropertyGroup>
<ItemGroup>
<Compile Include="SomeProject.cs" />
</ItemGroup>
</Project>
PackageIdis the slug of your project showing on nuget.org.Descriptionindicates a text descriping your project.Authorsindicates the list of authors' names (comma separated).RepositoryUrlis the url of the home page of your project.PackageLicenseExpressionindicates the license of your project. And we can also use PackageLicenseFile or PackageLicenseUrl to indicate the license.- Setting
EnableDefaultCompileItemstofalseand adding aCompile/Includeoption indicate that the compiler will not import all source files automatically. Because we will create a subfolder as a test project later.
Initialize test project.
To make sure the reliability of your implementation, we should also add a test project to cover the functionalities. There are serveral choices of unit test frameworks in .NET platform (xUnit, NUnit, MSTest) to setup our test project.
dotnet new mstest -o SomeProjectTestSince the main project will be used by the test project, we should make a reference from main project to test project.
dotnet add SomeProjectTest/SomeProjectTest.csproj reference SomeProject.csprojDevelopment.
In this sample, we plant to implement a very simple class called SomeCounter. It contains only a method AddAndGet. Every time you call this method, it should increment the counter and return value after incrementation.
Here is the test code (SomeProjectTest/SomeProjectTest.cs).
using Microsoft.VisualStudio.TestTools.UnitTesting;
using SomeProject;
namespace SomeProjectTest
{
[TestClass]
public class SomeProjectTest
{
[TestMethod]
public void TestCounter()
{
var counter = new SomeCounter();
Assert.AreEqual(1, counter.AddAndGet());
}
}
}Here is the source code (SomeProject.cs):
namespace SomeProject
{
public class SomeCounter
{
private int count = 0;
public int AddAndGet()
{
return ++count;
}
}
}Run the unit tests.
Now we can run the test cases to check the correcness of our code. By using the additional parameter logger, the logs (if we have) can be displayed on the terminal.
dotnet test --logger "console;verbosity=detailed" SomeProjectTestInitialize Git repository.
Now we initialize the git repository.
git initTo ignore the build output, we should create a .gitignore first.
bin/
obj/We commit the changes.
git add -A
git commit -m "Initialize SomeProject."And we create a tag, the tag will used as the version in the next step.
git tag -a v1.0.0Build & publish the NuGet package manually.
Now we can start to build the NuGet package by running the command above. After execution, we should find a NuGet package file (bin/Debug/SomeProject.1.0.0.nupkg).
In this command, we take ${"$(git describe --tags --abbrev=0)":1} to read the latest git tag (v1.0.0), skip the first character ('v') and use the rest ("1.0.0") as the automatic package version.
dotnet pack -p:PackageVersion=${"$(git describe --tags --abbrev=0)":1}To publish the NuGet package, we should prepare for the API key first. It can be created via NuGet's dashboard. And the we can run the following command to publish the package manually. HINT: Due to Microsoft's security strategy, it may take hours to do the package validation and indexing.
dotnet nuget push ./bin/Debug/SomeProject.1.0.0.nupkg \
--api-key {API_KEY} \
--source https://api.nuget.org/v3/index.jsonCI & CD workflow.
Now we want to combine the previous several steps (run test cases, build and deploy) together to setup up a Continuious Integration (CI) & Continuous Deployment (CD) workflow based Github actions. The above yaml file (.github/workflows/build-dotnet.yml) is an example to show how the CI & CD action works.
name: build/dotnet
on: [push, pull_request]
jobs:
release:
runs-on: ubuntu-latest
steps:
- name: Checkout Git repository.
uses: actions/checkout@v1
- name: Set up dotnet CLI.
uses: actions/setup-dotnet@v1
with:
dotnet-version: 3.1.302
- name: Run unit tests.
run: |
dotnet test --logger "console;verbosity=detailed" SomeProjectTest
- name: Build a NuGet package.
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags')
run: |
dotnet pack -p:PackageVersion=${GITHUB_REF#refs/tags/v}
- name: Publish the NuGet packge.
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags')
run: |
dotnet nuget push ./bin/Debug/SomeProject.*.nupkg \
--api-key ${API_KEY} \
--source https://api.nuget.org/v3/index.json
env:
API_KEY: ${{secrets.NUGET_API_KEY}}We need to config a secret key NUGET_API_KEY via the Github dashboard. Then, every time after we push commits or submit a pull request, we will see if all the test cases can be passed. And after each time we push a new tag, it will build a NuGet package and deploy to nuget.org automatically.