| File: Utils\ContainerImageUrlHelpers.cs | Web Access |
| Project: src\src\Aspire.Dashboard\Aspire.Dashboard.csproj (Aspire.Dashboard) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics.CodeAnalysis; namespace Aspire.Dashboard.Utils; internal static class ContainerImageUrlHelpers { /// <summary> /// Attempts to build a browsable registry page URL for a container image reference, such as /// <c>redis:7</c>, <c>mcr.microsoft.com/dotnet/aspire-dashboard:9.0</c>, or <c>myuser/myimage:latest</c>. /// </summary> /// <remarks> /// Only registries with a well-known, stable web catalog URL scheme are supported (Docker Hub and MCR). /// Other registries (private registries, GHCR, etc.) don't have a URL scheme we can confidently construct /// without risking a broken or misleading link, so they're left without a link. /// </remarks> public static bool TryGetRegistryPageUrl(string image, [NotNullWhen(returnValue: true)] out string? url) { url = null; // Image references look like: [registry[:port]/]repository[:tag|@digest] var reference = image; var atIndex = reference.IndexOf('@'); if (atIndex >= 0) { reference = reference[..atIndex]; } string? registry = null; var slashIndex = reference.IndexOf('/'); if (slashIndex >= 0) { var firstSegment = reference[..slashIndex]; // A registry host contains a '.' or ':' (port), or is exactly "localhost"; otherwise the first // path segment is a Docker Hub namespace (e.g. "library" or a username), not a registry host. if (firstSegment.Contains('.') || firstSegment.Contains(':') || firstSegment == "localhost") { registry = firstSegment; reference = reference[(slashIndex + 1)..]; } } var colonIndex = reference.LastIndexOf(':'); var repository = colonIndex >= 0 ? reference[..colonIndex] : reference; if (string.IsNullOrEmpty(repository)) { return false; } switch (registry) { case null or "docker.io" or "index.docker.io" or "registry-1.docker.io": // Docker Hub. Official images have no namespace and live under "_/<repo>"; namespaced // images (user or org) live under "r/<namespace>/<repo>". The "library/" namespace is // Docker Hub's internal name for official images, so strip it back to the "_/<repo>" form. repository = repository.StartsWith("library/", StringComparison.Ordinal) ? repository["library/".Length..] : repository; url = repository.Contains('/') ? $"https://hub.docker.com/r/{repository}" : $"https://hub.docker.com/_/{repository}"; return true; case "mcr.microsoft.com": url = $"https://mcr.microsoft.com/en-us/product/{repository}"; return true; default: return false; } } }