Loupe

Mise en place d'un outil global dotnet avec .NET Core 2.1

Depuis sa version 2.1, .NET Core introduit le concept d'outils globaux, qui permettent d'étendre les fonctionnalités de la ligne de commandes au moyen d'un simple package Nuget.

Si vous êtes développeur Web, vous devez déjà être habitué à ce type de ligne de commande:

npm install -g TypeScript

Celle-ci permet d'ajouter à la ligne de commande, de manière globale, les outils et exécutables nécessaires à l'utilisation de TypeSscript. Et bien il s'avère que depuis la version 2.1, .NET Core propose le même système d'extensibilité via l'utilisation de packages Nuget. Ces packages sont en fait des applications Consoles qui seront installées sur votre poste et qui vous permettront d'utiliser des commandes supplémentaires.

Commençons par créer notre application Console (en .NET Core) qui va, pour l'exemple, utiliser Roslyn pour compter le nombre de chaînes de caractères dans les fichiers CSharp (au lieu d'utiliser des fichiers de ressources). L'exemple est volontairement simple et non optimisé mais cela vous donne une idée du résultat :

using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;

namespace StringsSearcherDotnetTool
{
    class Program
    {
        private static string[] EXCLUDED_TERMS = new string[] { ".g.cs" };

        static void Main(string[] args)
        {
            var searchingFolder = args.Length > 0 ? args[0] : ".";
            var csharpFiles = Directory.GetFiles(searchingFolder, "*.cs", SearchOption.AllDirectories);

            var textNodes = new List<Tuple<string, int, string>>();

            foreach (var file in csharpFiles)
            {
                if (EXCLUDED_TERMS.Any(t => Path.GetFileName(file).Contains(t)))
                    continue;

                using (var reader = new StreamReader(file))
                {
                    var syntaxTree = CSharpSyntaxTree.ParseText(reader.ReadToEnd());

                    var root = (CompilationUnitSyntax)syntaxTree.GetRoot();
                    foreach (var node in root.DescendantNodes())
                    {
                        if (node.Kind() == SyntaxKind.StringLiteralExpression)
                        {
                            // Source: http://source.roslyn.codeplex.com/#Microsoft.CodeAnalysis/Syntax/SyntaxTree.cs,271
                            var lineNumber = syntaxTree.GetMappedLineSpan(node.Span).StartLinePosition.Line + 1;
                            var nodeText = node.ToFullString();

                            textNodes.Add(new Tuple<string, int, string>(file, lineNumber, nodeText));
                        }
                    }
                }
            }

            if (textNodes.Any())
            {
                Console.WriteLine($"Total number of literal strings: {textNodes.Count}");
                foreach (var file in textNodes.GroupBy(t => t.Item1))
                {
                    Console.WriteLine($"File: {file.Key}");

                    foreach (var node in file)
                    {
                        Console.WriteLine($"Line {node.Item2}: {node.Item3}");
                    }
                }
            }
            else
            {
                Console.WriteLine("Congrats, no literal strings found!");
            }
        }
    }
}

Je ne détaille pas le contenu de ce bout de code, ce n'est pas l'objectif, mais voyons ce qu'il se passe si je l'exécute depuis Visual Studio, en passant en paramètres un dossier contenant des fichiers C# :

Results.PNG

L'application se lance et m'affiche la liste des fichiers contenant des chaînes de caractères avec, pour chaque fichier, la liste des chaînes identifiées. Il est à présent temps de transformer cette application en outil global !

Editez le fichier CSPROJ de votre application et ajoutez les informations PackAsTool et ToolCommandName comme dans l'exemple suivant:

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

PackAsTool est la propriété a définir pour indiquer que votre application sera au final un outil d'extensibilité de la ligne de commandes. Quand à ToolCommandName, il s'agit de la propriété qui permet de définir le nom que l'utilisateur devra saisir dans la ligne de commande. Et c'est tout!

Il ne reste plus qu'à générer le fichier nupkg, correspondant à votre package Nuget:

dotnet pack --output .

Vous disposez alors d'un package Nuget que vous pouvez publier sur le site officiel, sur un flux privé VSTS, sur Myget, ou bien que vous pouvez tester en local (pensez à remplacer NOM_DE_VOTRE_PACKAGE_NUGET dans l'exemple ci-dessous):

dotnet tool install -g NOM_DE_VOTRE_PACKAGE_NUGET --add-source .

Si tout se passe bien, vous devriez recevoir un message vous indiquant que vous pouvez à présent utiliser votre outil en ligne de commandes:

NugetAdd.PNG

Pour vérifier que l'outil est bien installé, un petit coup de ligne de commandes peut être utile également:

ToolInstalled.PNG

Et voilà, votre outil en ligne de commande a été installé et il ne vous reste plus qu'à saisir le nom de la commande (avec ses paramètres) pour voir le résultat:

ResultsCommand.PNG

Si vous souhaitez désinstaller cet outil, il vous suffit d'exécuter la commande:

dotnet tool uninstall NOM_DE_VOTRE_PACKAGE_NUGET --global

Et le tour est joué! Grâce à cette technique, vous pouvez donc plus facilement travailler avec les autres membres de votre équipe, leur partager les outils/utilitaires que vous avez développés pour vous aider, etc.

 

Happy coding! :)

 

Ces billets pourraient aussi vous intéresser

Vous nous direz ?!

Commentaires

comments powered by Disqus