Exploring global .NET Core tools

.NET Core allows us to create and run .NET apps on many platforms, but actually shipping our apps is hard, especially if those apps aren’t web apps that use one of the well-integrated publishing methods (WebDeploy, Azure, Docker, …).

Node already allows to package up apps and then install and use them using:

> npm install -g something
> something

The upcoming .NET Core SDK 2.1.300, slated to be released with .NET Core 2.1, will offer a similar functionality:

> dotnet install tool -g something
> something

These applications are downloaded and installed from NuGet and are marked as a special package type.

So let’s build one ourself. Get a preview 2.1.300 .NET Core SDK version from the links at https://github.com/dotnet/cli to follow along. The full sample is available on GitHub at https://github.com/dasMulli/GlobalCliToolsExample.

Some config first

In order to use preview packages from the nightly feed and use our built nuget packages:

  • Create a folder to work in, this will be the base for all commands run
  • Create a folder named nupkgs that will hold our built NuGet packages
  • Run dotnet new nugetconfig to generate a nuget.config file. Edit it to add the custom folder and the CI feed:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
 <packageSources>
    <add key="local-packages" value="./nupkgs" />
    <add key="dotnet-core" value="https://dotnet.myget.org/F/dotnet-core/api/v3/index.json" />
 </packageSources>
</configuration>

Create the app

So we now need an application to work with. As a good sample, let’s create a program that just greets the user:

> dotnet new console -o sayhi.tool

This will create a folder sayhi.tool and a console app and .csproj file inside of it. First, I don’t like “Hello World!” that much, so changed the Program.cs file to something slightly more interesting:

static void Main(string[] args)
{
    Console.WriteLine($"Hi {Environment.UserName}!");
}

Mark the app as tool

In order to be packed correctly so that we can install the app as a tool, we need to change the sayhi.tool.csproj file a little:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp2.1</TargetFramework>
    <PackAsTool>true</PackAsTool>
    <ToolCommandName>sayhi</ToolCommandName>
    <PackageOutputPath>../nupkgs</PackageOutputPath>
  </PropertyGroup>

</Project>

Using netcoreapp2.1 will cause the tool to break between .NET Core 2.1 preview/RC/RTM releaes. You will need to release/publish a new version of your global tool once the framework you target becomes RTM / "stable".

While you may recognize OutputType and TargetFramework from normal csproj files, there are a few new ones here:

  • PackageOutputPath: This tells dotnet pack where to put the .nupkg file to. This propertiy isn’t new and alredy exists in released tooling. For our example, it needs to point to the path we configured in nuget.config.
  • PackAsTool: This instructs the tooling to package our app correctly for being installed as a tool.
  • ToolCommandName: This lets us choose the command line executable name for the app. It defaults to the assembly name of the application, but want to type sayhi instead of sayhi.tool in this example.

Build and run!

In the top level folder for this sample, run:

> dotnet pack -c Release
… 
Successfully created package 'C:\repos\GlobalCliToolsExample\nupkgs\sayhi.tool.1.0.0.nupkg'.

> dotnet install tool -g sayhi.tool

The installation succeeded. If there are no further instructions, you can type the following command in shell directly to invoke: sayhi

You may see some warnings because of prerelease version numbers being used by the preview tooling - you can ignore them.

Now to be safe, open up a new console window. You can skip this if you already used global tools before, but if this is your first dotnet install call, you may still need to do so.

Our program can now be run from the command line using just:

> sayhi
Hi martin.ullrich!

Where is it installed to?

Our sayhi tool is installed into a .dotnet\tools folder in the user’s directory:

tools directory

The install command generates a .exe file as a warpper on windows and shell scripts on macOS / Linux. On windows, this is currently a .NET Framework executable, but it is planned to be replaced with a native executable for an upcoming preview.

Bonus: Integrate with the dotnet command

The dotnet CLI always allowed to use programs named dotnet-something as verbs (=> dotnet something). This is now possible with global tools as well.

To do so, let’s create a similar application greet.tool using the same steps as above, but in greet.tool.csproj, set ToolCommandName differently:

  <PropertyGroup>
    <ToolCommandName>dotnet-greet</ToolCommandName>
  </PropertyGroup>

Now build and install this tool:

> dotnet pack
> dotnet install tool -g greet.tool

And it can be run using:

> dotnet greet
Hi martin.ullrich!

Sample code

The full sample is avilable at https://github.com/dasMulli/GlobalCliToolsExample.

Martin A. Ullrich

Martin A. Ullrich

Martin A. Ullrich
Software Developer in the .NET space. Also do Java, Swift/ObjC and front-end stuff.

Use appsettings.json and environment overrides in classic ASP.NET apps

Supercharge your classic ASP.NET apps with the config system introduced in ASP.NET Core Continue reading

Make dotnet test work on solution files

Published on January 20, 2018