Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix SemVerHelper #848

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
121 changes: 85 additions & 36 deletions src/app/FakeLib/SemVerHelper.fs
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,69 @@ module Fake.SemVerHelper
open System
open System.Text.RegularExpressions

let (|ParseRegex|_|) pattern input =
let m = Regex.Match(input, pattern, RegexOptions.ExplicitCapture)

match m.Success with
| true ->
Some (List.tail [ for g in m.Groups -> g.Value ])
| false ->
None

let (|SemVer|_|) version =
let pattern =
@"^(?<major>\d+)" +
@"(\.(?<minor>\d+))?" +
@"(\.(?<patch>\d+))?" +
@"(\-(?<pre>[0-9A-Za-z\-\.]+))?" +
@"(\+(?<build>[0-9A-Za-z\-\.]+))?$"

match version with
| ParseRegex pattern [major; minor; patch; pre; build] ->
Some [major; minor; patch; pre; build]
| _ ->
None

let (|ValidVersion|_|) = function
| null | "" -> None
| ver when ver.Length > 1 && ver.StartsWith("0") -> None
| _ -> Some ValidVersion

let ComparePreRelease a b =
let (|Int|_|) str =
match System.Int32.TryParse(str) with
| (true, int) -> Some(int)
| _ -> None

let comp a b =
match (a, b) with
| (Int a, Int b) -> a.CompareTo(b)
| (Int a, _) -> -1
| (_, Int b) -> 1
| _ -> match String.CompareOrdinal(a, b) with
| i when not (i = 0) -> i
| _ -> 0

let aEmpty = String.IsNullOrEmpty(a)
let bEmpty = String.IsNullOrEmpty(b)

match (aEmpty, bEmpty) with
| (true, true) -> 0
| (true, false) -> 1
| (false, true) -> -1
| _ -> Seq.compareWith comp (a.Split '.') (b.Split '.')

[<CustomEquality; CustomComparison>]
type PreRelease =
{ Origin: string
Name: string
Number: int option }
Name: string }
static member TryParse str =
let m = Regex("^(?<name>[a-zA-Z]+)(?<number>\d*)$").Match(str)
match m.Success, m.Groups.["name"].Value, m.Groups.["number"].Value with
| true, name, "" -> Some { Origin = str; Name = name; Number = None }
| true, name, number -> Some { Origin = str; Name = name; Number = Some (int number) }
| _ -> None
match str with
| ParseRegex "^(?<name>[0-9A-Za-z\-\.]+)$" [name] ->
Some { Origin = str; Name = name }
| _ ->
None

override x.Equals(yobj) =
match yobj with
| :? PreRelease as y -> x.Origin = y.Origin
Expand All @@ -24,8 +76,7 @@ type PreRelease =
member x.CompareTo yobj =
match yobj with
| :? PreRelease as y ->
if x.Name <> y.Name then compare x.Name y.Name else
compare x.Number y.Number
ComparePreRelease x.Name y.Name
| _ -> invalidArg "yobj" "cannot compare values of different types"

/// Contains the version information.
Expand All @@ -42,12 +93,13 @@ type SemVerInfo =
/// The optional build no.
Build: string }
override x.ToString() =
sprintf "%d.%d.%d" x.Major x.Minor x.Patch +
(match x.PreRelease, isNotNullOrEmpty x.Build with
| Some preRelease, _ -> "-" + preRelease.Name
| None, true -> "-"
| _ -> "") +
(if isNotNullOrEmpty x.Build then "." + x.Build else "")
sprintf "%d.%d.%d%s%s" x.Major x.Minor x.Patch
(match x.PreRelease with
| Some preRelease -> sprintf "-%s"preRelease.Name
| _ -> "")
(match isNotNullOrEmpty x.Build with
| true -> sprintf "+%s" x.Build
| _ -> "")

override x.Equals(yobj) =
match yobj with
Expand Down Expand Up @@ -77,13 +129,13 @@ type SemVerInfo =
| _ -> invalidArg "yobj" "cannot compare values of different types"


