File: GetLastStablePackage.cs
Web Access
Project: src\src\Microsoft.DotNet.Build.Tasks.Packaging\src\Microsoft.DotNet.Build.Tasks.Packaging.csproj (Microsoft.DotNet.Build.Tasks.Packaging)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
using NuGet.Versioning;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
 
namespace Microsoft.DotNet.Build.Tasks.Packaging
{
    public class GetLastStablePackage : BuildTask
    {
        /// <summary>
        /// List of packages to look up.
        /// The closest StablePackage version that is less than the version of each of these packages will be returned in LastStablePackages.
        ///   Identity: Package ID
        ///   Version: Package version.
        /// </summary>
        [Required]
        public ITaskItem[] LatestPackages { get; set; }
 
        /// <summary>
        /// List of previously shipped packages.
        ///   Identity: Package ID
        ///   Version: Package version.
        /// </summary>
        public ITaskItem[] StablePackages { get; set; }
 
        /// <summary>
        /// Package index files used to define stable packages.
        /// </summary>
        public ITaskItem[] PackageIndexes { get; set; }
 
        /// <summary>
        /// <see langword="true"/> if the result version can be a version from the same release.
        /// <see langword="false"/> otherwise. Defaults to false.
        /// </summary>
        public bool DoNotAllowVersionsFromSameRelease { get; set; }
        
 
        /// <summary>
        /// Latest version from StablePackages for all packages in LatestPackages.
        /// If a version isn't found for an item in LatestPackage that will not be included in this set.
        /// </summary>
        [Output]
        public ITaskItem[] LastStablePackages { get; set; }
 
        public override bool Execute()
        {
            if (LatestPackages == null || LatestPackages.Length == 0)
            {
                return true;
            }
 
            if (PackageIndexes != null && PackageIndexes.Length > 0)
            {
                GetLastStablePackagesFromIndex();
            }
            else
            {
                GetLastStablePackagesFromStablePackages();
            }
 
            return !Log.HasLoggedErrors;
        }
 
        public void GetLastStablePackagesFromStablePackages()
        {
            Dictionary<string, ITaskItem> originalItems = new Dictionary<string, ITaskItem>();
            Dictionary<string, Version> latestPackages = new Dictionary<string, Version>();
            Dictionary<string, Version> lastStablePackages = new Dictionary<string, Version>();
 
            foreach (var latestPackage in LatestPackages)
            {
                var packageId = latestPackage.ItemSpec;
 
                var versionString = latestPackage.GetMetadata("Version");
                NuGetVersion nuGetVersion = null;
                if (versionString == null || !NuGetVersion.TryParse(versionString, out nuGetVersion))
                {
                    Log.LogMessage($"Could not parse version {versionString} for LatestPackage {packageId}, will use latest stable.");
                }
 
                latestPackages[packageId] = nuGetVersion?.Version;
                originalItems[packageId] = latestPackage;
            }
 
            foreach (var stablePackage in StablePackages.NullAsEmpty())
            {
                var packageId = stablePackage.ItemSpec;
 
                Version latestVersion;
                if (!latestPackages.TryGetValue(packageId, out latestVersion))
                {
                    continue;
                }
 
                var versionString = stablePackage.GetMetadata("Version");
                Version stableVersion;
                if (versionString == null || !Version.TryParse(versionString, out stableVersion))
                {
                    Log.LogError($"Could not parse version {versionString} for StablePackage {packageId}");
                    continue;
                }
                stableVersion = VersionUtility.As4PartVersion(stableVersion);
 
                // only consider a stable version less or equal to than current version
                if (latestVersion != null && stableVersion >= latestVersion)
                {
                    continue;
                }
 
                Version lastStableVersion;
                if (!lastStablePackages.TryGetValue(packageId, out lastStableVersion) || lastStableVersion < stableVersion)
                {
                    lastStablePackages[packageId] = stableVersion;
                }
            }
 
            LastStablePackages = lastStablePackages.Select(p => CreateItem(originalItems[p.Key], p.Value)).ToArray();
        }
 
        public void GetLastStablePackagesFromIndex()
        {
            var index = PackageIndex.Load(PackageIndexes.Select(pi => pi.GetMetadata("FullPath")));
 
            List<ITaskItem> lastStablePackages = new List<ITaskItem>();
 
            foreach (var latestPackage in LatestPackages)
            {
                var packageId = latestPackage.ItemSpec;
 
                var versionString = latestPackage.GetMetadata("Version");
                NuGetVersion nuGetVersion = null;
                if (versionString == null || !NuGetVersion.TryParse(versionString, out nuGetVersion))
                {
                    Log.LogMessage($"Could not parse version {versionString} for LatestPackage {packageId}, will use latest stable.");
                }
 
                var latestVersion = (DoNotAllowVersionsFromSameRelease) ? VersionUtility.As2PartVersion(nuGetVersion?.Version) : nuGetVersion?.Version;
 
                PackageInfo info;
                if (index.Packages.TryGetValue(packageId, out info))
                {
                    IEnumerable<Version> candidateVersions = (latestVersion == null) ? info.StableVersions : info.StableVersions.Where(sv => VersionUtility.As4PartVersion(sv) < latestVersion);
                    if (candidateVersions.Any())
                    {
                        lastStablePackages.Add(CreateItem(latestPackage, candidateVersions.Max()));
                    }
                }
            }
 
            LastStablePackages = lastStablePackages.ToArray();
 
        }
 
        private ITaskItem CreateItem(ITaskItem originalItem, Version version)
        {
            var item = new TaskItem(originalItem);
            item.SetMetadata("Version", version.ToString(3));
            return item;
        }
    }
}