diff --git a/eng/common/scripts/Helpers/DevOps-WorkItem-Helpers.ps1 b/eng/common/scripts/Helpers/DevOps-WorkItem-Helpers.ps1 new file mode 100644 index 0000000000000..061134f90d9b0 --- /dev/null +++ b/eng/common/scripts/Helpers/DevOps-WorkItem-Helpers.ps1 @@ -0,0 +1,805 @@ + +$ReleaseDevOpsOrgParameters = @("--organization", "https://dev.azure.com/azure-sdk") +$ReleaseDevOpsCommonParameters = $ReleaseDevOpsOrgParameters + @("--output", "json") +$ReleaseDevOpsCommonParametersWithProject = $ReleaseDevOpsCommonParameters + @("--project", "Release") + +function Invoke-AzBoardsCmd($subCmd, $parameters, $output = $true) +{ + $azCmdStr = "az boards ${subCmd} $($parameters -join ' ')" + if ($output) { + Write-Host $azCmdStr + } + return Invoke-Expression "$azCmdStr" | ConvertFrom-Json -AsHashTable +} + +function LoginToAzureDevops([string]$devop_pat) +{ + if (!$devop_pat) { + return + } + $azCmdStr = "'$devops_pat' | az devops login $($ReleaseDevOpsOrgParameters -join ' ')" + Invoke-Expression $azCmdStr +} + +function BuildHashKeyNoNull() +{ + $filterNulls = $args | Where-Object { $_ } + # if we had any nulls then return null + if (!$filterNulls -or $args.Count -ne $filterNulls.Count) { + return $null + } + return BuildHashKey $args +} + +function BuildHashKey() +{ + # if no args or the first arg is null return null + if ($args.Count -lt 1 -or !$args[0]) { + return $null + } + + # exclude null values + $keys = $args | Where-Object { $_ } + return $keys -join "|" +} + +$parentWorkItems = @{} +function FindParentWorkItem($serviceName, $packageDisplayName, $outputCommand = $true) +{ + $key = BuildHashKey $serviceName $packageDisplayName + if ($key -and $parentWorkItems.ContainsKey($key)) { + return $parentWorkItems[$key] + } + + if ($serviceName) { + $serviceCondition = "[ServiceName] = '${serviceName}'" + if ($packageDisplayName) { + $serviceCondition += " AND [PackageDisplayName] = '${packageDisplayName}'" + } + else { + $serviceCondition += " AND [PackageDisplayName] = ''" + } + } + else { + $serviceCondition = "[ServiceName] <> ''" + } + + $parameters = $ReleaseDevOpsCommonParametersWithProject + $parameters += "--wiql" + $parameters += "`"SELECT [ID], [ServiceName], [PackageDisplayName], [Parent] FROM WorkItems WHERE [Work Item Type] = 'Epic' AND ${serviceCondition}`"" + + $workItems = Invoke-AzBoardsCmd "query" $parameters $outputCommand + + foreach ($wi in $workItems) { + $localKey = BuildHashKey $wi.fields["Custom.ServiceName"] $wi.fields["Custom.PackageDisplayName"] + if (!$localKey) { continue } + if ($parentWorkItems.ContainsKey($localKey)) { + Write-Warning "Already found parent [$($parentWorkItems[$localKey].id)] with key [$localKey], using that one instead of [$($wi.id)]." + } + else { + Write-Verbose "[$($wi.id)]$localKey - Cached" + $parentWorkItems[$localKey] = $wi + } + } + + if ($key -and $parentWorkItems.ContainsKey($key)) { + return $parentWorkItems[$key] + } + return $null +} + +$packageWorkItems = @{} +$packageWorkItemWithoutKeyFields = @{} + +function FindLatestPackageWorkItem($lang, $packageName, $outputCommand = $true) +{ + # Cache all the versions of this package and language work items + $null = FindPackageWorkItem $lang $packageName -includeClosed $true -outputCommand $outputCommand + + $latestWI = $null + foreach ($wi in $packageWorkItems.Values) + { + if ($wi.fields["Custom.Language"] -ne $lang) { continue } + if ($wi.fields["Custom.Package"] -ne $packageName) { continue } + + if (!$latestWI) { + $latestWI = $wi + continue + } + + # Note this only does string sorting which is enough for our current usages + # if we need absolute sorting at some point we would need to parse these versions + if ($wi.fields["Custom.PackageVersionMajorMinor"] -gt $latestWI.fields["Custom.PackageVersionMajorMinor"]) { + $latestWI = $wi + } + } + return $latestWI +} + +function FindPackageWorkItem($lang, $packageName, $version, $outputCommand = $true, $includeClosed = $false) +{ + $key = BuildHashKeyNoNull $lang $packageName $version + if ($key -and $packageWorkItems.ContainsKey($key)) { + return $packageWorkItems[$key] + } + + $fields = @() + $fields += "ID" + $fields += "State" + $fields += "System.AssignedTo" + $fields += "Microsoft.VSTS.Common.StateChangeDate" + $fields += "Parent" + $fields += "Language" + $fields += "Package" + $fields += "PackageDisplayName" + $fields += "Title" + $fields += "PackageType" + $fields += "PackageTypeNewLibrary" + $fields += "PackageVersionMajorMinor" + $fields += "PackageRepoPath" + $fields += "ServiceName" + $fields += "Planned Packages" + $fields += "Shipped Packages" + $fields += "PackageBetaVersions" + $fields += "PackageGAVersion" + $fields += "PackagePatchVersions" + + $fieldList = ($fields | ForEach-Object { "[$_]"}) -join ", " + $query = "SELECT ${fieldList} FROM WorkItems WHERE [Work Item Type] = 'Package'" + + if (!$includeClosed -and !$lang) { + $query += " AND [State] <> 'No Active Development'" + } + if ($lang) { + $query += " AND [Language] = '${lang}'" + } + if ($packageName) { + $query += " AND [Package] = '${packageName}'" + } + if ($version) { + $query += " AND [PackageVersionMajorMinor] = '${version}'" + } + $parameters = $ReleaseDevOpsCommonParametersWithProject + $parameters += "--wiql", "`"${query}`"" + + $workItems = Invoke-AzBoardsCmd "query" $parameters $outputCommand + + foreach ($wi in $workItems) + { + $localKey = BuildHashKeyNoNull $wi.fields["Custom.Language"] $wi.fields["Custom.Package"] $wi.fields["Custom.PackageVersionMajorMinor"] + if (!$localKey) { + $packageWorkItemWithoutKeyFields[$wi.id] = $wi + Write-Host "Skipping package [$($wi.id)]$($wi.fields['System.Title']) which is missing required fields language, package, or version." + continue + } + if ($packageWorkItems.ContainsKey($localKey)) { + Write-Warning "Already found package [$($packageWorkItems[$localKey].id)] with key [$localKey], using that one instead of [$($wi.id)]." + } + else { + Write-Verbose "Caching package [$($wi.id)] for [$localKey]" + $packageWorkItems[$localKey] = $wi + } + } + + if ($key -and $packageWorkItems.ContainsKey($key)) { + return $packageWorkItems[$key] + } + return $null +} + +function InitializeWorkItemCache($outputCommand = $true, $includeClosed = $false) +{ + # Pass null to cache all service parents + $null = FindParentWorkItem -serviceName $null -packageDisplayName $null -outputCommand $outputCommand + + # Pass null to cache all the package items + $null = FindPackageWorkItem -lang $null -packageName $null -version $null -outputCommand $outputCommand -includeClosed $includeClosed +} + +function GetCachedPackageWorkItems() +{ + return $packageWorkItems.Values +} + +function UpdateWorkItemParent($childWorkItem, $parentWorkItem, $outputCommand = $true) +{ + $childId = $childWorkItem.id + $existingParentId = $childWorkItem.fields["System.Parent"] + $newParentId = $parentWorkItem.id + + if ($existingParentId -eq $newParentId) { + return + } + + CreateWorkItemParent $childId $newParentId $existingParentId -outputCommand $outputCommand + $childWorkItem.fields["System.Parent"] = $newParentId +} + +function CreateWorkItemParent($id, $parentId, $oldParentId, $outputCommand = $true) +{ + # Have to remove old parent first if you want to add a new parent. + if ($oldParentId) + { + $parameters = $ReleaseDevOpsCommonParameters + $parameters += "--yes" + $parameters += "--id", $id + $parameters += "--relation-type", "parent" + $parameters += "--target-id", $oldParentId + + Invoke-AzBoardsCmd "work-item relation remove" $parameters $outputCommand | Out-Null + } + + $parameters = $ReleaseDevOpsCommonParameters + $parameters += "--id", $id + $parameters += "--relation-type", "parent" + $parameters += "--target-id", $parentId + + Invoke-AzBoardsCmd "work-item relation add" $parameters $outputCommand | Out-Null +} +function CreateWorkItem($title, $type, $iteration, $area, $fields, $assignedTo, $parentId, $outputCommand = $true) +{ + $parameters = $ReleaseDevOpsCommonParametersWithProject + $parameters += "--title", "`"${title}`"" + $parameters += "--type", "`"${type}`"" + $parameters += "--iteration", "`"${iteration}`"" + $parameters += "--area", "`"${area}`"" + if ($assignedTo) { + $parameters += "--assigned-to", "`"${assignedTo}`"" + } + if ($fields) { + $parameters += "--fields" + $parameters += $fields + } + + $workItem = Invoke-AzBoardsCmd "work-item create" $parameters $outputCommand + + if ($parentId) { + $parameters = $ReleaseDevOpsCommonParameters + $parameters += "--id", $workItem.id + $parameters += "--relation-type", "parent" + $parameters += "--target-id", $parentId + + Invoke-AzBoardsCmd "work-item relation add" $parameters $outputCommand | Out-Null + } + + return $workItem +} + +function ResetWorkItemState($workItem, $resetState = $null, $outputCommand = $true) +{ + if (!$resetState -or $resetState -eq "New") { + $resetState = "Next Release Unknown" + } + if ($workItem.fields["System.State"] -ne $resetState) + { + Write-Verbose "Resetting state for [$($workItem.id)] from '$($workItem.fields['System.State'])' to '$resetState'" + return UpdateWorkItem $workItem.id -state $resetState -outputCommand $outputCommand + } + return $workItem +} + +function UpdateWorkItem($id, $fields, $title, $state, $assignedTo, $outputCommand = $true) +{ + $parameters = $ReleaseDevOpsCommonParameters + $parameters += "--id", $id + if ($title) { + $parameters += "--title", "`"${title}`"" + } + if ($state) { + $parameters += "--state", "`"${state}`"" + } + if ($assignedTo) { + $parameters += "--assigned-to", "`"${assignedTo}`"" + } + if ($fields) { + $parameters += "--fields" + $parameters += $fields + } + + return Invoke-AzBoardsCmd "work-item update" $parameters $outputCommand +} + +function UpdatePackageWorkItemReleaseState($id, $state, $releaseType, $outputCommand = $true) +{ + $fields = "`"Custom.ReleaseType=${releaseType}`"" + return UpdateWorkItem -id $id -state $state -fields $fields -outputCommand $outputCommand +} + +function CreateOrUpdatePackageWorkItem($lang, $pkg, $verMajorMinor, $existingItem, $assignedTo = $null, $outputCommand = $true) +{ + if (!$lang -or !$pkg -or !$verMajorMinor) { + Write-Host "Cannot create or update because one of lang, pkg or verMajorMinor aren't set. [$lang|$($pkg.Package)|$verMajorMinor]" + return + } + $pkgName = $pkg.Package + $pkgDisplayName = $pkg.DisplayName + $pkgType = $pkg.Type + $pkgNewLibrary = $pkg.New + $pkgRepoPath = $pkg.RepoPath + $serviceName = $pkg.ServiceName + $title = $lang + " - " + $pkg.DisplayName + " - " + $verMajorMinor + + $fields = @() + $fields += "`"Language=${lang}`"" + $fields += "`"Package=${pkgName}`"" + $fields += "`"PackageDisplayName=${pkgDisplayName}`"" + $fields += "`"PackageType=${pkgType}`"" + $fields += "`"PackageTypeNewLibrary=${pkgNewLibrary}`"" + $fields += "`"PackageVersionMajorMinor=${verMajorMinor}`"" + $fields += "`"ServiceName=${serviceName}`"" + $fields += "`"PackageRepoPath=${pkgRepoPath}`"" + + if ($existingItem) + { + $changedField = $null + + if ($lang -ne $existingItem.fields["Custom.Language"]) { $changedField = "Custom.Language" } + if ($pkgName -ne $existingItem.fields["Custom.Package"]) { $changedField = "Custom.Package" } + if ($verMajorMinor -ne $existingItem.fields["Custom.PackageVersionMajorMinor"]) { $changedField = "Custom.PackageVersionMajorMinor" } + if ($pkgDisplayName -ne $existingItem.fields["Custom.PackageDisplayName"]) { $changedField = "Custom.PackageDisplayName" } + if ($pkgType -ne $existingItem.fields["Custom.PackageType"]) { $changedField = "Custom.PackageType" } + if ($pkgNewLibrary -ne $existingItem.fields["Custom.PackageTypeNewLibrary"]) { $changedField = "Custom.PackageTypeNewLibrary" } + if ($pkgRepoPath -ne $existingItem.fields["Custom.PackageRepoPath"]) { $changedField = "Custom.PackageRepoPath" } + if ($serviceName -ne $existingItem.fields["Custom.ServiceName"]) { $changedField = "Custom.ServiceName" } + if ($title -ne $existingItem.fields["System.Title"]) { $changedField = "System.Title" } + + if ($changedField) { + Write-Host "At least field $changedField ($($existingItem.fields[$field])) changed so updating." + } + + $beforeState = $existingItem.fields["System.State"] + + if ($changedField) { + # Need to set to New to be able to update + $existingItem = UpdateWorkItem -id $existingItem.id -fields $fields -title $title -state "New" -assignedTo $assignedTo -outputCommand $outputCommand + Write-Host "[$($existingItem.id)]$lang - $pkgName($verMajorMinor) - Updated" + } + $existingItem = ResetWorkItemState $existingItem $beforeState -outputCommand $outputCommand + + $newparentItem = FindOrCreatePackageGroupParent $serviceName $pkgDisplayName -outputCommand $outputCommand + UpdateWorkItemParent $existingItem $newParentItem -outputCommand $outputCommand + return $existingItem + } + + $parentItem = FindOrCreatePackageGroupParent $serviceName $pkgDisplayName -outputCommand $outputCommand + $workItem = CreateWorkItem $title "Package" "Release" "Release" $fields $assignedTo $parentItem.id -outputCommand $outputCommand + $workItem = ResetWorkItemState $workItem -outputCommand $outputCommand + Write-Host "[$($workItem.id)]$lang - $pkgName($verMajorMinor) - Created" + return $workItem +} + +function FindOrCreatePackageGroupParent($serviceName, $packageDisplayName, $outputCommand = $true) +{ + $existingItem = FindParentWorkItem $serviceName $packageDisplayName -outputCommand $outputCommand + if ($existingItem) { + $newparentItem = FindOrCreateServiceParent $serviceName -outputCommand $outputCommand + UpdateWorkItemParent $existingItem $newParentItem + return $existingItem + } + + $fields = @() + $fields += "`"PackageDisplayName=${packageDisplayName}`"" + $fields += "`"ServiceName=${serviceName}`"" + $serviceParentItem = FindOrCreateServiceParent $serviceName -outputCommand $outputCommand + $workItem = CreateWorkItem $packageDisplayName "Epic" "Release" "Release" $fields $null $serviceParentItem.id + + $localKey = BuildHashKey $serviceName $packageDisplayName + Write-Host "[$($workItem.id)]$localKey - Created Parent" + $parentWorkItems[$localKey] = $workItem + return $workItem +} + +function FindOrCreateServiceParent($serviceName, $outputCommand = $true) +{ + $serviceParent = FindParentWorkItem $serviceName -outputCommand $outputCommand + if ($serviceParent) { + return $serviceParent + } + + $fields = @() + $fields += "`"PackageDisplayName=`"" + $fields += "`"ServiceName=${serviceName}`"" + $parentId = $null + $workItem = CreateWorkItem $serviceName "Epic" "Release" "Release" $fields $null $parentId -outputCommand $outputCommand + + $localKey = BuildHashKey $serviceName + Write-Host "[$($workItem.id)]$localKey - Created" + $parentWorkItems[$localKey] = $workItem + return $workItem +} + +function ParseVersionSetFromMDField([string]$field) +{ + $MDTableRegex = "\|\s*(?\S*)\s*\|\s*(?\S*)\s*\|\s*(?\S*)\s*\|" + $versionSet = @{} + $tableMatches = [Regex]::Matches($field, $MDTableRegex) + + foreach ($match in $tableMatches) + { + if ($match.Groups["t"].Value -eq "Type" -or $match.Groups["t"].Value -eq "-") { + continue + } + $version = New-Object PSObject -Property @{ + Type = $match.Groups["t"].Value + Version = $match.Groups["v"].Value + Date = $match.Groups["d"].Value + } + if (!$versionSet.ContainsKey($version.Version)) { + $versionSet[$version.Version] = $version + } + } + return $versionSet +} + +function GetTextVersionFields($versionList, $pkgWorkItem) +{ + $betaVersions = $gaVersions = $patchVersions = "" + foreach ($v in $versionList) { + $vstr = "$($v.Version),$($v.Date)" + if ($v.Type -eq "Beta") { + if ($betaVersions.Length + $vstr.Length -lt 255) { + if ($betaVersions.Length -gt 0) { $betaVersions += "|" } + $betaVersions += $vstr + } + } + elseif ($v.Type -eq "GA") { + if ($gaVersions.Length + $vstr.Length -lt 255) { + if ($gaVersions.Length -gt 0) { $gaVersions += "|" } + $gaVersions += $vstr + } + } + elseif ($v.Type -eq "Patch") { + if ($patchVersions.Length + $vstr.Length -lt 255) { + if ($patchVersions.Length -gt 0) { $patchVersions += "|" } + $patchVersions += $vstr + } + } + } + + $fieldUpdates = @() + if ("$($pkgWorkItem.fields["Custom.PackageBetaVersions"])" -ne $betaVersions) + { + $fieldUpdates += @" +{ + "op": "replace", + "path": "/fields/PackageBetaVersions", + "value": "$betaVersions" +} +"@ + } + + if ("$($pkgWorkItem.fields["Custom.PackageGAVersion"])" -ne $gaVersions) + { + $fieldUpdates += @" +{ + "op": "replace", + "path": "/fields/PackageGAVersion", + "value": "$gaVersions" +} +"@ + } + + if ("$($pkgWorkItem.fields["Custom.PackagePatchVersions"])" -ne $patchVersions) + { + $fieldUpdates += @" +{ + "op": "replace", + "path": "/fields/PackagePatchVersions", + "value": "$patchVersions" +} +"@ + } + return ,$fieldUpdates +} + +function GetMDVersionValue($versionlist) +{ + $mdVersions = "" + $mdFormat = "| {0} | {1} | {2} |`n" + $versionlist | ForEach-Object { $mdVersions += ($mdFormat -f $_.Type, $_.Version, $_.Date) } + + $htmlVersions = "" + $htmlFormat = @" + +{0} +{1} +{2} + + +"@ + $versionlist | ForEach-Object { $htmlVersions += ($htmlFormat -f $_.Type, $_.Version, $_.Date) } + + $htmlTemplate = @" +
+ + + + + + + +htmlVersions +
TypeVersionDate
+
+"@ -replace "'", '\"' + + return $htmlTemplate.Replace("mdVersions", $mdVersions).Replace("htmlVersions", "`n$htmlVersions"); +} + +function UpdatePackageVersions($pkgWorkItem, $plannedVersions, $shippedVersions) +{ + # Create the planned and shipped versions, adding the new ones if any + $updatePlanned = $false + $plannedVersionSet = ParseVersionSetFromMDField $pkgWorkItem.fields["Custom.PlannedPackages"] + foreach ($version in $plannedVersions) + { + if (!$plannedVersionSet.ContainsKey($version.Version)) + { + $plannedVersionSet[$version.Version] = $version + $updatePlanned = $true + } + else + { + # Lets check to see if someone wanted to update a date + $existingVersion = $plannedVersionSet[$version.Version] + if ($existingVersion.Date -ne $version.Date) { + $existingVersion.Date = $version.Date + $updatePlanned = $true + } + } + } + + $updateShipped = $false + $shippedVersionSet = ParseVersionSetFromMDField $pkgWorkItem.fields["Custom.ShippedPackages"] + foreach ($version in $shippedVersions) + { + if (!$shippedVersionSet.ContainsKey($version.Version)) + { + $shippedVersionSet[$v.Version] = $version + $updateShipped = $true + } + } + + $versionSet = @{} + foreach ($version in $shippedVersionSet.Keys) + { + if (!$versionSet.ContainsKey($version)) + { + $versionSet[$version] = $shippedVersionSet[$version] + } + } + + foreach ($version in $plannedVersionSet.Keys) + { + if (!$versionSet.ContainsKey($version)) + { + $versionSet[$version] = $plannedVersionSet[$version] + } + else + { + # Looks like we shipped this version so remove it from the planned set + $plannedVersionSet.Remove($version) + $updatePlanned = $true + } + } + + $fieldUpdates = @() + if ($updatePlanned) + { + $plannedPackages = GetMDVersionValue ($plannedVersionSet.Values | Sort-Object Date, Version -Descending) + $fieldUpdates += @" +{ + "op": "replace", + "path": "/fields/Planned Packages", + "value": "$plannedPackages" +} +"@ + } + + if ($updateShipped) + { + $newShippedVersions = $shippedVersionSet.Values | Sort-Object Date, Version -Descending + $shippedPackages = GetMDVersionValue $newShippedVersions + $fieldUpdates += @" +{ + "op": "replace", + "path": "/fields/Shipped Packages", + "value": "$shippedPackages" +} +"@ + + # If we shipped a version after we set "In Release" state then reset the state to "Next Release Unknown" + if ($pkgWorkItem.fields["System.State"] -eq "In Release") + { + $lastShippedDate = [DateTime]$newShippedVersions[0].Date + $markedInReleaseDate = ([DateTime]$pkgWorkItem.fields["Microsoft.VSTS.Common.StateChangeDate"]) + + # We just shipped so lets set the state to "Next Release Unknown" + if ($markedInReleaseDate -le $lastShippedDate) + { + $fieldUpdates += @' +{ + "op": "replace", + "path": "/fields/State", + "value": "Next Release Unknown" +} +'@ + } + } + } + + # Full merged version set + $versionList = $versionSet.Values | Sort-Object Date, Version -Descending + + $versionFieldUpdates = GetTextVersionFields $versionList $pkgWorkItem + if ($versionFieldUpdates.Count -gt 0) + { + $fieldUpdates += $versionFieldUpdates + } + + + # If no version files to update do nothing + if ($fieldUpdates.Count -eq 0) { + return + } + + $versionsForDebug = ($versionList | Foreach-Object { $_.Version }) -join "," + $id = $pkgWorkItem.id + $loggingString = "[$($pkgWorkItem.id)]" + $loggingString += "$($pkgWorkItem.fields['Custom.Language'])" + $loggingString += " - $($pkgWorkItem.fields['Custom.Package'])" + $loggingString += "($($pkgWorkItem.fields['Custom.PackageVersionMajorMinor']))" + $loggingString += " - Updating versions $versionsForDebug" + Write-Host $loggingString + + $body = "[" + ($fieldUpdates -join ',') + "]" + + # Get a temp access token from the logged in az cli user for azure devops resource + $jwt_accessToken = (az account get-access-token --resource "499b84ac-1321-427f-aa17-267ca6975798" --query "accessToken" --output tsv) + $headers = @{ Authorization = "Bearer $jwt_accessToken" } + $response = Invoke-RestMethod -Method PATCH ` + -Uri "https://dev.azure.com/azure-sdk/_apis/wit/workitems/${id}?api-version=6.0" ` + -Headers $headers -Body $body -ContentType "application/json-patch+json" +} \ No newline at end of file diff --git a/eng/common/scripts/SemVer.ps1 b/eng/common/scripts/SemVer.ps1 index 29101690d745a..4e804e765e8d5 100644 --- a/eng/common/scripts/SemVer.ps1 +++ b/eng/common/scripts/SemVer.ps1 @@ -25,6 +25,7 @@ class AzureEngSemanticVersion { [string] $BuildNumber [int] $PrereleaseNumber [bool] $IsPrerelease + [string] $VersionType [string] $RawVersion [bool] $IsSemVerFormat [string] $DefaultPrereleaseLabel @@ -80,6 +81,10 @@ class AzureEngSemanticVersion { $this.PrereleaseLabel = "zzz" $this.PrereleaseNumber = 99999999 $this.IsPrerelease = $false + $this.VersionType = "GA" + if ($this.Patch -ne 0) { + $this.VersionType = "Patch" + } } else { @@ -88,6 +93,7 @@ class AzureEngSemanticVersion { $this.PrereleaseNumber = [int]$matches["prenumber"] $this.PrereleaseNumberSeparator = $matches["prenumsep"] $this.IsPrerelease = $true + $this.VersionType = "Beta" $this.BuildNumberSeparator = $matches["buildnumsep"] $this.BuildNumber = $matches["buildnumber"] diff --git a/eng/common/scripts/Update-DevOps-Release-WorkItem.ps1 b/eng/common/scripts/Update-DevOps-Release-WorkItem.ps1 new file mode 100644 index 0000000000000..52fb9bd342999 --- /dev/null +++ b/eng/common/scripts/Update-DevOps-Release-WorkItem.ps1 @@ -0,0 +1,107 @@ + +[CmdletBinding()] +param( + [Parameter(Mandatory=$true)] + [string]$language, + [Parameter(Mandatory=$true)] + [string]$packageName, + [Parameter(Mandatory=$true)] + [string]$version, + [string]$plannedDate, + [string]$serviceName = $null, + [string]$packageDisplayName = $null, + [string]$packageRepoPath = "NA", + [string]$packageType = "client", + [string]$packageNewLibrary = "true" +) +Set-StrictMode -Version 3 + +if (!(Get-Command az)) { + Write-Host 'You must have the Azure CLI installed: https://aka.ms/azure-cli' + exit 1 +} + +az extension show -n azure-devops > $null +if (!$?){ + Write-Host 'You must have the azure-devops extension run `az extension add --name azure-devops`' + exit 1 +} + +. (Join-Path $PSScriptRoot SemVer.ps1) +. (Join-Path $PSScriptRoot Helpers DevOps-WorkItem-Helpers.ps1) + +$parsedNewVersion = [AzureEngSemanticVersion]::new($version) +$state = "In Release" +$releaseType = $parsedNewVersion.VersionType +$versionMajorMinor = "" + $parsedNewVersion.Major + "." + $parsedNewVersion.Minor + +$packageInfo = [PSCustomObject][ordered]@{ + Package = $packageName + DisplayName = $packageDisplayName + ServiceName = $serviceName + RepoPath = $packageRepoPath + Type = $packageType + New = $packageNewLibrary +}; + +if (!$plannedDate) { + $plannedDate = Get-Date -Format "MM/dd/yyyy" +} + +$plannedVersions = @( + [PSCustomObject][ordered]@{ + Type = $releaseType + Version = $version + Date = $plannedDate + } +) + +$workItem = FindPackageWorkItem -lang $language -packageName $packageName -version $versionMajorMinor -includeClosed $true -outputCommand $false + +if (!$workItem) { + $latestVersionItem = FindLatestPackageWorkItem -lang $language -packageName $packageName -outputCommand $false + $assignedTo = "me" + if ($latestVersionItem) { + Write-Host "Copying data from latest matching [$($latestVersionItem.id)] with version $($latestVersionItem.fields["Custom.PackageVersionMajorMinor"])" + if ($latestVersionItem.fields["System.AssignedTo"]) { + $assignedTo = $latestVersionItem.fields["System.AssignedTo"]["uniqueName"] + } + $packageInfo.DisplayName = $latestVersionItem.fields["Custom.PackageDisplayName"] + $packageInfo.ServiceName = $latestVersionItem.fields["Custom.ServiceName"] + if (!$packageInfo.RepoPath -and $packageInfo.RepoPath -ne "NA" -and $packageInfo.fields["Custom.PackageRepoPath"]) { + $packageInfo.RepoPath = $packageInfo.fields["Custom.PackageRepoPath"] + } + } + + Write-Host "Creating a release work item for a package release with the following properties:" + Write-Host " Lanuage: $language" + Write-Host " Version: $versionMajorMinor" + Write-Host " Package: $packageName" + Write-Host " AssignedTo: $assignedTo" + + if (!$packageInfo.DisplayName) { + Write-Host "We need a package display name to be used in various places and it should be consistent across languages for similar packages." + while (($readInput = Read-Host -Prompt "Input the display name") -eq "") { } + $packageInfo.DisplayName = $readInput + } + Write-Host " PackageDisplayName: $($packageInfo.DisplayName)" + + if (!$packageInfo.ServiceName) { + Write-Host "We need a package service name to be used in various places and it should be consistent across languages for similar packages." + while (($readInput = Read-Host -Prompt "Input the service name") -eq "") { } + $packageInfo.ServiceName = $readInput + } + Write-Host " ServiceName: $($packageInfo.ServiceName)" + Write-Host " PackageType: $packageType" + + $workItem = CreateOrUpdatePackageWorkItem -lang $language -pkg $packageInfo -verMajorMinor $versionMajorMinor -assignedTo $assignedTo -outputCommand $false +} + +if (!$workItem) { + Write-Host "Something failed as we don't have a work-item so exiting." + exit 1 +} +Write-Host "Marking item [$($workItem.id)]$($workItem.fields['System.Title']) as '$state' for '$releaseType'" +$updatedWI = UpdatePackageWorkItemReleaseState -id $workItem.id -state "In Release" -releaseType $releaseType -outputCommand $false +UpdatePackageVersions $workItem -plannedVersions $plannedVersions +Write-Host "https://dev.azure.com/azure-sdk/Release/_workitems/edit/$($updatedWI.id)/"