diff --git a/eng/common/scripts/SemVer.ps1 b/eng/common/scripts/SemVer.ps1 index ab4eef38052b..29101690d745 100644 --- a/eng/common/scripts/SemVer.ps1 +++ b/eng/common/scripts/SemVer.ps1 @@ -7,6 +7,9 @@ See https://azure.github.io/azure-sdk/policies_releases.html#package-versioning Example: 1.2.3-beta.4 Components: Major.Minor.Patch-PrereleaseLabel.PrereleaseNumber +Example: 1.2.3-alpha.20200828.4 +Components: Major.Minor.Patch-PrereleaseLabel.PrereleaseNumber.BuildNumber + Note: A builtin Powershell version of SemVer exists in 'System.Management.Automation'. At this time, it does not parsing of PrereleaseNumber. It's name is also type accelerated to 'SemVer'. #> @@ -17,14 +20,19 @@ class AzureEngSemanticVersion { [string] $PrereleaseLabelSeparator [string] $PrereleaseLabel [string] $PrereleaseNumberSeparator + [string] $BuildNumberSeparator + # BuildNumber is string to preserve zero-padding where applicable + [string] $BuildNumber [int] $PrereleaseNumber [bool] $IsPrerelease [string] $RawVersion [bool] $IsSemVerFormat [string] $DefaultPrereleaseLabel + [string] $DefaultAlphaReleaseLabel # Regex inspired but simplified from https://semver.org/#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string - static [string] $SEMVER_REGEX = "(?0|[1-9]\d*)\.(?0|[1-9]\d*)\.(?0|[1-9]\d*)(?:(?-?)(?[a-zA-Z-]*)(?\.?)(?0|[1-9]\d*))?" + # Validation: https://regex101.com/r/vkijKf/426 + static [string] $SEMVER_REGEX = "(?0|[1-9]\d*)\.(?0|[1-9]\d*)\.(?0|[1-9]\d*)(?:(?-?)(?[a-zA-Z]+)(?:(?\.?)(?[0-9]{1,8})(?:(?\.?)(?\d{1,3}))?)?)?" static [string] $ParseLanguage = $Language static [AzureEngSemanticVersion] ParseVersionString([string] $versionString) @@ -70,7 +78,7 @@ class AzureEngSemanticVersion { { # artifically provide these values for non-prereleases to enable easy sorting of them later than prereleases. $this.PrereleaseLabel = "zzz" - $this.PrereleaseNumber = 999 + $this.PrereleaseNumber = 99999999 $this.IsPrerelease = $false } else @@ -80,6 +88,9 @@ class AzureEngSemanticVersion { $this.PrereleaseNumber = [int]$matches["prenumber"] $this.PrereleaseNumberSeparator = $matches["prenumsep"] $this.IsPrerelease = $true + + $this.BuildNumberSeparator = $matches["buildnumsep"] + $this.BuildNumber = $matches["buildnumber"] } } else @@ -94,8 +105,9 @@ class AzureEngSemanticVersion { [bool] HasValidPrereleaseLabel() { if ($this.IsPrerelease -eq $true) { - if ($this.PrereleaseLabel -ne $this.DefaultPrereleaseLabel) { - Write-Host "Unexpected pre-release identifier '$($this.PrereleaseLabel)', should be '$($this.DefaultPrereleaseLabel)'" + if ($this.PrereleaseLabel -ne $this.DefaultPrereleaseLabel -and $this.PrereleaseLabel -ne $this.DefaultAlphaReleaseLabel) { + Write-Host "Unexpected pre-release identifier '$($this.PrereleaseLabel)', "` + "should be '$($this.DefaultPrereleaseLabel)' or '$($this.DefaultAlphaReleaseLabel)'" return $false; } if ($this.PrereleaseNumber -lt 1) @@ -104,6 +116,7 @@ class AzureEngSemanticVersion { return $false; } } + return $true; } @@ -113,7 +126,11 @@ class AzureEngSemanticVersion { if ($this.IsPrerelease) { - $versionString += $this.PrereleaseLabelSeparator + $this.PrereleaseLabel + $this.PrereleaseNumberSeparator + $this.PrereleaseNumber + $versionString += $this.PrereleaseLabelSeparator + $this.PrereleaseLabel + ` + $this.PrereleaseNumberSeparator + $this.PrereleaseNumber + if ($this.BuildNumber) { + $versionString += $this.BuildNumberSeparator + $this.BuildNumber + } } return $versionString; } @@ -129,6 +146,9 @@ class AzureEngSemanticVersion { } else { + if ($this.BuildNumber) { + throw "Cannot increment releases tagged with azure pipelines build numbers" + } $this.PrereleaseNumber++ } } @@ -136,8 +156,9 @@ class AzureEngSemanticVersion { [void] SetupPythonConventions() { # Python uses no separators and "b" for beta so this sets up the the object to work with those conventions - $this.PrereleaseLabelSeparator = $this.PrereleaseNumberSeparator = "" + $this.PrereleaseLabelSeparator = $this.PrereleaseNumberSeparator = $this.BuildNumberSeparator = "" $this.DefaultPrereleaseLabel = "b" + $this.DefaultAlphaReleaseLabel = "a" } [void] SetupDefaultConventions() @@ -145,7 +166,9 @@ class AzureEngSemanticVersion { # Use the default common conventions $this.PrereleaseLabelSeparator = "-" $this.PrereleaseNumberSeparator = "." + $this.BuildNumberSeparator = "." $this.DefaultPrereleaseLabel = "beta" + $this.DefaultAlphaReleaseLabel = "alpha" } static [string[]] SortVersionStrings([string[]] $versionStrings) @@ -157,7 +180,10 @@ class AzureEngSemanticVersion { static [AzureEngSemanticVersion[]] SortVersions([AzureEngSemanticVersion[]] $versions) { - return ($versions | Sort-Object -Property Major, Minor, Patch, PrereleaseLabel, PrereleaseNumber -Descending) + return ($versions | ` + Sort-Object -Descending -Property ` + Major, Minor, Patch, PrereleaseLabel, PrereleaseNumber, ` + @{ Expression = { [int]$_.BuildNumber }; Descending = $true }) } static [void] QuickTests() @@ -165,9 +191,15 @@ class AzureEngSemanticVersion { $versions = @( "1.0.1", "2.0.0", - "2.0.0-alpha.20200920", + "2.0.0-alpha.20200920", + "2.0.0-alpha.20200920.1", "2.0.0-beta.2", "1.0.10", + "2.0.0-alpha.20201221.03", + "2.0.0-alpha.20201221.1", + "2.0.0-alpha.20201221.5", + "2.0.0-alpha.20201221.2", + "2.0.0-alpha.20201221.10", "2.0.0-beta.1", "2.0.0-beta.10", "1.0.0", @@ -179,6 +211,12 @@ class AzureEngSemanticVersion { "2.0.0-beta.10", "2.0.0-beta.2", "2.0.0-beta.1", + "2.0.0-alpha.20201221.10", + "2.0.0-alpha.20201221.5", + "2.0.0-alpha.20201221.03", + "2.0.0-alpha.20201221.2", + "2.0.0-alpha.20201221.1", + "2.0.0-alpha.20200920.1", "2.0.0-alpha.20200920", "1.0.10", "1.0.2", @@ -191,7 +229,7 @@ class AzureEngSemanticVersion { for ($i = 0; $i -lt $expectedSort.Count; $i++) { if ($sort[$i] -ne $expectedSort[$i]) { - Write-Host "Error: Incorrect sort:" + Write-Host "Error: Incorrect version sort:" Write-Host "Expected: " Write-Host $expectedSort Write-Host "Actual:" @@ -200,23 +238,61 @@ class AzureEngSemanticVersion { } } - $devVerString = "1.2.3-alpha.20200828.1" - $devVerNew = [AzureEngSemanticVersion]::new($devVerString) - if (!$devVerNew -or $devVerNew.IsSemVerFormat -ne $false) { - Write-Host "Error: Didn't expect daily dev version to match our semver regex because of the extra .r" + $alphaVerString = "1.2.3-alpha.20200828.9" + $alphaVer = [AzureEngSemanticVersion]::new($alphaVerString) + if (!$alphaVer.IsPrerelease) { + Write-Host "Expected alpha version to be marked as prerelease" + } + if ($alphaVer.Major -ne 1 -or $alphaVer.Minor -ne 2 -or $alphaVer.Patch -ne 3 -or ` + $alphaVer.PrereleaseLabel -ne "alpha" -or $alphaVer.PrereleaseNumber -ne 20200828 -or $alphaVer.BuildNumber -ne 9) { + Write-Host "Error: Didn't correctly parse alpha version string $alphaVerString" + } + if ($alphaVerString -ne $alphaVer.ToString()) { + Write-Host "Error: alpha string did not correctly round trip with ToString. Expected: $($alphaVerString), Actual: $($alphaVer)" + } + + [AzureEngSemanticVersion]::ParseLanguage = "python" + $pythonAlphaVerString = "1.2.3a20200828009" + $pythonAlphaVer = [AzureEngSemanticVersion]::new($pythonAlphaVerString) + if (!$pythonAlphaVer.IsPrerelease) { + Write-Host "Expected python alpha version to be marked as prerelease" + } + # Note: For python we lump build number into prerelease number, since it simplifies the code and regex, and is behaviorally the same + if ($pythonAlphaVer.Major -ne 1 -or $pythonAlphaVer.Minor -ne 2 -or $pythonAlphaVer.Patch -ne 3 ` + -or $pythonAlphaVer.PrereleaseLabel -ne "a" -or $pythonAlphaVer.PrereleaseNumber -ne 20200828 ` + -or $pythonAlphaVer.BuildNumber -ne "009") { + Write-Host "Error: Didn't correctly parse python alpha version string $pythonAlphaVerString" + } + if ($pythonAlphaVerString -ne $pythonAlphaVer.ToString()) { + Write-Host "Error: python alpha string did not correctly round trip with ToString. Expected: $($pythonAlphaVerString), Actual: $($pythonAlphaVer)" } - $devVerparse = [AzureEngSemanticVersion]::ParseVersionString($devVerString) - if ($devVerparse) { - Write-Host "Error: Didn't expect daily dev version to parse because of the extra .r" + + $versions = @("1.0.1", "2.0.0", "2.0.0a20201208001", "2.0.0a20201105020", "2.0.0a20201208012", ` + "2.0.0b2", "1.0.10", "2.0.0b1", "2.0.0b10", "1.0.0", "1.0.0b2", "1.0.2") + $expectedSort = @("2.0.0", "2.0.0b10", "2.0.0b2", "2.0.0b1", "2.0.0a20201208012", "2.0.0a20201208001", ` + "2.0.0a20201105020", "1.0.10", "1.0.2", "1.0.1", "1.0.0", "1.0.0b2") + $sort = [AzureEngSemanticVersion]::SortVersionStrings($versions) + for ($i = 0; $i -lt $expectedSort.Count; $i++) + { + if ($sort[$i] -ne $expectedSort[$i]) { + Write-Host "Error: Incorrect python version sort:" + Write-Host "Expected: " + Write-Host $expectedSort + Write-Host "Actual:" + Write-Host $sort + break + } } + [AzureEngSemanticVersion]::ParseLanguage = "" + $gaVerString = "1.2.3" $gaVer = [AzureEngSemanticVersion]::ParseVersionString($gaVerString) if ($gaVer.Major -ne 1 -or $gaVer.Minor -ne 2 -or $gaVer.Patch -ne 3) { Write-Host "Error: Didn't correctly parse ga version string $gaVerString" } if ($gaVerString -ne $gaVer.ToString()) { - Write-Host "Error: Ga string did not correctly round trip with ToString" + Write-Host "Error: Ga string did not correctly round trip with ToString. Expected: $($gaVerString), Actual: $($gaVer)" } $gaVer.IncrementAndSetToPrerelease() if ("1.3.0-beta.1" -ne $gaVer.ToString()) { @@ -229,7 +305,7 @@ class AzureEngSemanticVersion { Write-Host "Error: Didn't correctly parse beta version string $betaVerString" } if ($betaVerString -ne $betaVer.ToString()) { - Write-Host "Error: beta string did not correctly round trip with ToString" + Write-Host "Error: beta string did not correctly round trip with ToString. Expected: $($betaVerString), Actual: $($betaVer)" } $betaVer.IncrementAndSetToPrerelease() if ("1.2.3-beta.5" -ne $betaVer.ToString()) {