Ejemplo n.º 1
0
        private static void Run(CommandLineApplication cmd, ILogger log)
        {
            cmd.Description = "Convert a set of pre-release packages to stable or the specified version/release label. Package dependencies will also be modified to match. Defaults to stable.";

            var idFilter             = cmd.Option(Constants.IdFilterTemplate, Constants.IdFilterDesc, CommandOptionType.SingleValue);
            var versionFilter        = cmd.Option(Constants.VersionFilterTemplate, Constants.VersionFilterDesc, CommandOptionType.SingleValue);
            var excludeSymbolsFilter = cmd.Option(Constants.ExcludeSymbolsTemplate, Constants.ExcludeSymbolsDesc, CommandOptionType.NoValue);
            var highestVersionFilter = cmd.Option(Constants.HighestVersionFilterTemplate, Constants.HighestVersionFilterDesc, CommandOptionType.NoValue);

            var newVersion = cmd.Option("-n|--new-version", "New version, this replaces the entire version of the nupkg. Cannot be used with --label.", CommandOptionType.SingleValue);
            var label      = cmd.Option("-r|--label", "Pre-release label, this keeps the version number the same and only modifies the release label. Cannot be used with --new-version.", CommandOptionType.SingleValue);
            var stable     = cmd.Option("-s|--stable", "Remove pre-release label, this is the default option.", CommandOptionType.NoValue);

            var argRoot = cmd.Argument(
                "[root]",
                Constants.MultiplePackagesRootDesc,
                multipleValues: true);

            cmd.HelpOption(Constants.HelpOption);

            cmd.OnExecute(() =>
            {
                try
                {
                    // Validate parameters
                    int optionCount = 0;

                    if (stable.HasValue())
                    {
                        optionCount++;
                    }

                    if (label.HasValue())
                    {
                        optionCount++;
                    }

                    if (newVersion.HasValue())
                    {
                        optionCount++;
                    }

                    if (optionCount > 1)
                    {
                        throw new ArgumentException($"Invalid option combination. Specify only one of the following options: {stable.LongName}, {newVersion.LongName}, {label.LongName}.");
                    }

                    var inputs = argRoot.Values;

                    if (inputs.Count < 1)
                    {
                        inputs.Add(Directory.GetCurrentDirectory());
                    }

                    // Gather all package data
                    var packages = Util.GetPackagesWithFilter(idFilter, versionFilter, excludeSymbolsFilter, highestVersionFilter, inputs.ToArray());

                    var packageSet      = new List <Tuple <string, PackageIdentity, string, XDocument, PackageIdentity> >();
                    var updatedIds      = new HashSet <string>();
                    var fileNameUpdates = new Dictionary <string, string>(StringComparer.Ordinal);

                    foreach (var package in packages)
                    {
                        log.LogMinimal($"reading {package}");

                        // Get nuspec file path
                        using (var packageReader = new PackageArchiveReader(package))
                        {
                            var nuspecPath = packageReader.GetNuspecFile();
                            var nuspecXml  = XDocument.Load(packageReader.GetNuspec());
                            var identity   = packageReader.GetIdentity();

                            var updatedVersion = identity.Version;

                            if (newVersion.HasValue())
                            {
                                updatedVersion = NuGetVersion.Parse(newVersion.Value());
                            }
                            else if (label.HasValue())
                            {
                                var versionPart = identity.Version.ToString().Split('-')[0];

                                updatedVersion = NuGetVersion.Parse($"{versionPart}-{label.Value()}");
                            }
                            else
                            {
                                var versionPart = identity.Version.ToString().Split('-')[0];

                                updatedVersion = NuGetVersion.Parse($"{versionPart}");
                            }

                            var newIdentity = new PackageIdentity(identity.Id, updatedVersion);

                            packageSet.Add(new Tuple <string, PackageIdentity, string, XDocument, PackageIdentity>(package, identity, nuspecPath, nuspecXml, newIdentity));

                            updatedIds.Add(identity.Id);

                            // Determine the new file name
                            var newFileName = $"{newIdentity.Id}.{newIdentity.Version.ToString()}.nupkg";

                            if (package.EndsWith(".symbols.nupkg"))
                            {
                                newFileName = $"{newIdentity.Id}.{newIdentity.Version.ToString()}.symbols.nupkg";
                            }

                            var rootDir     = Path.GetDirectoryName(package);
                            var newFullPath = Path.Combine(rootDir, newFileName);

                            // Old path -> New path
                            fileNameUpdates.Add(package, newFullPath);
                        }
                    }

                    // Verify there are no collisions
                    VerifyNoConflicts(fileNameUpdates);

                    // Update dependency info
                    foreach (var package in packageSet)
                    {
                        log.LogMinimal($"processing {package.Item1}");

                        var nuspec = package.Item4;

                        // Update the version
                        Util.AddOrUpdateMetadataElement(nuspec, "version", package.Item5.Version.ToString());

                        if (package.Item2.Version != package.Item5.Version)
                        {
                            log.LogInformation($"{package.Item2.Version} -> {package.Item5.Version}");
                        }

                        var metadata = Util.GetMetadataElement(nuspec);

                        // Find all <dependency> elements
                        foreach (var dependency in metadata.DescendantNodes().Select(e => e as XElement).Where(e => e != null))
                        {
                            var depId = dependency.Attributes().FirstOrDefault(e => e.Name.LocalName.Equals("id", StringComparison.OrdinalIgnoreCase))?.Value;

                            // Check if this is a package that has been updated
                            if (updatedIds.Contains(depId))
                            {
                                var rangeAttribute = dependency.Attributes().FirstOrDefault(e => e.Name.LocalName.Equals("version", StringComparison.OrdinalIgnoreCase));

                                // Modify the range if needed
                                if (rangeAttribute != null && !string.IsNullOrWhiteSpace(rangeAttribute.Value))
                                {
                                    var range = VersionRange.Parse(rangeAttribute.Value);

                                    // Verify the original range is valid.
                                    if (range.HasLowerAndUpperBounds &&
                                        VersionComparer.VersionRelease.Compare(range.MinVersion, range.MaxVersion) > 0)
                                    {
                                        log.LogWarning($"dependency range is invalid: {depId} {rangeAttribute.Value}. Skipping.");
                                        continue;
                                    }

                                    // Look up the package this refers to
                                    // If there are multiple packages of the same id apply the update
                                    // for the lowest version, favoring one with an original version
                                    // that matched the original range.
                                    // Ordering by the original version is used just as a tie breaker
                                    var depPackageEntry = packageSet
                                                          .Where(e => e.Item2.Id.Equals(depId, StringComparison.OrdinalIgnoreCase))
                                                          .OrderBy(e => range.Satisfies(e.Item2.Version) ? 0 : 1)
                                                          .ThenBy(e => e.Item5.Version)
                                                          .ThenBy(e => e.Item2.Version)
                                                          .First();

                                    // Get version replacement
                                    var oldVersion     = depPackageEntry.Item2.Version;
                                    var updatedVersion = depPackageEntry.Item5.Version;

                                    // Verify the original package version was part of the original range.
                                    if (!range.Satisfies(oldVersion))
                                    {
                                        log.LogWarning($"dependency {depId} does not allow the original version of {depId} {oldVersion.ToNormalizedString()}. Skipping.");
                                        continue;
                                    }

                                    var minVersion = range.MinVersion;
                                    var maxVersion = range.MaxVersion;
                                    var includeMin = range.IsMinInclusive;
                                    var includeMax = range.IsMaxInclusive;
                                    var changed    = false;

                                    // Always update the min version if the range includes one
                                    if (minVersion != null &&
                                        (!VersionComparer.VersionRelease.Equals(updatedVersion, minVersion) ||
                                         !includeMin))
                                    {
                                        minVersion = updatedVersion;
                                        includeMin = true;
                                        changed    = true;
                                    }

                                    // Update the max if the original max matches the original version,
                                    // or if the new version is above the old max.
                                    if (maxVersion != null &&
                                        (VersionComparer.VersionRelease.Compare(oldVersion, maxVersion) == 0 ||
                                         VersionComparer.VersionRelease.Compare(updatedVersion, maxVersion) >= 0))
                                    {
                                        maxVersion = updatedVersion;
                                        includeMax = true;
                                        changed    = true;
                                    }

                                    if (changed)
                                    {
                                        // Create new range
                                        var updatedRange = new VersionRange(
                                            minVersion: minVersion,
                                            includeMinVersion: includeMin,
                                            maxVersion: maxVersion,
                                            includeMaxVersion: includeMax);

                                        // Verify the new version is allowed by the new range.
                                        if (!updatedRange.Satisfies(updatedVersion))
                                        {
                                            if (range.HasUpperBound)
                                            {
                                                // Lock to only the new version
                                                minVersion = updatedVersion;
                                                maxVersion = updatedVersion;
                                                includeMin = true;
                                                includeMax = true;

                                                log.LogWarning($"Locking dependency range for {depId} to = {minVersion.ToNormalizedString()}.");
                                            }
                                            else
                                            {
                                                // If there was no max, update to >= updatedVersion
                                                minVersion = updatedVersion;
                                                maxVersion = null;
                                                includeMin = true;
                                                includeMax = false;

                                                log.LogWarning($"Resetting dependency range for {depId} to >= {minVersion.ToNormalizedString()}.");
                                            }
                                        }

                                        rangeAttribute.SetValue(updatedRange.ToLegacyShortString());

                                        log.LogInformation($"dependency {depId} {range} -> {updatedRange}");
                                    }
                                }
                            }
                        }

                        // Update the nuspec file in the zip
                        Util.AddOrReplaceZipEntry(package.Item1, package.Item3, nuspec, log);

                        // Move the file
                        var newPath = fileNameUpdates[package.Item1];
                        if (!newPath.Equals(package.Item1, StringComparison.Ordinal))
                        {
                            log.LogMinimal($"{package.Item1} -> {newPath}");
                            File.Move(package.Item1, newPath);
                        }
                    }

                    return(0);
                }
                catch (Exception ex)
                {
                    log.LogError(ex.Message);
                    log.LogDebug(ex.ToString());
                }

                return(1);
            });
        }