Esempio n. 1
0
 /// <summary>
 /// Initializes a new filter from a string.
 /// Throws an <see cref="ArgumentException"/> on invalid syntax.
 /// Simply uses <see cref="TryParse(ReadOnlySpan{char}, out PackageQualityFilter)"/>) to handle invalid syntax.
 /// </summary>
 /// <param name="s">The string.</param>
 public PackageQualityFilter(ReadOnlySpan <char> s)
 {
     if (!TryParse(s, out PackageQualityFilter p))
     {
         throw new ArgumentException("Invalid PackageQualityFilter syntax.");
     }
     _min = p._min;
     _max = p._max;
 }
 static bool TryParse(string s, out PackageQuality q, PackageQuality def)
 {
     q = def;
     if (s.Length == 0)
     {
         return(true);
     }
     return(Enum.TryParse(s, out q));
 }
Esempio n. 3
0
 /// <summary>
 /// Initializes a new filter. Min must be lower or equal to max otherwise an <see cref="ArgumentException"/> is thrown.
 /// </summary>
 /// <param name="min">The minimal quality.</param>
 /// <param name="max">The maximal quality.</param>
 public PackageQualityFilter(PackageQuality min, PackageQuality max)
 {
     if (min > max)
     {
         throw new ArgumentException("min must be lower or equal to max.");
     }
     _min = min;
     _max = max;
 }
