Como vimos nos posts anteriores (.NET Core 2.1 Preview 1 e Global Tools) as Global Tools são parte das novas adições nesta versão do .NET Core. Esse texto é um tutorial simples mostrando como criar uma global tool.

Atenção! .NET Core 2.1 está em Preview 1, é possível que até a data de lançamento (ou a data que você esteja lendo este post) algo tenha mudado. Se for o caso, deixe um comentário que farei o possível para atualizar.

O que é uma Global Tool

Além da explicação dada no artigo anterior, uma Global Tool nada mais é do que uma aplicação console feita em .NET Core, simples assim. Não tem nada secreto, misterioso ou novo. A grande novidade é identificar o que é uma ferramenta que valha ser criada e distribuída desta forma.

Sendo uma aplicação console, a complexidade vai realmente depender do que você deseja fazer. Para este artigo, vamos criar uma tool que fornece a última cotação de Bitcoin em USD (dólares americanos) ou BRL (reais).

Nosso objetivo será ter uma ferramenta que faça (algo parecido com) isso:

$ cryptool BRL
R$ 25.593,05

ou

$ cryptool
USD 7.845,45
EUR 6.336,41
GBP 5.610,46

Notem que este comando está sendo executado diretamente no console/terminal, tanto no Windows quanto no Mac/Linux. É assim que as global tools se comportam e funcionam.

Criação do projeto

Todos os passos aqui serão executados em um Mac, rodando o .NET Core 2.1 Preview 1. Eu usarei a linha de comando para executar os comandos .NET e o Visual Studio Code para a edição do código. Todos os passos aqui descritos devem funcionar no Windows ou no Visual Studio. Vou colocar imagens logo após os comandos para que vocês possam conferir o output padrão.

$ dotnet new console --name cryptool

Feito isso, vamos abrir nossa solution e editar diretamente o arquivo Program.cs. Vou colocar o código inteiro aqui, e explico logo abaixo. O que a aplicação faz, de forma bastante simples é:

    1. Decidir se foi passado algum argumento, e se foi verificamos se ele é válido;
    1. Retornar or argumento passado formatado para nossa URI ou uma string vazia;
    1. Realizar uma chamada para a API
    1. Exibir informação no console (vermelho em caso de erro).
using System;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Text.RegularExpressions;
using System.Threading.Tasks;

namespace cryptool
{
    class Program
    {
        static HttpClient client = new HttpClient();
        static string ArgsResolver(string[] args)
        {
            if(args.Any() && Regex.IsMatch(args[0], "^[a-zA-Z]{3}$"))
                return $"/{args[0]}";
            
            return string.Empty;
        }

        static async Task Main(string[] args)
        {
            var uri = $"https://api.coindesk.com/v1/bpi/currentprice{ArgsResolver(args)}.json";

            var result = await client.GetAsync(uri);
            var content = await result.Content.ReadAsStringAsync();

            if (result.StatusCode != HttpStatusCode.OK)
            {
                Console.WriteLine("Ocorreu um problema 😬");
                Console.ForegroundColor = ConsoleColor.Red;
            }

            Console.WriteLine(content);          
        }
    }
}

O código acima é bastante simples, encontrei uma api pública na Internet que fornece as informações de preço do Bitcoin, então tudo que faço é um request para essa API. Neste ponto já podemos executar nosso código, seja dando um play no Visual Studio Code/Visual Studio/Visual Studio for Mac, ou então pelo console, usando:

$ dotnet run

ou

$ dotnet run BRL
Rodando Global Tool com doente run

Rodando Global Tool com dotnet run

Sua tool poderia fazer qualquer outro tipo de coisa: se conectar ao Azure, enviar emails, gerar memes, postar algo no Twitter, conferir o status dos servidores de aplicação que você possui, baixar e executar uma outra global tool (dx, projeto do Giovanni), listar processos dotnet core (dotnetps, uma outra tool que eu criei), e muito mais. As possibilidades são diversas.

Empacotando com nuget

Para podermos distribuir nossa app como uma global tool, via nuget, permitindo que seja instalada com o comando dotnet install precisamos utilizar o nuget para gerar o pacote. Antes de gerar o pacote, vamos atualizar algumas informações no .csproj. Adicione algo parecido com o código abaixo antes do fechamento de </PropertyGroup>:

 
<PackAsTool>true</PackAsTool>
 <Version>1.0.0</Version>
 <Description>A tool for obtaining the latest Bitcoin value in USD, EUR, GBP or BRL.
Install with the .NET CLI 2.1 or newer:
 dotnet install tool -g cryptool
  
 Run cryptool from the command line, for example:
 `cryptool` or `cryptool BRL`
 </Description>

Agora já podemos gerar nosso pacote. Na pasta raiz do nosso projeto vamos executar o seguinte comando:

$ dotnet pack . -c Release -o nupkg

O que este comando faz é:

  1. Faz o build da aplicação para release (-c ou –configuration);
  2. Define o diretório onde será colocado o pacote gerado (-o ou –output)

Com isso devemos ter um arquivo .nupkg na pasta nupkg.

Pacote .nupkg gerado

Pacote .nupkg gerado

Testando a instalação do pacote localmente

É possível realizar o teste da geração do pacote localmente. Para isso vamos configurar, dentro da pasta nupkg um arquivo de nuget.config com o seguinte conteúdo:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
    <packageSources>
        <clear/>
        <add key="local-packages" value="." />
    </packageSources>
</configuration>

O que este arquivo quer dizer é: sempre que o comando nuget for executado de dentro desta pasta, ignore todas as outras fontes do nuget e procure os pacotes somente dentro desta pasta. Com isso poderemos instalar nosso pacote sem publicá-lo no nuget.org, o que é extremamente válido durante o desenvolvimento.

Feito isso, basta executar o seguinte comando, dentro da pasta nupkg:

$ dotnet install tool -g cryptool
Instalando pacote localmente

Instalando pacote localmente

Finalizando

Como vimos ao longo desse breve artigo, a criação de uma Global Tool, no .NET Core 2.1 é bastante simples. Uma console application empacotada e distribuída via nuget. Como puderam reparar, o código desta global tool que eu criei ainda responde um json na saída do console. Quem quiser enviar um pull request para o parser do json, adicionar tratamento de exception, etc., o código está aqui no Github.

Você também pode instalar esse pacote via nuget, está publicado aqui. A realização do public para o repositório oficial do nuget é bastante simples, e pode ser feita com um único comando:

$ dotnet nuget push --api-key <sua key> -s nuget.org <nome do pacote>.nupkg

Em um outro post podemos abordar melhor a questão da publicação, falando apenas sobre nuget.

Espero comentários de vocês aqui no blog, ou então podem me achar no Twitter como @vquaiato.