Introduction
Learn how to create a dotnet Global Tool that lists all NET Core Frameworks with release and end-of-life information.
💡 For my other article on creating various dotnet tools see
C# .NET Tools with System.CommandLine.
This tool is extremely simple: unlike most tools, there are no required arguments or help; the code reads information from the following URL and displays it.
Main code
Located in CommonLibrary project
using CommonLibrary.Models;
using System.Text.Json;
namespace CommonLibrary;
public static class DotNetReleaseService
{
private static readonly HttpClient _httpClient = new();
public static async Task<List<ReleaseIndexItem>> GetReleaseIndexAsync(CancellationToken cancellationToken = default)
{
const string url = "https://dotnetcli.azureedge.net/dotnet/release-metadata/releases-index.json";
using HttpResponseMessage response = await _httpClient.GetAsync(url, cancellationToken);
response.EnsureSuccessStatusCode();
await using var stream = await response.Content.ReadAsStreamAsync(cancellationToken);
var root = await JsonSerializer.DeserializeAsync<ReleasesIndexRoot>(
stream,
Options,
cancellationToken);
return root?.ReleasesIndex ?? [];
}
public static JsonSerializerOptions Options
=> new() { PropertyNameCaseInsensitive = true };
}
public sealed class ReleaseIndexItem
{
[JsonPropertyName("channel-version")]
public string? ChannelVersion { get; set; }
[JsonPropertyName("latest-release")]
public string? LatestRelease { get; set; }
[JsonPropertyName("latest-release-date")]
public DateTime? LatestReleaseDate { get; set; }
[JsonPropertyName("security")]
public bool Security { get; set; }
[JsonPropertyName("latest-runtime")]
public string? LatestRuntime { get; set; }
[JsonPropertyName("latest-sdk")]
public string? LatestSdk { get; set; }
[JsonPropertyName("product")]
public string? Product { get; set; }
/// <summary>
/// Gets or sets the support phase of the release.
/// </summary>
/// <remarks>
/// The support phase indicates the current lifecycle stage of the release,
/// such as "active", "preview", or "eol" (end of life).
/// </remarks>
[JsonPropertyName("support-phase")]
public string? SupportPhase { get; set; }
[JsonPropertyName("eol-date")]
public DateTime? EndOfLifeDate { get; set; }
[JsonPropertyName("release-type")]
public string? ReleaseType { get; set; }
[JsonPropertyName("releases.json")]
public string? ReleasesJsonUrl { get; set; }
}
public sealed class ReleasesIndexRoot
{
[JsonPropertyName("releases-index")]
public List<ReleaseIndexItem>? ReleasesIndex { get; set; }
Entry point code
using CommonLibrary;
using Spectre.Console;
using SpectreConsoleLibrary.Core;
using System.CommandLine;
namespace FrameworkLifeCycle;
internal partial class Program
{
static async Task Main(string[] args)
{
RootCommand rootCommand = new("Get dotnet framework life cycles");
var releases = await DotNetReleaseService.GetReleaseIndexAsync();
SpectreConsoleHelpers.InfoPill(Justify.Left, $"Found {releases.Count} releases.");
Console.WriteLine();
var table = new Table().Title("[bold blue] .NET Release Information [/]");
table.AddColumn(new TableColumn("[bold yellow]Channel[/]"));
table.AddColumn(new TableColumn("[bold yellow]Latest[/]"));
table.AddColumn(new TableColumn("[bold yellow]ReleaseType[/]"));
table.AddColumn(new TableColumn("[bold yellow]End Of Life Date[/]"));
table.AddColumn(new TableColumn("[bold yellow]Support[/]"));
foreach (var item in releases)
{
var eolText = item.EndOfLifeDate.HasValue
? item.EndOfLifeDate.Value.ToString("MM/dd/yyyy")
: "Not set";
var releaseType = item.ReleaseType ?? "Unknown";
if (releaseType != "Unknown")
{
releaseType = releaseType.ToUpper();
}
table.AddRow(
FrameworkUtilities.IsProjectFramework(item.ChannelVersion ?? ""),
item.LatestRelease ?? "",
releaseType,
eolText,
Colorize(item.SupportPhase ?? "Unknown"));
}
AnsiConsole.Write(table);
Console.WriteLine();
}
private static string Colorize(string input) =>
input switch
{
{ } s when s.Contains("active", StringComparison.OrdinalIgnoreCase) => "[green]Active[/]",
{ } s when s.Contains("eol", StringComparison.OrdinalIgnoreCase) => "[red]eol[/]",
{ } s when s.Contains("preview", StringComparison.OrdinalIgnoreCase) => "[yellow]preview[/]",
_ => input,
};
}
Project file
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<PackAsTool>true</PackAsTool>
<ToolCommandName>flc</ToolCommandName>
<PackageOutputPath>./nupkg</PackageOutputPath>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
<Version>1.0.0</Version>
</PropertyGroup>
- PackAsTool Indicate this as a dotnet tool
- ToolCommandName the command to run as at the command line
- PackageOutputPath Path to binaries
Summary
Once installed, typing flc will list all dot net Framework life cycles and for many developers be their first useful donet tool.
💡 For my other article on creating various dotnet tools, see
C# .NET Tools with System.CommandLine, which shows how to work with arguments and provide help.

Top comments (0)