.NET Core tools and Github Actions

Thu Jul 02 2020

TL;DR .NET Core tools are a great way of developing small platform-independent command line tools with C# and .NET. Github Actions are really great for automating the whole process of building, and publishing these tools.

Introduction

Recently I have been switching a lot between Powershell on Windows and bash in Ubuntu in WSL to get some of our openEHR components in .NET running on Linux.

Since I was switching back and forth between the different shells, there were times when I wished I had access to the same tools in both shells. For example, in the development process I wanted to remove all build artifacts to make a clean build. On Ubuntu I could run a command such as find . -name bin -exec rm -rf {} \; to recursively find and remove all folders named bin. In Powershell, I had to find a separate command and run that etc. etc.

Since I had been hearing a lot about .NET Core tools, and building code with Github Actions, I thought that it would be a fun experiment to create a small tool for removing directories, and publishing it as a .NET Core tool to nuget.org.

Background

When we compile code or fetch dependencies in .NET projects, the build tools usually put the build artifacts into bin/ and obj/ folders within the project. Once in a while it might be useful to clean out these directories.

When you install the .NET Core CLI you get a bunch of different CLI tools for developing your software. dotnet clean is a tool that cleans out the output from the previous build. However, it only cleans out build artifacts specified in a MSBuild project or solution. And the project had been built on Windows, running dotnet clean on Ubuntu in WSL resulted in error messages such as NuGet.Packaging.Core.PackagingException: Unable to find fallback package folder 'C:\Program Files\dotnet\sdk\NuGetFallbackFolder'. which does not make a lot of sense on Ubuntu.

In addition, the library I was working on targeted both net461 and netstandard2.0, and since .NET Framework is not a thing on Ubuntu, running dotnet clean after building it on Windows, left net641 dlls and other build artifacts in the output directories.

As I already mentioned, in bash on Ubuntu I had a command for removing all bin/ and obj/ folders, but I wanted to run this command both from Powershell and bash. So instead of finding a similar command in Powershell, I wrote my first .NET Core tool, scrub.

Step 1: Build the .NET Core tool

I followed the guide on how to create a .NET Core tool at docs.microsoft.com where it all started with dotnet new console scrub. To parse command line flags etc. I used System.CommandLine.DragonFruit which is great and easy to use. When I was mostly happy with scrub I packed it up with dotnet pack locally and installed it with dotnet tool install to see that everything worked as planned.

Step 2: Push to Github and create a Workflow

I set up a scrub repository and created a .NET Core workflow in Github Actions from the starter workflows. This workflow simply restores the dependencies and builds the project. A Github Workflow is just a yaml file with the different steps of the build. In the starter workflow these are simply dotnet restore and dotnet build. Below is the starter workflow, which I copied from the starter-workflows repository.

name: .NET Core

on:
  push:
    branches: [master]
  pull_request:
    branches: [master]

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v2
      - name: Setup .NET Core
        uses: actions/setup-dotnet@v1
        with:
          dotnet-version: 3.1.101
      - name: Install dependencies
        run: dotnet restore
      - name: Build
        run: dotnet build --configuration Release --no-restore
      - name: Test
        run: dotnet test --no-restore --verbosity normal

The workflow above is fairly simple: it gets the dependencies, builds the project, and runs the tests. You can specify what version of the .NET SDK

The Github workflows are very similar to Azure Pipelines if you have tried them out.

Step 3: Get an API Key

To push packages to nuget.org you'll need a user account, and an API KEY. I set up my account and created an API here.

When I had my API key, I stored it as a secret in my repository, and named it NUGET_API_KEY which I could use in my Workflow.

Step 4: Update the workflow to get it to push to nuget.org

To get a .NET Core tool pushed to nuget.org you simply need to add a dotnet pack and a dotnet nuget push step to the Workflow:

- name: Pack
  run: dotnet pack -c Release -o out
- name: Push generated package to GitHub registry
  run: dotnet nuget push out/*.nupkg -k ${{ secrets.NUGET_API_KEY }} --skip-duplicate --no-symbols true -s https://api.nuget.org/v3/index.json

That's it! It's pushed to nuget.org/packages/scrub and it's possible to download and install it with

dotnet tool install scrub

Conclusion

.NET Core tools are a neat way of developing small platform-independent command line tools. If you come from the go community, you are probably used to getting small tools with go install, and these .NET Core tools are very similar. The largest difference is however that with go you can install a tool from source, or a Github repository, while with the current dotnet tool install you'll have to specify a NuGet package. It would be interesting to try to implement something like dotnet tool install github.com/fjukstad/scrub though!

Github Actions were also nice to get started with. Easy to set up a build, and the build stays together with your code, nice and simple. Would reccommend to a friend!