let private SemVerPattern = "^(?:0|[1-9][0-9]*)\.(?:0|[1-9][0-9]*)\.(?:0|[1-9][0-9]*)(?:-[\da-zA-Z\-]+(?:\.[\da-zA-Z\-]+)*)?(?:\+[\da-zA-Z\-]+(?:\.[\da-zA-Z\-]+)*)?$"

/// Returns true if input appears to be a parsable semver string
let isValidSemVer input =
let m = Regex.Match(input, SemVerPattern)
if m.Success then true
else false
let isValidSemVer version =
match version with
| SemVer [ValidVersion major; ValidVersion minor; ValidVersion patch; pre; build] ->
true
| _ ->
false

/// Parses the given version string into a SemVerInfo which can be printed using ToString() or compared
/// according to the rules described in the [SemVer docs](http://semver.org/).
Expand All @@ -92,23 +144,20 @@ let isValidSemVer input =
/// parse "1.0.0-rc.1" < parse "1.0.0" // true
/// parse "1.2.3-alpha" > parse "1.2.2" // true
/// parse "1.2.3-alpha2" > parse "1.2.3-alpha" // true
/// parse "1.2.3-alpha002" > parse "1.2.3-alpha1" // true
/// parse "1.2.3-alpha002" > parse "1.2.3-alpha1" // false
/// parse "1.5.0-beta.2" > parse "1.5.0-rc.1" // false
let parse version =
let splitted = split '.' version
let l = splitted.Length
let patch,preRelease =
if l <= 2 then 0,"" else
let splitted' = split '-' splitted.[2]
Int32.Parse splitted'.[0], if splitted'.Length > 1 then splitted'.[1] else ""


{ Major = if l > 0 then Int32.Parse splitted.[0] else 0
Minor = if l > 1 then Int32.Parse splitted.[1] else 0
Patch = patch
PreRelease = PreRelease.TryParse preRelease
Build = if l > 3 then splitted.[3] else ""
}
match version with
| SemVer [major; minor; patch; pre; build] ->
{
Major = if isNullOrEmpty major then 1 else Int32.Parse major
Minor = if isNullOrEmpty minor then 0 else Int32.Parse minor
Patch = if isNullOrEmpty patch then 0 else Int32.Parse patch
PreRelease = PreRelease.TryParse pre
Build = build
}
| _ ->
failwithf "Unable to parse version %s" version



10 changes: 5 additions & 5 deletions src/test/Test.FAKECore/SemVerHelperSpecs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,13 +60,13 @@ public class when_validating_semver_strings
public class when_parsing_semver_strings
{
static SemVerHelper.SemVerInfo semVer;
Because of = () => semVer = SemVerHelper.parse("1.2.3-alpha.beta");
Because of = () => semVer = SemVerHelper.parse("1.2.3-alpha+beta");

It should_parse_major = () => semVer.Major.ShouldEqual(1);
It should_parse_minor = () => semVer.Minor.ShouldEqual(2);
It should_parse_patch = () => semVer.Patch.ShouldEqual(3);
It should_parse_prerelease = () => semVer.PreRelease.ShouldEqual(
FSharpOption<SemVerHelper.PreRelease>.Some(new SemVerHelper.PreRelease("alpha", "alpha", FSharpOption<int>.None)));
FSharpOption<SemVerHelper.PreRelease>.Some(new SemVerHelper.PreRelease("alpha", "alpha")));
It should_parse_build = () => semVer.Build.ShouldEqual("beta");
}

Expand Down Expand Up @@ -128,9 +128,9 @@ public class when_comparing_semvers
() => SemVerHelper.parse("2.3.4-alpha2")
.ShouldBeGreaterThan(SemVerHelper.parse("2.3.4-alpha"));

It should_assume_alpha003_is_greater_than_alpha2 =
() => SemVerHelper.parse("2.3.4-alpha003")
.ShouldBeGreaterThan(SemVerHelper.parse("2.3.4-alpha2"));
It should_assume_alpha2_is_greater_than_alpha003 =
() => SemVerHelper.parse("2.3.4-alpha2")
.ShouldBeGreaterThan(SemVerHelper.parse("2.3.4-alpha003"));

It should_assume_rc_is_greater_than_beta2 =
() => SemVerHelper.parse("2.3.4-rc")
Expand Down