Building advanced .NET Core Global Tool using CommandLineUtils package

Overview on the new feature in .NET Core 2.1 and how to build and use your own tool

Microsoft introduced a new feature called Global Tool with the new .NET Core release 2.1. Essentially, global tools are simple console applications which can be invoked from the console. Not a big technical innovation but quite handy in terms of expanding .NET Core CLI. Since .NET Core is cross platform, you can use Global Tools to setup the environment on both Windows and Linux and use them from the Command Prompt, Bash or even invoke them easily from Shell scripts on Linux or PowerShell on Windows and Linux as well with installed PowerShell Core.

Note

If you want to know more on how to setup cross platform PowerShell core and have your PowerShell script running on both Windows and Linux host, check the article at http://bit.ly/2tlWIMb

Global tool console applications can be packed and uploaded to NuGet gallery and then later on easily installed using .NET Core CLI. Let's see how can we build a sample global .NET Core tool, upload it to NuGet gallery, install and use. I will also cover some basic in design like advanced handling of arguments passed to the application from the console.

Let's first start with the basics of argument handling in console applications which is the common thing for any console application regardless we are going to use it as a global tool or not.

Handling the arguments

So far, I guess most of us were not paying much attentions to arguments passed to the console application on their invocation. Mostly because those console applications were build to do a certain purpose and be used by targeted user, rather than as a tool which would be used globally.

Microsoft has already developed a package Microsoft.Extensions.CommandLineUtils and it is available in NuGet gallery. Add a package reference to your .csproj file before writing your tool code

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

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp2.1</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.Extensions.CommandLineUtils" Version="1.1.1" />
  </ItemGroup>

</Project>
    

You are now ready to handle arguments passed to application like a pro. Package allows you to use both short and long argument declarations which makes your tool more usable and more user friendly for using. Here is the example of supporting both long and short argument declaration in application invoking

using Microsoft.Extensions.CommandLineUtils;
using System;

namespace Core.Tool.Sample
{
    class Program
    {
        static void Main(string[] args)
        {
            var cmd = new CommandLineApplication();
            var argInput = cmd.Option("-i | --input <value>", "Input file path", CommandOptionType.SingleValue);
            var argOutput = cmd.Option("-o | --output <value>", "Output file path", CommandOptionType.SingleValue);

            cmd.OnExecute(() =>
            {
                Console.WriteLine(argInput.Value());
                Console.WriteLine(argOutput.Value());
                return 0;
            });

            cmd.HelpOption("-? | -h | --help");
            cmd.Execute(args);
        }
    }
}

    

Once you compile and invoke application, you can invoke it with the following command in your command line 

dotnet Core.Tool.Sample.dll -i c:\temp\file.txt

Same behavior of the application is triggered with the long argument declaration

dotnet Core.Tool.Sample.dll --input c:\temp\file.txt

You can also notice that help argument comes as out of the box feature with configurable argument to use. Invoking the application with any of the declared help (-? or -h or --help)arguments will list all application supported arguments with their description

C:\>dotnet Core.Tool.Sample.dll -h


Usage:  [options]

Options:
  -i | --input <value>   Input file path
  -o | --output <value>  Output file path
  -? | -h | --help       Show help information

Now that we have our sample application with argument handling, we can now prepare it for the NuGet gallery publishing as a tool 

Setting up the NuGet package

.NET Core tools are built as any other other console applications and can be published easily to NuGet gallery. The only difference is in the NuGet package configuration where we explicitly declare that the package is a tool. This setting is available in .csproj file

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

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp2.1</TargetFramework>
    <PackAsTool>true</PackAsTool>
    <GeneratePackageOnBuild>true</GeneratePackageOnBuild>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.Extensions.CommandLineUtils" Version="1.1.1" />
  </ItemGroup>

</Project>

    

I also added GeneratePackageOnBuild attribute with value true, so now on every build we will have the NuGet .nupkg package file built and ready be uploaded to NuGet gallery. Next step is obviously uploading the generated package file to NuGet gallery and installing it with the CLI command.

Navigate to https://www.nuget.org/packages/manage/upload page and upload your .nupkg file by following the wizard steps. Make sure you have most of the NuGet properties declared because the more data you have, the more transparent your tool will look like and you will gain more trust from users and encourage them to install your tool from the NuGet gallery and use it. Package details can be populated from Visual Studio UI from the project properties or you can do it directly in the .csproj XML file.

Here are some sample properties of the NuGet package you should declare

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

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp2.1</TargetFramework>
    <PackAsTool>true</PackAsTool>
    <GeneratePackageOnBuild>true</GeneratePackageOnBuild>
    <Description>Sample tool</Description>
    <Copyright>Copyright 2018 dejanstojanovic.net</Copyright>
    <PackageLicenseUrl></PackageLicenseUrl>
    <PackageProjectUrl></PackageProjectUrl>
    <RepositoryUrl>https://github.com</RepositoryUrl>
    <AssemblyVersion>0.9.0.1</AssemblyVersion>
    <FileVersion>0.9.0.1</FileVersion>
    <PackageTags>sample dotnet dotnetcore tool</PackageTags>
    <Version>0.9.1</Version>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.Extensions.CommandLineUtils" Version="1.1.1" />
  </ItemGroup>

</Project>

    

Once you publish your tool, it will take some time for NuGet support to approve your package. Once it is approved, you will receive and email from the NuGet support and you can test your tool by installing it from the command line 

dotnet install tool -g Core.Tool.Sample

If you get the following error, it means that that your NuGet package is not probably published yet and you will have to wait for some more time

error NU1101: Unable to find package core.tool.sample. No packages exist with this id in source(s): C:\Program Files\dotnet\sdk\NuGetFallbackFolder, Microsoft Visual Studio Offline Packages, nuget.org
The tool package could not be restored.
Tool 'core.tool.sample' failed to install. This failure may have been caused by:

* You are attempting to install a preview release and did not use the --version option to specify the version.
* A package by this name was found, but it was not a .NET Core tool.
* The required NuGet feed cannot be accessed, perhaps because of an Internet connection problem.
* You mistyped the name of the tool.

This is a common message which I also encountered with this sample tool project even after receiving publishing confirmation email from nuget support. If you already received an email from support about the package publishing, do not worry and give it some time. Once tool becomes available it will be installed and you will be ready to use it in your environment.

After successful tool installation you can invoke it from the command line without even involving dotnet CLI prior to your project name, so the following command will work without any problem

C:\>Core.Tool.Sample -i c:\temp\file.txt

References

Disclaimer

Purpose of the code contained in snippets or available for download in this article is solely for learning and demo purposes. Author will not be held responsible for any failure or damages caused due to any other usage.


About the author

DEJAN STOJANOVIC

Dejan is a passionate Software Architect/Developer. He is highly experienced in .NET programming platform includion ASP.NET MVC and WebApi. He likes working on new technologies and exciting challenging projects

CONNECT WITH DEJAN  Loginlinkedin Logintwitter Logingoogleplus Logingoogleplus

JavaScript

read more

SQL/T-SQL

read more

Umbraco CMS

read more

PowerShell

read more

Comments for this article