From 5bb3b058c8e7f682758b72559c8a7822cfe0030e Mon Sep 17 00:00:00 2001 From: Keith Mahoney <41657372+kmahone@users.noreply.github.com> Date: Tue, 8 Mar 2022 14:45:45 -0800 Subject: [PATCH] Fix PGO Pipeline (#6796) --- .../MUX-BuildProject-Steps.yml | 1 + tools/MUXPGODatabase/config.ps1 | 6 +- tools/MUXPGODatabase/generate-nuspec.ps1 | 5 +- tools/MUXPGODatabase/restore-pgodb.ps1 | 80 ++++++++++---- tools/MUXPGODatabase/version.ps1 | 100 ++++++++++++------ 5 files changed, 136 insertions(+), 56 deletions(-) diff --git a/build/AzurePipelinesTemplates/MUX-BuildProject-Steps.yml b/build/AzurePipelinesTemplates/MUX-BuildProject-Steps.yml index 587387ce90..54ff91a18e 100644 --- a/build/AzurePipelinesTemplates/MUX-BuildProject-Steps.yml +++ b/build/AzurePipelinesTemplates/MUX-BuildProject-Steps.yml @@ -48,6 +48,7 @@ steps: targetType: filePath workingDirectory: $(Build.SourcesDirectory)\tools\MUXPGODatabase filePath: $(Build.SourcesDirectory)\tools\MUXPGODatabase\restore-pgodb.ps1 + arguments: -NuGetConfigPath $(Build.SourcesDirectory)\nuget.config - task: 333b11bd-d341-40d9-afcf-b32d5ce6f23b@2 displayName: 'NuGet restore ${{ parameters.solutionPath }}' diff --git a/tools/MUXPGODatabase/config.ps1 b/tools/MUXPGODatabase/config.ps1 index e9415ba69e..78cf743a6d 100644 --- a/tools/MUXPGODatabase/config.ps1 +++ b/tools/MUXPGODatabase/config.ps1 @@ -3,5 +3,7 @@ $packageId = "MUXPGODatabase" # Get release version [xml] $customProps = ( Get-Content "..\..\version.props" ) -$releaseVersionMajor = ( [int]::Parse( $customProps.GetElementsByTagName("MUXVersionMajor").'#text' ) ) -$releaseVersionMinor = ( [int]::Parse( $customProps.GetElementsByTagName("MUXVersionMinor").'#text' ) ) \ No newline at end of file +$releaseVersionMajor = ( [int64]::Parse( $customProps.GetElementsByTagName("MUXVersionMajor").'#text' ) ) +$releaseVersionMinor = ( [int64]::Parse( $customProps.GetElementsByTagName("MUXVersionMinor").'#text' ) ) +$releaseVersionPatch = [int64] 0 +$releaseVersionPrerelease = $null \ No newline at end of file diff --git a/tools/MUXPGODatabase/generate-nuspec.ps1 b/tools/MUXPGODatabase/generate-nuspec.ps1 index d382e5c40a..eac38ef71d 100644 --- a/tools/MUXPGODatabase/generate-nuspec.ps1 +++ b/tools/MUXPGODatabase/generate-nuspec.ps1 @@ -6,6 +6,7 @@ Param( . .\template.ps1 . .\config.ps1 -$version = FormatVersion ( MakeVersion $releaseVersionMajor $releaseVersionMinor ( GetDatetimeStamp $pgoBranch ) ) -Write-Host ( "PGO INSTRUMENT: generating {0} version {1}" -f $packageId, $version ) +$forkPoint = ( GetForkPoint $pgoBranch ) +$version = FormatVersion ( MakeVersion $releaseVersionMajor $releaseVersionMinor $releaseVersionPatch $releaseVersionPrerelease $forkPoint.DateString $forkPoint.BranchString ) +Write-Host ( "PGO INSTRUMENT: generating {0} version {1} ({2})" -f $packageId, $version, $forkPoint.SHA ) FillOut-Template $templatePath $outputPath @{ "version" = $version; "id" = $packageId } \ No newline at end of file diff --git a/tools/MUXPGODatabase/restore-pgodb.ps1 b/tools/MUXPGODatabase/restore-pgodb.ps1 index 6001abd433..ab76f320ca 100644 --- a/tools/MUXPGODatabase/restore-pgodb.ps1 +++ b/tools/MUXPGODatabase/restore-pgodb.ps1 @@ -1,47 +1,85 @@ +Param( + [Parameter(Position=0, Mandatory=$true)] + [string] $NuGetConfigPath +) + . .\version.ps1 . .\template.ps1 . .\config.ps1 -$feedUri = "https://pkgs.dev.azure.com/ms/microsoft-ui-xaml/_packaging/MUX-Dependencies/nuget/v2" +function Get-AvailablePackages ( $package ) +{ + $result = @() + + $output = ( & nuget.exe list $package -prerelease -allversions -configfile $NuGetConfigPath ) -$currentVersion = MakeVersion $releaseVersionMajor $releaseVersionMinor ( GetDatetimeStamp $pgoBranch ) + if ( $LastExitCode -ne 0 ) + { + throw "FAILED: nuget.exe list" + } -Write-Host ( "PGO OPTIMIZE: requesting {0} version {1}" -f $packageId, ( FormatVersion $currentVersion ) ) + foreach ( $line in $output ) + { + $name, $version = $line.Split(" ") -$packageSource = Register-PackageSource -ForceBootstrap -Name MUX_Dependencies -Location $feedUri -ProviderName NuGet -Trusted -$packages = ( Find-Package $packageId -Source MUX_Dependencies -AllowPrereleaseVersions -AllVersions ) | Sort-Object -Property Version -Descending + if ( $name -eq $package ) + { + $result += ( MakeVersionFromString $version ) + } + } -$best = $null + return $result +} -foreach ( $existing in $packages ) +function Install-Package ( $package, $version ) { - $existingVersion = MakeVersionFromString $existing.Version + & nuget.exe install $package -prerelease -version $version -configfile $NuGetConfigPath + + if ( $LastExitCode -ne 0 ) + { + throw "FAILED: nuget.exe install" + } +} + +$forkPoint = ( GetForkPoint $pgoBranch ) + +$requestedVersion = MakeVersion $releaseVersionMajor $releaseVersionMinor $releaseVersionPatch $releaseVersionPrerelease $forkPoint.DateString $forkPoint.BranchString + +Write-Host ( "PGO OPTIMIZE: requesting {0} version {1} ({2})" -f $packageId, ( FormatVersion $requestedVersion ), $forkPoint.SHA ) - if ( ( CompareBranches $existingVersion $currentVersion ) -eq $False -or - ( CompareReleases $existingVersion $currentVersion ) -ne 0 ) +$packageVersions = ( Get-AvailablePackages $packageId ) | Sort-Object -Descending -Property Major, Minor, Patch, Prerelease, Branch, Revision + +$bestVersion = $null + +foreach ( $existingVersion in $packageVersions ) +{ + if ( ( CompareReleaseAndBranch $existingVersion $requestedVersion ) -eq $False ) { - # If this is different release or branch, then skip it. + # If this is different release number, pre-release tag or branch, then skip it. continue } - if ( ( CompareRevisions $existingVersion $currentVersion ) -le 0 ) + # Sorting guarantees that all eligible entries are consecutive and sorted according to decreasing revision time stamp. + # Revisions are fixed, 10 character strings containing numbers (YYMMHHhhmm). Sorting will arrange them reverse-chronologically. + # Once we are here, we are beginning of that segment. + + if ( ( CompareRevisions $existingVersion $requestedVersion ) -le 0 ) { - # Version are sorted in descending order, the first one less than or equal to the current is the one we want. - # NOTE: at this point the only difference between versions will be revision (date-time stamp) - # which is formatted as a fixed-length string, so string comparison WILL sort it correctly. - $best = $existing + # Revisions are sorted in descending order, the first one less than or equal to the current is the one we want. + $bestVersion = $existingVersion break } } -if ( $best -eq $null ) +if ( $bestVersion -eq $null ) { throw "Appropriate database cannot be found" } -Write-Host ( "PGO OPTIMIZE: picked {0} version {1}" -f $packageId, $best.Version ) +$bestVersionAsString = ( FormatVersion $bestVersion ) + +Write-Host ( "PGO OPTIMIZE: picked {0} version {1}" -f $packageId, $bestVersionAsString ) -$best | Install-Package -Destination ..\..\packages -Force -$packageSource | Unregister-PackageSource +Install-Package $packageId $bestVersionAsString -FillOut-Template "PGO.version.props.template" "PGO.version.props" @{ "version" = $best.Version; "id" = $packageId } \ No newline at end of file +FillOut-Template "PGO.version.props.template" "PGO.version.props" @{ "version" = $bestVersionAsString; "id" = $packageId } \ No newline at end of file diff --git a/tools/MUXPGODatabase/version.ps1 b/tools/MUXPGODatabase/version.ps1 index c6fb8f677a..ada31ab101 100644 --- a/tools/MUXPGODatabase/version.ps1 +++ b/tools/MUXPGODatabase/version.ps1 @@ -1,61 +1,95 @@ -function MakeVersion ( $major, $minor, $datetimeStamp ) -{ - $revision, $branch = $datetimeStamp.Split("-") - - if ( $branch -eq $null ) - { - $branch = "" - } +$REVISION_FMT = 'yyMMddHHmm' +function MakeVersion ( $major, $minor, $patch, $prerelease, $revision, $branch ) +{ return [PSCustomObject] @{ - Major = $major - Minor = $minor - Revision = $revision - Branch = $branch + Major = [int64] $major + Minor = [int64] $minor + Patch = [int64] $patch + Prerelease = [string] $prerelease + Revision = [string] $revision + Branch = [string] $branch } } function MakeVersionFromString ( $str ) { - $parts = $str.Split(".") - return MakeVersion ( [int]::Parse($parts[0]) ) ( [int]::Parse($parts[1]) ) $parts[2] + $match = [Regex]::match($str, '^(?0|[1-9]\d*)\.(?0|[1-9]\d*)\.(?0|[1-9]\d*)(?:-(?(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+(?[0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$') + + if ( -not $match.Success ) + { + throw "Failed to parse version string." + } + + $REVISION_RE = '(?[0-9]{10})' + $BRANCH_RE = '(?[a-zA-Z0-9-]+)' + + $prereleaseMatch = [Regex]::match($match.Groups["prerelease"].Value, "^((?.*)-)?$REVISION_RE-$BRANCH_RE$") + + if ( $prereleaseMatch.Success ) + { + # new way: revision + branch encoded in prerelease tag + return MakeVersion $match.Groups["major"].Value ` + $match.Groups["minor"].Value ` + $match.Groups["patch"].Value ` + $prereleaseMatch.Groups["actualprerelease"].Value ` + $prereleaseMatch.Groups["revision"].Value ` + $prereleaseMatch.Groups["branch"].Value + } + else + { + # old way: revision in patch and branch is prerelease + $patchMatch = [Regex]::match($match.Groups["patch"].Value, "^$REVISION_RE$") + $prereleaseMatch = [Regex]::match($match.Groups["prerelease"].Value, "^$BRANCH_RE$") + + if ( -not ( $patchMatch.Success -and $prereleaseMatch.Success ) ) + { + throw "Failed to parse fork point form version string." + } + + return MakeVersion $match.Groups["major"].Value ` + $match.Groups["minor"].Value ` + 0 ` + $null ` + $patchMatch.Groups["revision"].Value ` + $prereleaseMatch.Groups["branch"].Value + } } function FormatVersion ( $version ) { $branch = "" - if ( $version.Branch -ne "" ) + if ( $version.Branch -and $version.Branch -ne "" ) { $branch = "-{0}" -f $version.Branch } - return "{0}.{1}.{2}{3}" -f $version.Major, $version.Minor, $version.Revision, $branch -} + $prerelease = "" -function CompareReleases ( $version1, $version2 ) -{ - $cmpMajor = [Math]::Sign($version1.Major - $version2.Major) - - if ( $cmpMajor -ne 0 ) + if ( $version.Prerelease -and $version.Prerelease -ne "" ) { - return $cmpMajor + $prerelease = "-{0}" -f $version.Prerelease } - return [Math]::Sign($version1.Minor - $version2.Minor) + return "{0}.{1}.{2}{3}-{4}{5}" -f $version.Major, $version.Minor, $version.Patch, $prerelease, $version.Revision, $branch } -function CompareRevisions ( $version1, $version2 ) +function CompareReleaseAndBranch ( $version1, $version2 ) { - return [Math]::Sign($version1.Revision - $version2.Revision) + return ( $version1.Major -eq $version2.Major ) -and + ( $version1.Minor -eq $version2.Minor ) -and + ( $version1.Patch -eq $version2.Patch ) -and + ( $version1.Prerelease -like $version2.Prerelease ) -and + ( $version1.Branch -like $version2.Branch ) } -function CompareBranches ( $version1, $version2 ) +function CompareRevisions ( $version1, $version2 ) { - return $version1.Branch -eq $version2.Branch + return [DateTime]::ParseExact($version1.Revision, $REVISION_FMT, $null) - [DateTime]::ParseExact($version2.Revision, $REVISION_FMT, $null) } -function GetDatetimeStamp ( $pgoBranch ) +function GetForkPoint ( $pgoBranch ) { $forkSHA = $( git merge-base origin/$pgoBranch HEAD ) @@ -64,12 +98,16 @@ function GetDatetimeStamp ( $pgoBranch ) throw "FAILED: git merge-base" } - $forkDate = ( Get-Date -Date $( git log -1 $forkSHA --date=iso --pretty=format:"%ad" ) ).ToUniversalTime().ToString("yyMMddHHmm") + $forkDate = ( Get-Date -Date $( git log -1 $forkSHA --date=iso --pretty=format:"%ad" ) ).ToUniversalTime().ToString($REVISION_FMT) if ( $LastExitCode -ne 0 ) { throw "FAILED: Get forkDate" } - return $forkDate + "-" + $pgoBranch.Replace("/", "_").Replace("-", "_").Replace(".", "_") + return [PSCustomObject] @{ + DateString = $forkDate + BranchString = ( $pgoBranch -replace "(/|\.|@|>|<)", "-" ) + SHA = $forkSHA + } } \ No newline at end of file