Notes for me

Check for outdated packages in dotnet solution

As part of a CI process we needed to regularly check for outdated packages for a dotnet solution. This particular solution used Central Package Management.

Before reading ahead, there is a global tool dotnet-outdated that does all this so before writing your own code consider using that.

We didn’t for this solution as we already have a fairly comprehensive set of rake-based commands for dev infrastructure and wanted to be self-contained without requiring additional commands to be installed.

Given this was rake (ruby make), the following code is in ruby:

def self.outdated_packages(sln_or_proj_path, only_minor = false)

  dir = File.dirname(sln_or_proj_path)

  json = `dotnet list #{sln_or_proj_path} package --outdated #{only_minor ? "--highest-minor" : ""} --format json`

  data = JSON.parse(json)

  transformed = {
    packages: [
    ]
  }

  data["projects"].each do |project|

    unless project["frameworks"].nil?
      project["frameworks"].each do |framework|
        framework["topLevelPackages"].each do |package|
          transformed_package = transformed[:packages].find {|p| p[:id] == package["id"] && p[:current] == package["resolvedVersion"]}
          if transformed_package.nil?
            transformed[:packages] << {
              "id": package["id"],
              "current": package["resolvedVersion"],
              "latest": package["latestVersion"],
              "projects": [
                project["path"]
              ]
            }
          else
            transformed_package[:projects] << project["path"]
          end
        end
      end
    end
  end

  if transformed[:packages].empty?
    puts "All packages up-to-date"
  else
    sorted = transformed[:packages].sort_by { |p| p[:id] }
    sorted.each do |package|
      printf "%-30s %-15s %-15s\n", package[:id], package[:current], package[:latest]
      sorted_projects = package[:projects].sort
      sorted_projects.each do |project|
        puts "\t#{project.sub(dir, ".")}"
      end
    end
  end
end

The output will look something like:

Microsoft.NET.Test.Sdk         17.8.0          17.9.0         
	/proj1/tests/IcCloud.Account.Tests.csproj
	/proj2/tests/IcCloud.Common.Tests.csproj
	/proj3/tests/IcCloud.Financials.Tests.csproj
	/proj4/tests/IcCloud.Inventory.Tests.csproj
Quartz.Extensions.Hosting      3.6.3           3.8.0          
	/app/serviceapp.csproj

Of course you might want to get the result into machine readable format so you could act on any outdated packages (e.g. automate issuing a pull request to update to version xyz).

But for now that is left as an exercise for the reader.