NashTech Insights

List All 3rd Party Packages Being Used In Your Project – Part 2: Create Your Own .Net CLI Tool

Phong Nguyen
Phong Nguyen
Table of Contents

Introduction

In my previous post How To List All 3rd Packages Being Used In Your Project, I demonstrated how to list Nuget packages and Npm packages for C# and ReactJs projects by creating and executing Console application, but rather than executing manually, Isn’t it cooler if we can pack and install the application as a global CLI tool so that we can execute it anywhere in any folder. In this article, I’m gonna show you how.

Pack The Console Application as a NuGet Package

Let’s create another Console application named CheckNugetPackages.DotNetCliTool using Visual Studio 2022 or Visual Studio Code.

Copy the code inside the Program.cs class of the CheckNugetPackages application in the previous post and paste it into the Program.cs class of the CheckNugetPackages.DotNetCliTool application, then we have the code as below:

using System.Xml.Linq;

var packages = new List<(string Name, string Version, string Project)>();

var directories = new[]
{
    @"D:\Project1\API",
    @"D:\Project2\API",
};

foreach (var directory in directories)
{
    var packagesInPackagesConfigureFiles = ScanPackagesInPackagesConfigureFiles(directory);
    var packagesInCsProjectFiles = ScanPackagesInCsProjectFiles(directory);
    packages.AddRange(packagesInPackagesConfigureFiles);
    packages.AddRange(packagesInCsProjectFiles);
}

var packageGroups = packages.GroupBy(x => new { x.Name, x.Version })
    .Select(g => new
    {
        g.Key.Name,
        g.Key.Version,
        Projects = string.Join(", ", g.Select(x => x.Project)),
        Url = $"https://www.nuget.org/packages/{g.Key.Name}/{g.Key.Version}"
    })
    .OrderBy(x => x.Name)
    .ThenBy(x => x.Version).ToList();

var ignoredPackages = new List<string>
{
    //"System.",
    //"Microsoft."
};

using (var fileStream = File.Open("packages.csv", FileMode.Create))
{
    using (var streamWriter = new StreamWriter(fileStream))
    {
        foreach (var package in packageGroups)
        {
            if (ignoredPackages.Any(x => package.Name.StartsWith(x)))
            {
                continue;
            }

            streamWriter.WriteLine($"{package.Name},{package.Version}, ,\"{package.Url}\",\"{package.Projects}\"");
        }
    }
}

static List<(string Name, string Version, string Project)> ScanPackagesInPackagesConfigureFiles(string directory)
{
    var files = Directory.GetFiles(directory, "packages.config", SearchOption.AllDirectories);
    var packages = new List<(string Name, string Version, string Project)>();
    foreach (var file in files)
    {
        var projectName = new DirectoryInfo(Path.GetDirectoryName(file)).Name;
        XDocument xdoc = XDocument.Load(file);
        var packagesNode = xdoc.Descendants("packages").First();
        var packageNodes = packagesNode.Descendants("package");
        foreach (var node in packageNodes)
        {
            var packageName = node.Attribute("id")?.Value;
            var packageVersion = node.Attribute("version")?.Value;

            packages.Add((packageName, packageVersion, projectName));
        }
    }

    return packages;
}

static List<(string Name, string Version, string Project)> ScanPackagesInCsProjectFiles(string directory)
{
    var files = Directory.GetFiles(directory, "*.csproj", SearchOption.AllDirectories);
    var packages = new List<(string Name, string Version, string Project)>();
    foreach (var file in files)
    {
        var projectName = new DirectoryInfo(Path.GetDirectoryName(file)).Name;
        XDocument xdoc = XDocument.Load(file);
        var ItemGroupNodes = xdoc.Descendants("ItemGroup");
        foreach (var ItemGroupNode in ItemGroupNodes)
        {
            var packageNodes = ItemGroupNode.Descendants("PackageReference");
            foreach (var node in packageNodes)
            {
                var packageName = node.Attribute("Include")?.Value;
                var packageVersion = node.Attribute("Version")?.Value;

                if (string.IsNullOrWhiteSpace(packageName))
                    continue;

                packages.Add((packageName, packageVersion, projectName));
            }
        }

    }

    return packages;
}

Let’s change the below code:

var directories = new[]
{
    @"D:\Project1\API",
    @"D:\Project2\API",
};

to:

var directories = args;

So rather than hard coding the input, now the application can receive input from the arguments being passed from outside before executing.

Next, we need to update the CheckNugetPackages.DotNetCliTool.csproj to add some instructions:

<PackAsTool>true</PackAsTool>
<ToolCommandName>CheckNugetPackages</ToolCommandName>
<PackageOutputPath>./nupkg</PackageOutputPath>

Let’s publish the NuGet package by right clicking and select Pack, or we can simply type dotnet pack command.

Install The NuGet Package as a .Net CLI Tool

Now we have the NuGet package published, let’s open the CheckNugetPackages.DotNetCliTool folder, open cmd and type:

dotnet tool install CheckNugetPackages.DotNetCliTool --global --add-source ./nupkg

To test the new CLI tool, let’s use the same GitHub code Repo in the previous post:

cd D:\Phong.NguyenDoan\GitHub\Practical.CleanArchitecture\src\Microservices
CheckNugetPackages ./

Wait for it to execute successfully, now we have the same packages.csv file generated exactly in the previous post.

Wrap Up

Up until now, our tool has been improved a lot and is much more reusable, in the next article, I’ll show you how we can publish the tool to the official NuGet website so that we can integrate and run it as a Task in an Azure DevOps CICD Pipeline.

As usual, the full source code for this demo is published at https://github.com/phongnguyend/blog.nashtechglobal.com please check that out.

Phong Nguyen

Phong Nguyen

Phong is currently working as Technical Architect at NashTech, has over 12+ years of experience in designing, building and integrating Enterprise Applications. He is interested in Performance Optimization, Security, Code Analysis, Architecture and Cloud Computing.

Leave a Comment

Your email address will not be published. Required fields are marked *

Suggested Article

%d