Skip to content

Commit

Permalink
Support alpha and azure pipeline build version formats for SemVer par…
Browse files Browse the repository at this point in the history
…sing and sorting (#1273)

This PR updates the semver tooling to support our dev feed formats like:

```
5.3.0-alpha.20200928.3
# Python
2.0.0a20201208001
```

Supporting this format will enable us to sort release feeds that contain alpha releases, primarily so we can target smoke tests against the latest nightly and/or preview release, where applicable. Currently the smoke tests (at least for .net) have a bug where they are testing against very old dev releases, since they just pick the last item in the API response.
  • Loading branch information
benbp authored Dec 12, 2020
1 parent e4bd2be commit cd67d61
Showing 1 changed file with 94 additions and 18 deletions.
112 changes: 94 additions & 18 deletions eng/common/scripts/SemVer.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -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'.
#>

Expand All @@ -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 = "(?<major>0|[1-9]\d*)\.(?<minor>0|[1-9]\d*)\.(?<patch>0|[1-9]\d*)(?:(?<presep>-?)(?<prelabel>[a-zA-Z-]*)(?<prenumsep>\.?)(?<prenumber>0|[1-9]\d*))?"
# Validation: https://regex101.com/r/vkijKf/426
static [string] $SEMVER_REGEX = "(?<major>0|[1-9]\d*)\.(?<minor>0|[1-9]\d*)\.(?<patch>0|[1-9]\d*)(?:(?<presep>-?)(?<prelabel>[a-zA-Z]+)(?:(?<prenumsep>\.?)(?<prenumber>[0-9]{1,8})(?:(?<buildnumsep>\.?)(?<buildnumber>\d{1,3}))?)?)?"
static [string] $ParseLanguage = $Language

static [AzureEngSemanticVersion] ParseVersionString([string] $versionString)
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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)
Expand All @@ -104,6 +116,7 @@ class AzureEngSemanticVersion {
return $false;
}
}

return $true;
}

Expand All @@ -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;
}
Expand All @@ -129,23 +146,29 @@ class AzureEngSemanticVersion {
}
else
{
if ($this.BuildNumber) {
throw "Cannot increment releases tagged with azure pipelines build numbers"
}
$this.PrereleaseNumber++
}
}

[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()
{
# Use the default common conventions
$this.PrereleaseLabelSeparator = "-"
$this.PrereleaseNumberSeparator = "."
$this.BuildNumberSeparator = "."
$this.DefaultPrereleaseLabel = "beta"
$this.DefaultAlphaReleaseLabel = "alpha"
}

static [string[]] SortVersionStrings([string[]] $versionStrings)
Expand All @@ -157,17 +180,26 @@ 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()
{
$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",
Expand All @@ -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",
Expand All @@ -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:"
Expand All @@ -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()) {
Expand All @@ -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()) {
Expand Down

0 comments on commit cd67d61

Please sign in to comment.