Esempio n. 4
0
 /// <summary>
 /// Gets the best version for a given quality or null if no such version exists.
 /// </summary>
 /// <param name="quality">The minimal required quality.</param>
 /// <returns>The best version or null if not found.</returns>
 public SVersion?GetVersion(PackageQuality quality)
 {
     return(quality switch
     {
         PackageQuality.Stable => Stable,
         PackageQuality.ReleaseCandidate => ReleaseCandidate,
         PackageQuality.Preview => Preview,
         PackageQuality.Exploratory => Exploratory,
         _ => CI,
     });
 /// <summary>
 /// Initializes a new filter (min and max are reordered if needed).
 /// </summary>
 /// <param name="min">The minimal quality.</param>
 /// <param name="max">The maximal quality.</param>
 public PackageQualityFilter(PackageQuality min, PackageQuality max)
 {
     if (min > max)
     {
         Min = max;
         Max = min;
     }
     else
     {
         Min = min;
         Max = max;
     }
 }
Esempio n. 6
0
 /// <summary>
 /// Initializes a new version range on a valid <see cref="Base"/> version.
 /// </summary>
 /// <param name="version">The base version that must be valid.</param>
 /// <param name="r">The lock to apply.</param>
 /// <param name="minQuality">The minimal quality to accept.</param>
 public SVersionBound(SVersion?version = null, SVersionLock r = SVersionLock.None, PackageQuality minQuality = PackageQuality.None)
 {
     _base = version ?? SVersion.ZeroVersion;
     if (!_base.IsValid)
     {
         throw new ArgumentException("Must be valid. Error: " + _base.ErrorMessage, nameof(version));
     }
     if (minQuality == PackageQuality.Stable && r == SVersionLock.LockPatch)
     {
         r = SVersionLock.Lock;
     }
     _minQuality = minQuality;
     Lock        = r;
 }
 /// <summary>
 /// Initializes a new filter by parsing a nullable string.
 /// Throws an <see cref="ArgumentException"/> on invalid syntax: use <see cref="TryParse(string, out PackageQualityFilter)"/>
 /// to handle invalid syntax.
 /// </summary>
 /// <param name="s">The string. Can be null or empty.</param>
 public PackageQualityFilter(string s)
 {
     if (!String.IsNullOrWhiteSpace(s))
     {
         if (!TryParse(s, out PackageQualityFilter p))
         {
             throw new ArgumentException("Invalid PackageQualityFilter syntax.");
         }
         Min = p.Min;
         Max = p.Max;
     }
     else
     {
         Min = Max = PackageQuality.None;
     }
 }
        static (ParseResult Result, bool IsFloatingMinor) TryMatchRangeAlone(ref ReadOnlySpan <char> s, SVersionLock defaultBound, bool includePrerelease)
        {
            var r = TryMatchFloatingVersion(ref s);

            if (r.Error != null)
            {
                return(new ParseResult(r.Error), false);
            }

            PackageQuality quality = includePrerelease ? PackageQuality.None : PackageQuality.Stable;

            if (r.Version != null)
            {
                // As soon as a prerelease appears, this can only be an approximation (with one exception - see below) since for npm:
                //
                // "For example, the range >1.2.3-alpha.3 would be allowed to match the version 1.2.3-alpha.7, but it
                //  would not be satisfied by 3.4.5-alpha.9, even though 3.4.5-alpha.9 is technically "greater than"
                //  1.2.3-alpha.3 according to the SemVer sort rules. The version range only accepts prerelease tags
                //  on the 1.2.3 version. The version 3.4.5 would satisfy the range, because it does not have a prerelease
                //  flag, and 3.4.5 is greater than 1.2.3-alpha.7."
                //
                // We also set the MinQuality to CI (otherwise alpha.7 will not be authorized for alpha.3) regardless of the includePrerelease.
                // Moreover, if we are coming from the ~ (tilde range), the lock is on the patch, not on the minor, and this exactly matches the
                // npm behavior.
                //
                bool isApproximated = !includePrerelease;
                if (r.Version.IsPrerelease)
                {
                    quality = PackageQuality.None;
                    if (defaultBound == SVersionLock.LockMinor)
                    {
                        defaultBound   = SVersionLock.LockPatch;
                        isApproximated = false;
                    }
                }
                return(new ParseResult(new SVersionBound(r.Version, defaultBound, quality), isApproximated, r.FourtPartLost), false);
            }
            if (r.FMajor < 0)
            {
                return(new ParseResult(new SVersionBound(_000Version, SVersionLock.None, quality), isApproximated: !includePrerelease, false), false);
            }
            if (r.FMinor < 0)
            {
                return(new ParseResult(new SVersionBound(SVersion.Create(r.FMajor, 0, 0), SVersionLock.LockMajor, quality), isApproximated: !includePrerelease, false), true);
            }
            return(new ParseResult(new SVersionBound(SVersion.Create(r.FMajor, r.FMinor, 0), SVersionLock.LockMinor, quality), isApproximated: !includePrerelease, false), false);
        }
Esempio n. 9
0
        /// <summary>
        /// Attempts to parse a string as a <see cref="PackageQualityFilter"/>.
        /// Note that the parse is case insensitive, that white spaces are silently ignored and min/max can be reversed.
        /// The <paramref name="head"/> is forwarded right after the match: the head may be on any kind of character.
        /// <para>
        /// Examples:
        /// "Stable" (is the same as "Stable-Stable"): only <see cref="PackageQuality.Stable"/> is accepted
        /// "CI-Stable" (is the same as "-Stable" or "CI-" or ""): everything is accepted.
        /// "-ReleaseCandidate" (same as "CI-RC" or "CI-ReleaseCandidate"): everything except Stable.
        /// "Exploratory-Preview": No CI, ReleaseCandidate, nor Stable.
        /// </para>
        /// </summary>
        /// <param name="head">The string to parse (leading and internal white spaces between tokens are skipped).</param>
        /// <param name="filter">The result.</param>
        /// <returns>True on success, false on error.</returns>
        public static bool TryParse(ref ReadOnlySpan <char> head, out PackageQualityFilter filter)
        {
            var start = head;

            head = head.TrimStart();
            bool           hasMin = PackageQualityExtension.TryMatch(ref head, out var min);
            bool           hasMax = false;
            PackageQuality max    = PackageQuality.Stable;
            var            sHead  = head;

            if ((head = head.TrimStart()).Length > 0 && head[0] == '-')
            {
                if (!hasMin)
                {
                    sHead = head;
                }
                head   = head.Slice(1).TrimStart();
                hasMax = PackageQualityExtension.TryMatch(ref head, out max);
                if (!hasMax)
                {
                    head = sHead;
                }
            }
            else
            {
                head = sHead;
            }
            if (hasMin || hasMax)
            {
                if (max == PackageQuality.None)
                {
                    max = PackageQuality.Stable;
                }
                if (min > max)
                {
                    var t = max;
                    max = min;
                    min = t;
                }
                filter = new PackageQualityFilter(min, max == PackageQuality.None ? PackageQuality.Stable : max);
                return(true);
            }
            filter = new PackageQualityFilter();
            head   = start;
            return(false);
        }
        /// <summary>
        /// Gets the best version for a given quality or null if no such version exists.
        /// </summary>
        /// <param name="quality">The minimal required quality.</param>
        /// <returns>The best version or null if not found.</returns>
        public SVersion GetVersion(PackageQuality quality)
        {
            if (!IsValid)
            {
                throw new InvalidOperationException();
            }
            switch (quality)
            {
            case PackageQuality.Release: return(Stable);

            case PackageQuality.ReleaseCandidate: return(Latest);

            case PackageQuality.Preview: return(Preview);

            case PackageQuality.Exploratory: return(Exploratory);

            default: return(CI);
            }
        }
Esempio n. 11
0
        /// <summary>
        /// Tries to parse "<see cref="SVersionLock"/>[,<see cref="PackageQuality"/>]" or "<see cref="PackageQuality"/>[,<see cref="SVersionLock"/>]".
        /// Note that match is case insensitive, that white spaces are silently ignored, that all "Lock" wan be written as "Locked" and that "rc" is
        /// a synonym of <see cref="PackageQuality.ReleaseCandidate"/>.
        /// The <paramref name="head"/> is forwarded right after the match: the head may be on any kind of character.
        /// </summary>
        /// <param name="head">The string to parse (leading and internal white spaces are skipped).</param>
        /// <param name="l">The read lock.</param>
        /// <param name="q">The read quality.</param>
        /// <returns>True on success, false otherwise.</returns>
        public static bool TryParseLockAndMinQuality(ref ReadOnlySpan <char> head,
                                                     out SVersionLock l,
                                                     out PackageQuality q)
        {
            var sSaved = head;

            l = SVersionLock.None;
            q = PackageQuality.None;
            if (head.Length == 0)
            {
                return(false);
            }
            if (SVersionLockExtension.TryMatch(ref Trim(ref head), out l))
            {
                if (TryMatch(ref Trim(ref head), ','))
                {
                    // This handles None,None: if the first read is "None", then it could have been the "None" of the quality:
                    // if we don't match a Quality we give a second chance to the Lock.
                    if (!PackageQualityExtension.TryMatch(ref Trim(ref head), out q) && l == SVersionLock.None)
                    {
                        SVersionLockExtension.TryMatch(ref Trim(ref head), out l);
                    }
                    return(true);
                }
                return(true);
            }
            if (PackageQualityExtension.TryMatch(ref Trim(ref head), out q))
            {
                if (TryMatch(ref Trim(ref head), ','))
                {
                    SVersionLockExtension.TryMatch(ref Trim(ref head), out l);
                    return(true);
                }
                return(true);
            }
            head = sSaved;
            return(false);
        }
Esempio n. 12
0
        /// <summary>
        /// Tries to parse a version bound: it is a <see cref="SVersion.TryParse(ref ReadOnlySpan{char}, bool, bool, bool)"/> that may be
        /// followed by an optional bracketed "[<see cref="TryParseLockAndMinQuality"/>]".
        /// The head is forwarded right after the match: on success, the head may be on any kind of character.
        /// </summary>
        /// <param name="head">The string to parse (leading and internal white spaces between tokens are skipped).</param>
        /// <param name="bound">The result. This is <see cref="SVersionBound.None"/> on error.</param>
        /// <param name="defaultLock">Default lock to apply if none is provided.</param>
        /// <param name="defaultQuality">Default quality to apply if none is provided.</param>
        /// <returns>True on success, false otherwise.</returns>
        public static bool TryParse(ref ReadOnlySpan <char> head, out SVersionBound bound, SVersionLock defaultLock = SVersionLock.None, PackageQuality defaultQuality = PackageQuality.None)
        {
            var sHead = head;

            bound = SVersionBound.None;
            var v = SVersion.TryParse(ref Trim(ref head), checkBuildMetaDataSyntax: false);

            if (!v.IsValid)
            {
                head = sHead;
                return(false);
            }
            SVersionLock   l = SVersionLock.None;
            PackageQuality q = PackageQuality.None;

            if (Trim(ref head).Length > 0 && TryMatch(ref head, '['))
            {
                // Allows empty []. Note that TryParseLockAndMinQuality calls Trim.
                TryParseLockAndMinQuality(ref head, out l, out q);
                // Match the closing ] if it's here. Ignores it if it's not here.
                if (Trim(ref head).Length > 0)
                {
                    TryMatch(ref head, ']');
                }
            }
            if (l == SVersionLock.None)
            {
                l = defaultLock;
            }
            if (q == PackageQuality.None)
            {
                q = defaultQuality;
            }
            bound = new SVersionBound(v, l, q);
            return(true);
        }
Esempio n. 13
0
 /// <summary>
 /// Gets the standard package labels that corresponds to this <see cref="PackageQuality"/>.
 /// </summary>
 /// <param name="this">This PackageQuality.</param>
 /// <returns>The corresponding labels.</returns>
 public static IReadOnlyList <PackageLabel> GetLabels(this PackageQuality @this)
 {
     return(_map[(int)@this]);
 }
Esempio n. 14
0
 public void version_to_quality_mapping(string version, PackageQuality q)
 {
     SVersion.TryParse(version).PackageQuality.Should().Be(q);
 }
Esempio n. 15
0
 /// <summary>
 /// Gets this quality followed by all its lowest qualities.
 /// </summary>
 /// <param name="this">This quality.</param>
 /// <returns>This quality followed by its lowest ones.</returns>
 public static IReadOnlyList <PackageQuality> GetAllQualities(this PackageQuality @this)
 => _map[@this switch
Esempio n. 16
0
 /// <summary>
 /// Intersects this quality with another one: the strongest wins, merging <see cref="PackageQuality.CI"/> and <see cref="PackageQuality.Stable"/>
 /// results in <see cref="PackageQuality.Stable"/>.
 /// </summary>
 /// <param name="this">This quality.</param>
 /// <param name="other">The other quality.</param>
 /// <returns>The strongest of the two.</returns>
 public static PackageQuality Intersect(this PackageQuality @this, PackageQuality other)
 {
     return(@this > other ? @this : other);
 }
Esempio n. 17
0
 /// <summary>
 /// Merges this quality with another one: the weakest wins, merging <see cref="PackageQuality.CI"/> and <see cref="PackageQuality.Stable"/>
 /// results in <see cref="PackageQuality.CI"/>.
 /// </summary>
 /// <param name="this">This quality.</param>
 /// <param name="other">The other quality.</param>
 /// <returns>The weakest of the two.</returns>
 public static PackageQuality Union(this PackageQuality @this, PackageQuality other)
 {
     return(@this < other ? @this : other);
 }
Esempio n. 18
0
 public void parse_SVersionBound_with_defaults(string p, string expected, SVersionLock defL, PackageQuality defQ)
 {
     SVersionBound.TryParse(p, out var b, defL, defQ).Should().BeTrue();
     b.ToString().Should().Be(expected);
 }
        public void Quality_to_Labels_mappings(PackageQuality q, string labels)
        {
            var l = labels.Split(',').Select(s => (PackageLabel)Enum.Parse(typeof(PackageLabel), s));

            q.GetLabels().Should().BeEquivalentTo(l, o => o.WithStrictOrdering());
        }
Esempio n. 20
0
 /// <summary>
 /// Tries to parse a version bound: it is a <see cref="SVersion.TryParse(ref ReadOnlySpan{char}, bool, bool, bool)"/> that may be
 /// followed by an optional bracketed "[<see cref="TryParseLockAndMinQuality"/>]".
 /// </summary>
 /// <param name="head">The string to parse (leading and internal white spaces between tokens are skipped).</param>
 /// <param name="bound">The result. This is <see cref="SVersionBound.None"/> on error.</param>
 /// <param name="defaultLock">The <see cref="SVersionLock"/> to use when no lock appears in the string to parse.</param>
 /// <param name="defaultQuality">The <see cref="PackageQuality"/> to use when no lock appears in the string to parse.</param>
 /// <returns>True on success, false otherwise.</returns>
 public static bool TryParse(ReadOnlySpan <char> head, out SVersionBound bound, SVersionLock defaultLock = SVersionLock.None, PackageQuality defaultQuality = PackageQuality.None) => TryParse(ref head, out bound, defaultLock, defaultQuality);
Esempio n. 21
0
 /// <summary>
 /// Sets a lock by returning this or a new <see cref="SVersionBound"/>.
 /// </summary>
 /// <param name="min">The lock to set.</param>
 /// <returns>This or a new range.</returns>
 public SVersionBound SetMinQuality(PackageQuality min) => MinQuality != min ? new SVersionBound(Base, Lock, min) : this;
 /// <summary>
 /// Gets whether this filter allows the specified quality.
 /// </summary>
 /// <param name="q">The quality to challenge. <see cref="PackageQuality.None"/> is never accepted.</param>
 /// <returns>Whether <paramref name="q"/> is accepted or not.</returns>
 public bool Accepts(PackageQuality q) => q != PackageQuality.None &&
 (!HasMin || q >= Min) &&
 (!HasMax || q <= Max);
Esempio n. 23
0
        /// <summary>
        /// This method must choose between possible versions. It may return null to cancel the process.
        /// </summary>
        /// <param name="m">The monitor to use.</param>
        /// <param name="c">The context.</param>
        public void ChooseFinalVersion(IActivityMonitor m, IReleaseVersionSelectorContext c)
        {
            Console.WriteLine("=========");
            Console.Write($"======== {c.Solution.Solution.Name} ");
            if (c.PreviousVersionCommitSha != null)
            {
                Console.Write($" last release: {c.PreviousVersion}");
                var diffResult = c.GetProjectsDiff(m);
                if (diffResult == null)
                {
                    c.Cancel();
                    return;
                }

                if (diffResult.Diffs.All(d => d.DiffType == DiffRootResultType.None) && diffResult.Others.DiffType == DiffRootResultType.None)
                {
                    Console.WriteLine($" (No change in {c.Solution.Solution.GeneratedArtifacts.Select( p => p.Artifact.Name ).Concatenate()})");
                }
                else
                {
                    Console.WriteLine(", changes:");
                }

                Console.WriteLine(diffResult.ToString());
            }
            else
            {
                Console.WriteLine("(No previous released version)");
            }

            var projExRefNotRelease = c.Solution.Solution.Projects
                                      .Where(p => p.IsPublished && p.PackageReferences.Any(q => q.Kind == ArtifactDependencyKind.Transitive))
                                      .Select(p =>
            {
                List <PackageReference> pcks = p.PackageReferences
                                               .Where(q => q.Kind == ArtifactDependencyKind.Transitive)
                                               .Where(
                    q => !c.Solution.ImportedLocalPackages
                    .Any(s => s.Package.Artifact == q.Target.Artifact)
                    )
                                               .ToList();
                if (pcks.Any())
                {
                    PackageQuality worstQuality = pcks.Select(q => q.Target.Version.PackageQuality).Min();
                    return(p, pcks.Where(q => q.Target.Version.PackageQuality == worstQuality));
                }
                p = null;
                return(p, Array.Empty <PackageReference>());
            })                                                                                               //There should be at least one package reference
                                      .Where(x => x.p != null)
                                      .GroupBy(p => p.Item2.First().Target.Version.PackageQuality).ToList(); //ugliest LINQ i ever wrote, should take 3 lines.
            var min   = projExRefNotRelease.Any() ? projExRefNotRelease.Min(q => q.Key) : PackageQuality.None;
            var worst = min != PackageQuality.None
                            ? projExRefNotRelease.SingleOrDefault(p => p.Key == min)
                            : null;

            if (worst == null || worst.Key == PackageQuality.Release)
            {
                Console.WriteLine("Nothing prevent to choose the Release quality.");
            }
            else
            {
                var prev = Console.ForegroundColor;
                Console.ForegroundColor = ConsoleColor.Yellow;
                Console.WriteLine($"Best quality is {worst.Key} because of projects:");
                foreach (var proj in worst)
                {
                    Console.WriteLine("    => " + proj.p.Name + " caused by packages references " +
                                      string.Join(", ", proj.Item2.Select(p => p.ToString()).ToArray()));
                }
                Console.ForegroundColor = prev;
            }

            foreach (var kv in c.PossibleVersions)
            {
                Console.Write($"= {(int)kv.Key} - {kv.Key} => ");
                for (int i = 0; i < kv.Value.Count; i++)
                {
                    CSVersion version = kv.Value[i];
                    var       prev    = Console.ForegroundColor;
                    Console.ForegroundColor = kv.Key != ReleaseLevel.None
                        ? version.PackageQuality > (worst?.Key ?? PackageQuality.Release) ? ConsoleColor.Red : ConsoleColor.Green
                        : ConsoleColor.White;
                    Console.Write(version.NormalizedText);
                    if (i < kv.Value.Count - 1)
                    {
                        Console.Write(", ");
                    }
                    Console.ForegroundColor = prev;
                }
                Console.WriteLine();
            }
            if (c.CanUsePreviouslyResolvedInfo)
            {
                Console.WriteLine($"= A - Already selected Level and Version: {c.PreviouslyResolvedInfo.Level} - {c.PreviouslyResolvedInfo.Version}");
            }
            Console.WriteLine("= X - Cancel.");
            ReleaseLevel level = ReleaseLevel.None;
            char         a;

            do
            {
                while ("0123AX".IndexOf((a = Console.ReadKey().KeyChar)) < 0)
                {
                    Console.Write('\b');
                }
                if (a == 'X')
                {
                    c.Cancel();
                }
                if (a == 'A')
                {
                    c.SetChoice(c.PreviouslyResolvedInfo.Level, c.PreviouslyResolvedInfo.Version);
                }
                else
                {
                    level = (ReleaseLevel)(a - '0');
                }
            }while(!c.IsAnswered && c.PossibleVersions[level].Count == 0);
            if (!c.IsAnswered)
            {
                Console.WriteLine($"= Selected: {level}, now choose a version:");
                var possibleVersions = c.PossibleVersions[level];
                for (int i = 0; i < possibleVersions.Count; ++i)
                {
                    Console.WriteLine($"= {i} - {possibleVersions[i]}");
                }
                Console.WriteLine($"= X - Cancel.");
                while (!c.IsAnswered)
                {
                    Console.Write($"= (Enter the final release number and press enter)> ");
                    string line = Console.ReadLine();
                    if (line == "X")
                    {
                        c.Cancel();
                    }
                    else if (Int32.TryParse(line, out var num) && num >= 0 && num < possibleVersions.Count)
                    {
                        c.SetChoice(level, possibleVersions[num]);
                    }
                    Console.WriteLine();
                }
            }
        }