Skip to content

Commit c3c92ed

Browse files
azure-sdkJennyPng
andauthored
Sync eng/common directory with azure-sdk-tools for PR 14495 (#4390)
Sync eng/common directory with azure-sdk-tools for PR Azure/azure-sdk-tools#14495 See [eng/common workflow](https://github.com/Azure/azure-sdk-tools/blob/main/eng/common/README.md#workflow) --------- Co-authored-by: jennypng <63012604+JennyPng@users.noreply.github.com>
1 parent 21d9225 commit c3c92ed

5 files changed

Lines changed: 200 additions & 63 deletions

File tree

eng/common/scripts/ChangeLog-Operations.ps1

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
. "${PSScriptRoot}\SemVer.ps1"
44

55
$RELEASE_TITLE_REGEX = "(?<releaseNoteTitle>^\#+\s+(?<version>$([AzureEngSemanticVersion]::SEMVER_REGEX))(\s+(?<releaseStatus>\(.+\))))"
6+
$PYTHON_RELEASE_TITLE_REGEX = "(?<releaseNoteTitle>^\#+\s+(?<version>$([AzureEngSemanticVersion]::PYTHON_SEMVER_REGEX))(\s+(?<releaseStatus>\(.+\))))"
67
$SECTION_HEADER_REGEX_SUFFIX = "##\s(?<sectionName>.*)"
78
$CHANGELOG_UNRELEASED_STATUS = "(Unreleased)"
89
$CHANGELOG_DATE_FORMAT = "yyyy-MM-dd"
@@ -62,11 +63,13 @@ function Get-ChangeLogEntriesFromContent {
6263
$changeLogEntries | Add-Member -NotePropertyName "InitialAtxHeader" -NotePropertyValue $initialAtxHeader
6364
$releaseTitleAtxHeader = $initialAtxHeader + "#"
6465
$headerLines = @()
66+
$parseLanguage = (Get-Variable -Name "Language" -ValueOnly -ErrorAction "Ignore")
67+
$titleRegex = if ($parseLanguage -eq "python") { $PYTHON_RELEASE_TITLE_REGEX } else { $RELEASE_TITLE_REGEX }
6568

6669
try {
6770
# walk the document, finding where the version specifiers are and creating lists
6871
foreach ($line in $changeLogContent) {
69-
if ($line -match $RELEASE_TITLE_REGEX) {
72+
if ($line -match $titleRegex) {
7073
$changeLogEntry = [pscustomobject]@{
7174
ReleaseVersion = $matches["version"]
7275
ReleaseStatus = $matches["releaseStatus"]

eng/common/scripts/SemVer.ps1

Lines changed: 182 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,20 @@ class AzureEngSemanticVersion : IComparable {
3030
[bool] $IsSemVerFormat
3131
[string] $DefaultPrereleaseLabel
3232
[string] $DefaultAlphaReleaseLabel
33+
# For Python PEP440 post-release support only
34+
[bool] $IsPostRelease
35+
[int] $PostReleaseNumber
36+
[string] $PostReleaseSeparator
3337

3438
# Regex inspired but simplified from https://semver.org/#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string
3539
# Validation: https://regex101.com/r/vkijKf/426
3640
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}))?)?)?"
3741

42+
# Python PEP 440 post-release extension
43+
# Handles all PEP 440 alternate formats: .postN, -postN, _postN, postN, .post.N, .post (implicit 0) (case-insensitive)
44+
# Validation: https://regex101.com/r/rAdOg0/2
45+
static [string] $PYTHON_SEMVER_REGEX = [AzureEngSemanticVersion]::SEMVER_REGEX + "(?:(?<postsep>[.\-_]?)(?<postword>(?i:post))\.?(?<postnum>\d{1,8})?)?"
46+
3847
static [AzureEngSemanticVersion] ParseVersionString([string] $versionString)
3948
{
4049
$version = [AzureEngSemanticVersion]::new($versionString)
@@ -47,36 +56,59 @@ class AzureEngSemanticVersion : IComparable {
4756

4857
static [AzureEngSemanticVersion] ParsePythonVersionString([string] $versionString)
4958
{
50-
$version = [AzureEngSemanticVersion]::ParseVersionString($versionString)
59+
$previousLanguage = (Get-Variable -Name "Language" -ValueOnly -ErrorAction "Ignore")
60+
$global:Language = "python"
61+
$version = $null
62+
try {
63+
$version = [AzureEngSemanticVersion]::new($versionString)
64+
}
65+
finally {
66+
$global:Language = $previousLanguage
67+
}
5168

52-
if (!$version) {
69+
if (!$version.IsSemVerFormat) {
5370
return $null
5471
}
55-
56-
$version.SetupPythonConventions()
5772
return $version
5873
}
5974

6075
AzureEngSemanticVersion([string] $versionString)
6176
{
62-
if ($versionString -match "^$([AzureEngSemanticVersion]::SEMVER_REGEX)$")
77+
$parseLanguage = (Get-Variable -Name "Language" -ValueOnly -ErrorAction "Ignore")
78+
79+
if ($parseLanguage -eq "python") {
80+
$parseRegex = $this.SetupPythonConventions()
81+
}
82+
else {
83+
$parseRegex = $this.SetupDefaultConventions()
84+
}
85+
86+
if ($versionString -match "^${parseRegex}$")
6387
{
6488
$this.IsSemVerFormat = $true
6589
$this.RawVersion = $versionString
6690
$this.Major = [int]$matches.Major
6791
$this.Minor = [int]$matches.Minor
6892
$this.Patch = [int]$matches.Patch
6993

70-
# If Language exists and is set to python setup the python conventions.
71-
$parseLanguage = (Get-Variable -Name "Language" -ValueOnly -ErrorAction "Ignore")
94+
$skipPrelabel = $false
7295
if ($parseLanguage -eq "python") {
73-
$this.SetupPythonConventions()
74-
}
75-
else {
76-
$this.SetupDefaultConventions()
96+
if ($matches['postword']) {
97+
$this.IsPostRelease = $true
98+
$this.PostReleaseNumber = if ($matches['postnum']) { [int]$matches['postnum'] } else { 0 }
99+
$this.PostReleaseSeparator = ".post"
100+
}
101+
elseif ($matches['prelabel'] -and $matches['prelabel'] -ieq 'post') {
102+
# Alternate PEP 440 forms like "1.0.0-post1" or "1.0.0post1" where the regex
103+
# matched "post" as a prerelease label — reinterpret as post-release.
104+
$this.IsPostRelease = $true
105+
$this.PostReleaseNumber = [int]$matches['prenumber']
106+
$this.PostReleaseSeparator = ".post"
107+
$skipPrelabel = $true
108+
}
77109
}
78110

79-
if ($null -eq $matches['prelabel'])
111+
if ($skipPrelabel -or $null -eq $matches['prelabel'])
80112
{
81113
$this.IsPrerelease = $false
82114
$this.VersionType = "GA"
@@ -141,6 +173,9 @@ class AzureEngSemanticVersion : IComparable {
141173
$versionString += $this.BuildNumberSeparator + $this.BuildNumber
142174
}
143175
}
176+
if ($this.IsPostRelease) {
177+
$versionString += $this.PostReleaseSeparator + $this.PostReleaseNumber
178+
}
144179
return $versionString;
145180
}
146181

@@ -150,6 +185,13 @@ class AzureEngSemanticVersion : IComparable {
150185
throw "Cannot increment releases tagged with azure pipelines build numbers"
151186
}
152187

188+
# Clear post-release state before incrementing
189+
if ($this.IsPostRelease) {
190+
$this.IsPostRelease = $false
191+
$this.PostReleaseNumber = 0
192+
$this.PostReleaseSeparator = ""
193+
}
194+
153195
if ($this.PrereleaseLabel)
154196
{
155197
$this.PrereleaseNumber++
@@ -180,22 +222,55 @@ class AzureEngSemanticVersion : IComparable {
180222
$this.IncrementAndSetToPrerelease("Minor")
181223
}
182224

183-
[void] SetupPythonConventions()
225+
[void] IncrementAndSetToPostRelease() {
226+
if ($this.BuildNumber)
227+
{
228+
throw "Cannot increment releases tagged with azure pipelines build numbers"
229+
}
230+
231+
if ($this.IsPostRelease) {
232+
$this.PostReleaseNumber++
233+
}
234+
else {
235+
$this.IsPostRelease = $true
236+
$this.PostReleaseNumber = 1
237+
$this.PostReleaseSeparator = ".post"
238+
}
239+
}
240+
241+
# Sets the version to a prerelease state with the specified label and number.
242+
# This clears any post-release state to ensure a clean prerelease version.
243+
[void] SetPrerelease([string] $Label, [int] $Number) {
244+
# Clear post-release state
245+
if ($this.IsPostRelease) {
246+
$this.IsPostRelease = $false
247+
$this.PostReleaseNumber = 0
248+
$this.PostReleaseSeparator = ""
249+
}
250+
251+
$this.PrereleaseLabel = $Label
252+
$this.PrereleaseNumber = $Number
253+
$this.IsPrerelease = $true
254+
}
255+
256+
[string] SetupPythonConventions()
184257
{
185258
# Python uses no separators and "b" for beta so this sets up the the object to work with those conventions
186259
$this.PrereleaseLabelSeparator = $this.PrereleaseNumberSeparator = $this.BuildNumberSeparator = ""
187260
$this.DefaultPrereleaseLabel = "b"
188261
$this.DefaultAlphaReleaseLabel = "a"
262+
return [AzureEngSemanticVersion]::PYTHON_SEMVER_REGEX
189263
}
190264

191-
[void] SetupDefaultConventions()
265+
[string] SetupDefaultConventions()
192266
{
193267
# Use the default common conventions
194268
$this.PrereleaseLabelSeparator = "-"
195269
$this.PrereleaseNumberSeparator = "."
196270
$this.BuildNumberSeparator = "."
197271
$this.DefaultPrereleaseLabel = "beta"
198272
$this.DefaultAlphaReleaseLabel = "alpha"
273+
return [AzureEngSemanticVersion]::SEMVER_REGEX
199274
}
200275

201276
[int] CompareTo($other)
@@ -239,12 +314,29 @@ class AzureEngSemanticVersion : IComparable {
239314
$ret = $thisPrereleaseNumber.CompareTo($otherPrereleaseNumber)
240315
if ($ret) { return $ret }
241316

242-
return ([int] $this.BuildNumber).CompareTo([int] $other.BuildNumber)
317+
$thisBuildNumber = if ($this.BuildNumber) { [int] $this.BuildNumber } else { 0 }
318+
$otherBuildNumber = if ($other.BuildNumber) { [int] $other.BuildNumber } else { 0 }
319+
$ret = $thisBuildNumber.CompareTo($otherBuildNumber)
320+
if ($ret) { return $ret }
321+
322+
# Post-release versions sort after their base version
323+
$thisPost = if ($this.IsPostRelease) { 1 } else { 0 }
324+
$otherPost = if ($other.IsPostRelease) { 1 } else { 0 }
325+
$ret = $thisPost.CompareTo($otherPost)
326+
if ($ret) { return $ret }
327+
328+
return $this.PostReleaseNumber.CompareTo($other.PostReleaseNumber)
243329
}
244330

245331
static [string[]] SortVersionStrings([string[]] $versionStrings)
246332
{
247-
$versions = $versionStrings | ForEach-Object { [AzureEngSemanticVersion]::ParseVersionString($_) }
333+
$parseLanguage = (Get-Variable -Name "Language" -ValueOnly -ErrorAction "Ignore")
334+
if ($parseLanguage -eq "python") {
335+
$versions = $versionStrings | ForEach-Object { [AzureEngSemanticVersion]::ParsePythonVersionString($_) }
336+
}
337+
else {
338+
$versions = $versionStrings | ForEach-Object { [AzureEngSemanticVersion]::ParseVersionString($_) }
339+
}
248340
$sortedVersions = [AzureEngSemanticVersion]::SortVersions($versions)
249341
return ($sortedVersions | ForEach-Object { $_.RawVersion })
250342
}
@@ -429,6 +521,80 @@ class AzureEngSemanticVersion : IComparable {
429521
Write-Host "Error: version string did not correctly increment. Expected: $expected, Actual: $version"
430522
}
431523

524+
# Python post-release parsing tests
525+
$postVerString = "1.0.0.post1"
526+
$postVer = [AzureEngSemanticVersion]::ParsePythonVersionString($postVerString)
527+
if ($postVer.Major -ne 1 -or $postVer.Minor -ne 0 -or $postVer.Patch -ne 0 -or `
528+
!$postVer.IsPostRelease -or $postVer.PostReleaseNumber -ne 1 -or $postVer.IsPrerelease) {
529+
Write-Host "Error: Didn't correctly parse python post-release string $postVerString"
530+
}
531+
if ($postVerString -ne $postVer.ToString()) {
532+
Write-Host "Error: post-release string did not correctly round trip with ToString. Expected: $($postVerString), Actual: $($postVer)"
533+
}
534+
535+
# Implicit post-release number (PEP 440: 1.0.0.post == 1.0.0.post0)
536+
$implicitPostVerString = "1.0.0.post"
537+
$implicitPostVer = [AzureEngSemanticVersion]::ParsePythonVersionString($implicitPostVerString)
538+
if ($null -eq $implicitPostVer -or !$implicitPostVer.IsSemVerFormat) {
539+
Write-Host "Error: Failed to parse implicit post-release string $implicitPostVerString"
540+
}
541+
elseif ($implicitPostVer.Major -ne 1 -or $implicitPostVer.Minor -ne 0 -or $implicitPostVer.Patch -ne 0 -or `
542+
!$implicitPostVer.IsPostRelease -or $implicitPostVer.PostReleaseNumber -ne 0) {
543+
Write-Host "Error: Didn't correctly parse implicit post-release string $implicitPostVerString"
544+
}
545+
$expected = "1.0.0.post0"
546+
if ($expected -ne $implicitPostVer.ToString()) {
547+
Write-Host "Error: implicit post-release did not normalize. Expected: $expected, Actual: $($implicitPostVer)"
548+
}
549+
550+
# Prerelease + post-release
551+
$preBetaPostString = "1.0.0b2.post1"
552+
$preBetaPost = [AzureEngSemanticVersion]::ParsePythonVersionString($preBetaPostString)
553+
if ($preBetaPost.Major -ne 1 -or $preBetaPost.Minor -ne 0 -or $preBetaPost.Patch -ne 0 -or `
554+
$preBetaPost.PrereleaseLabel -ne "b" -or $preBetaPost.PrereleaseNumber -ne 2 -or `
555+
!$preBetaPost.IsPostRelease -or $preBetaPost.PostReleaseNumber -ne 1) {
556+
Write-Host "Error: Didn't correctly parse python prerelease post-release string $preBetaPostString"
557+
}
558+
if ($preBetaPostString -ne $preBetaPost.ToString()) {
559+
Write-Host "Error: prerelease post-release string did not correctly round trip with ToString. Expected: $($preBetaPostString), Actual: $($preBetaPost)"
560+
}
561+
562+
# Post-release alternate separators normalize to canonical form
563+
$expectedNormalized = "1.0.0.post1"
564+
foreach ($altVerString in @("1.0.0-post1", "1.0.0_post1", "1.0.0post1")) {
565+
$parsed = [AzureEngSemanticVersion]::ParsePythonVersionString($altVerString)
566+
if ($null -eq $parsed -or !$parsed.IsPostRelease -or $parsed.PostReleaseNumber -ne 1) {
567+
Write-Host "Error: Failed to parse alternate post-release format $altVerString"
568+
}
569+
if ($expectedNormalized -ne $parsed.ToString()) {
570+
Write-Host "Error: Alternate post-release '$altVerString' did not normalize. Expected: $expectedNormalized, Actual: $($parsed)"
571+
}
572+
}
573+
574+
# Post-release increment clears post state
575+
$postIncVer = [AzureEngSemanticVersion]::ParsePythonVersionString("1.0.0.post1")
576+
$postIncVer.IncrementAndSetToPrerelease()
577+
$expected = "1.1.0b1"
578+
if ($expected -ne $postIncVer.ToString()) {
579+
Write-Host "Error: post-release increment did not produce expected result. Expected: $expected, Actual: $($postIncVer)"
580+
}
581+
582+
# Post-release increment stays in post state
583+
$postBumpVer = [AzureEngSemanticVersion]::ParsePythonVersionString("1.0.0.post1")
584+
$postBumpVer.IncrementAndSetToPostRelease()
585+
$expected = "1.0.0.post2"
586+
if ($expected -ne $postBumpVer.ToString()) {
587+
Write-Host "Error: post-release bump did not produce expected result. Expected: $expected, Actual: $($postBumpVer)"
588+
}
589+
590+
# Non-post version enters post state
591+
$gaToPost = [AzureEngSemanticVersion]::ParsePythonVersionString("1.0.0")
592+
$gaToPost.IncrementAndSetToPostRelease()
593+
$expected = "1.0.0.post1"
594+
if ($expected -ne $gaToPost.ToString()) {
595+
Write-Host "Error: GA to post-release did not produce expected result. Expected: $expected, Actual: $($gaToPost)"
596+
}
597+
432598
Write-Host "QuickTests done"
433599
}
434600
}

eng/common/scripts/SetTestPipelineVersion.ps1

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -98,9 +98,7 @@ if ($Artifacts -and $Artifacts.Count -gt 0) {
9898
$newVersion = [AzureEngSemanticVersion]::new($semVarsSorted[0])
9999
}
100100

101-
$newVersion.PrereleaseLabel = $newVersion.DefaultPrereleaseLabel
102-
$newVersion.PrereleaseNumber = $BuildID
103-
$newVersion.IsPrerelease = $True
101+
$newVersion.SetPrerelease($newVersion.DefaultPrereleaseLabel, $BuildID)
104102

105103
Write-Host "Version to publish [ $($newVersion.ToString()) ]"
106104

@@ -147,9 +145,7 @@ if ($Artifacts -and $Artifacts.Count -gt 0) {
147145
$newVersion = [AzureEngSemanticVersion]::new($semVarsSorted[0])
148146
}
149147

150-
$newVersion.PrereleaseLabel = $newVersion.DefaultPrereleaseLabel
151-
$newVersion.PrereleaseNumber = $BuildID
152-
$newVersion.IsPrerelease = $True
148+
$newVersion.SetPrerelease($newVersion.DefaultPrereleaseLabel, $BuildID)
153149

154150
Write-Host "Version to publish [ $($newVersion.ToString()) ]"
155151

eng/common/scripts/artifact-metadata-parsing.ps1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
. (Join-Path $EngCommonScriptsDir SemVer.ps1)
22

3-
$SDIST_PACKAGE_REGEX = "^(?<package>.*)\-(?<versionstring>$([AzureEngSemanticVersion]::SEMVER_REGEX))"
3+
$SDIST_PACKAGE_REGEX = "^(?<package>.*)\-(?<versionstring>$([AzureEngSemanticVersion]::PYTHON_SEMVER_REGEX))"
44

55
# Posts a github release for each item of the pkgList variable. Silently continue
66
function CreateReleases($pkgList, $releaseApiUrl, $releaseSha) {

0 commit comments

Comments
 (0)