| File: Product.cs | Web Access |
| Project: src\deployment-tools\src\Microsoft.Deployment.DotNet.Releases\src\Microsoft.Deployment.DotNet.Releases.csproj (Microsoft.Deployment.DotNet.Releases) |
// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics; using System.IO; using System.Text.Json; using System.Threading.Tasks; namespace Microsoft.Deployment.DotNet.Releases { /// <summary> /// Provides an overview of a single product, including information related to its support level and the latest SDK and runtime releases. /// </summary> [DebuggerDisplay("{DebuggerDisplay,nq}")] public class Product { private string DebuggerDisplay => $"{ProductName} {ProductVersion} ({SupportPhase})"; /// <summary> /// The version of the product, e.g "5.0" or "1.1". /// </summary> public string ProductVersion { get; private set; } /// <summary> /// The end-of-life (EOL) date for this <see cref="Product"/> when it is considered to be out of support. The value /// may be <see langword="null" /> if the EOL date is undetermined, e.g. when a product is still a prerelease. /// </summary> public DateTime? EndOfLifeDate { get; private set; } /// <summary> /// <see langword="True"/> if the latest release of the product contained a security update; /// <see langword="false"/> otherwise. /// </summary> public bool LatestReleaseIncludesSecurityUpdate { get; private set; } /// <summary> /// The date of the latest release for this product. /// </summary> public DateTime LatestReleaseDate { get; private set; } /// <summary> /// The version of the latest release. /// </summary> public ReleaseVersion LatestReleaseVersion { get; private set; } /// <summary> /// The version of the runtime included in the latest release. /// </summary> public ReleaseVersion LatestRuntimeVersion { get; private set; } /// <summary> /// The version of the SDK included in the latest release. /// </summary> /// <remarks> /// This is usually the SDK with the highest feature band. A <see cref="ProductRelease"/> /// may include multiple SDKs across different feature bands, all of which carry the same runtime version. /// </remarks> public ReleaseVersion LatestSdkVersion { get; private set; } /// <summary> /// The name of the product. /// </summary> public string ProductName { get; private set; } /// <summary> /// The URL pointing to the releases.json file that contains information about all the releases /// associated with this <see cref="Product"/>. /// </summary> public Uri ReleasesJson { get; private set; } /// <summary> /// A value indicating the support duration of the product. /// </summary> public ReleaseType ReleaseType { get; private set; } = ReleaseType.Unknown; /// <summary> /// The current support phase of this <see cref="Product"/>. For an LTS release, the <see cref="EndOfLifeDate"/> property should /// be checked to confirm whether a release is still supported. /// </summary> /// <remarks> /// The EOL dates are often published in advance, but there can be delays to updating the support phase in the published /// data. /// </remarks> public SupportPhase SupportPhase { get; private set; } = SupportPhase.Unknown; /// <summary> /// <see langword="true"/> if the support phase is not <see cref="SupportPhase.EOL"/> /// and the current date is less than the EOL date of the product, /// <see langword="false"/> otherwise. /// </summary> /// <returns><see langword="true"/> if the product is currently supported; <see langword="false"/> otherwise.</returns> public bool IsSupported => !IsOutOfSupport(); /// <summary> /// Gets a collection of all releases associated with this <see cref="Product"/>. /// </summary> /// <returns>A collection of all releases for this product.</returns> public Task<ReadOnlyCollection<ProductRelease>> GetReleasesAsync() => GetReleasesAsync(ReleasesJson); internal Product(JsonElement productElement) { ReleaseType = productElement.TryGetProperty("release-type", out JsonElement value) ? Enum.TryParse(value.GetString(), ignoreCase: true, out ReleaseType releaseType) ? releaseType : ReleaseType.Unknown : ReleaseType.Unknown; SupportPhase = productElement.TryGetProperty("support-phase", out value) ? Enum.TryParse(value.GetString()?.Replace("-", ""), ignoreCase: true, out SupportPhase supportPhase) ? supportPhase : SupportPhase.Unknown : SupportPhase.Unknown; if (productElement.TryGetProperty("eol-date", out value)) { EndOfLifeDate = value.ValueKind == JsonValueKind.Null ? null : value.GetDateTime(); } LatestReleaseDate = productElement.GetProperty("latest-release-date").GetDateTime(); LatestReleaseVersion = productElement.GetReleaseVersionOrDefault("latest-release"); LatestReleaseIncludesSecurityUpdate = productElement.GetProperty("security").GetBoolean(); LatestRuntimeVersion = productElement.GetReleaseVersionOrDefault("latest-runtime"); LatestSdkVersion = productElement.GetReleaseVersionOrDefault("latest-sdk"); ProductName = productElement.GetStringOrDefault("product"); ProductVersion = productElement.GetStringOrDefault("channel-version"); ReleasesJson = productElement.GetUriOrDefault("releases.json"); } /// <summary> /// Gets a collection of all releases associated with this <see cref="Product"/> using a file /// containing the releases data. /// </summary> /// <param name="path">The path of the file containing the releases data.</param> /// <param name="downloadLatest">When <see langword="true"/>, the latest copy of the releases data is used /// if the online version is newer than the local file copy.</param> /// <returns>A collection of releases associated with this <see cref="Product"/>.</returns> public async Task<ReadOnlyCollection<ProductRelease>> GetReleasesAsync(string path, bool downloadLatest) { await Utils.GetLatestFileAsync(path, downloadLatest, ReleasesJson).ConfigureAwait(false); using FileStream stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, bufferSize: 4096, useAsync: true); return await GetReleasesAsync(stream, this).ConfigureAwait(false); } /// <summary> /// Creates a new <see cref="ProductRelease"/> collection using the releases.json file pointed to /// by the provided URL. /// </summary> /// <param name="address">The URL pointing to the releases.json file to use.</param> /// <returns>A collection of releases associated with this <see cref="Product"/>.</returns> public async Task<ReadOnlyCollection<ProductRelease>> GetReleasesAsync(Uri address) { if (address == null) { throw new ArgumentNullException(nameof(address)); } using var stream = new MemoryStream(await Utils.s_httpClient.GetByteArrayAsync(address).ConfigureAwait(false)); return await GetReleasesAsync(stream, this).ConfigureAwait(false); } /// <summary> /// <see langword="true"/> if the support phase is <see cref="SupportPhase.EOL"/> /// or the current date is greater than or equal to the EOL date of the product, /// <see langword="false"/> otherwise. /// </summary> /// <returns><see langword="true"/> if the product is out of support; <see langword="false"/> otherwise.</returns> public bool IsOutOfSupport() { return SupportPhase == SupportPhase.EOL || EndOfLifeDate?.Date <= DateTime.Now.Date; } /// <summary> /// Gets a collection of all releases defined in the specified file. /// </summary> /// <param name="path">The path of the file containing the releases data.</param> /// <returns>A collection of releases. The releases are not linked to a specific <see cref="Product"/>.</returns> public static async Task<ReadOnlyCollection<ProductRelease>> GetReleasesAsync(string path) { using FileStream stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, bufferSize: 4096, useAsync: true); return await GetReleasesAsync(stream, null).ConfigureAwait(false); } private static async Task<ReadOnlyCollection<ProductRelease>> GetReleasesAsync(Stream stream, Product product) { if (stream == null) { throw new ArgumentNullException(nameof(stream)); } using var releasesDocument = await JsonDocument.ParseAsync(stream).ConfigureAwait(false); JsonElement root = releasesDocument.RootElement; var releases = new List<ProductRelease>(); var enumerator = root.GetProperty("releases").EnumerateArray(); while (enumerator.MoveNext()) { releases.Add(new ProductRelease(enumerator.Current, product)); } return new ReadOnlyCollection<ProductRelease>(releases); } } }