From 8d4724017f22ebab1903e4e78354652e47d6f804 Mon Sep 17 00:00:00 2001 From: Praveen Kuttappan Date: Wed, 6 Aug 2025 12:09:21 -0400 Subject: [PATCH 1/5] Add a new script to mark package as released from release pipeline --- .../Helpers/DevOps-WorkItem-Helpers.ps1 | 63 ++++++++ .../scripts/Mark-ReleasePlanCompletion.ps1 | 139 ++++++++++++++++++ 2 files changed, 202 insertions(+) create mode 100644 eng/common/scripts/Mark-ReleasePlanCompletion.ps1 diff --git a/eng/common/scripts/Helpers/DevOps-WorkItem-Helpers.ps1 b/eng/common/scripts/Helpers/DevOps-WorkItem-Helpers.ps1 index 9a46e2c126..a1aa40eb44 100644 --- a/eng/common/scripts/Helpers/DevOps-WorkItem-Helpers.ps1 +++ b/eng/common/scripts/Helpers/DevOps-WorkItem-Helpers.ps1 @@ -1015,3 +1015,66 @@ function UpdateValidationStatus($pkgvalidationDetails, $BuildDefinition, $Pipeli Write-Host "[$($workItem.id)]$LanguageDisplayName - $pkgName($versionMajorMinor) - Updated" return $true } + + +function Get-LanguageDevOpsName +{ + param([string]$LanguageShort) + + switch ($LanguageShort.ToLower()) + { + "net" { return "Dotnet" } + "js" { return "JavaScript" } + "java" { return "Java" } + "go" { return "Go" } + "python" { return "Python" } + "rust" { return "Rust" } + "cpp" { return "Cpp" } + "c" { return "C" } + default { return $null } + } +} + +function Get-ReleasePlanForPullRequest($prLink) +{ + $devopsFieldLanguage = Get-LanguageDevOpsName -LanguageShort $LanguageShort + if (!$devopsFieldLanguage) + { + Write-Host "Unsupported language to check release plans, language [$LanguageShort]" + return $null + } + + $prFieldName = "SDKPullRequestFor$($devopsFieldLanguage)" + $releaseStatusField = "ReleaseStatusFor$($devopsFieldLanguage)" + $fields = @() + $fields += "System.ID" + $fields += "System.State" + $fields += "System.AssignedTo" + $fields += "System.Parent" + $fields += "System.Tags" + + $fieldList = ($fields | ForEach-Object { "[$_]"}) -join ", " + $query = "SELECT ${fieldList} FROM WorkItems WHERE [Work Item Type] = 'Release Plan' AND [$prFieldName] = '${prLink}'" + $query += " AND [$releaseStatusField] <> 'Released'" + $query += " AND [System.State] NOT IN ('Finished', 'Abandoned')" + $workItems = Invoke-Query $fields $query + return $workItems +} + +function Update-ReleaseStatusInReleasePlan($releasePlanWorkItemId, $status, $version) +{ + $devopsFieldLanguage = Get-LanguageDevOpsName -LanguageShort $LanguageShort + if (!$devopsFieldLanguage) + { + Write-Host "Unsupported language to check release plans, language [$LanguageShort]" + return $null + } + + $fields = @() + $fields += "`"ReleaseStatusFor$($devopsFieldLanguage)=$status`"" + $fields += "`"ReleasedVersionFor$($devopsFieldLanguage)=$version`"" + + Write-Host "Updating Release Plan [$releasePlanWorkItemId] with status [$status] for language [$LanguageShort]." + $workItem = UpdateWorkItem -id $releasePlanWorkItemId -fields $fields + Write-Host "Updated release status for [$LanguageShort] in Release Plan [$releasePlanWorkItemId]" +} \ No newline at end of file diff --git a/eng/common/scripts/Mark-ReleasePlanCompletion.ps1 b/eng/common/scripts/Mark-ReleasePlanCompletion.ps1 new file mode 100644 index 0000000000..cd85ce1cfa --- /dev/null +++ b/eng/common/scripts/Mark-ReleasePlanCompletion.ps1 @@ -0,0 +1,139 @@ +param( + [Parameter(Mandatory = $true)] + [string]$Path, + + [Parameter(Mandatory = $true)] + [string]$PackageName, + + [Parameter(Mandatory = $true)] + [string]$PackageVersion, + + [Parameter(Mandatory = $true)] + [string]$GitHubRepoUrl, + + [Parameter(Mandatory = $false)] + [int]$MaxCount = 5 +) + +<# +.SYNOPSIS + Marks release plan completion by identifying pull requests that changed files in a given path. + +.DESCRIPTION + This script helps mark release plan completion by searching through git commit history to find + "Merge pull request" commits that affected files in the specified path. It extracts pull request + numbers and generates GitHub links to track what changes were included in the release. + +.PARAMETER Path + The file or directory path to check for changes (required) + +.PARAMETER PackageName + The package name being released (required) + +.PARAMETER PackageVersion + The package version being released (required) + +.PARAMETER GitHubRepoUrl + The GitHub repository URL. + +.PARAMETER MaxCount + Maximum number of recent pull request merges to check (default: 5) + +#> + +Set-StrictMode -Version 3 +. (Join-Path $PSScriptRoot common.ps1) +. (Join-Path $PSScriptRoot Helpers DevOps-WorkItem-Helpers.ps1) + +$AzureDevOpsOrganization = "azure-sdk" +$AzureDevOpsProject = "Release" + + +function Get-MergedPullRequests() +{ + Write-Host "Searching for merged pull requests..." + $gitArgs = @( + "log" + "--grep=Merge pull request" + "--oneline" + "--max-count=$MaxCount" + "--" + $Path + ) + + Write-Host "Executing: git $($gitArgs -join ' ')" + $commits = & git @gitArgs 2>$null + if ($LASTEXITCODE -ne 0) { + Write-Error "Git command failed with exit code $LASTEXITCODE" + exit 1 + } + + if (-not $commits) { + Write-Host "No merge pull request commits found for the specified path." + exit 0 + } + + $pullRequests = @() + foreach ($commit in $commits) { + # Parse commit hash and message + if ($commit -match "Merge pull request #(\d+)") { + $prNumber = $matches[1] + if ($prNumber) + { + $prLink = "$GitHubRepoUrl/pull/$prNumber" + $pullRequests += $prLink + Write-Host "Merged pull request: $prLink" + } + } + break + } + return $pullRequests +} + + +# Verify the path exists +if (-not (Test-Path $Path)) { + Write-Warning "Path '$Path' does not exist in the current repository" +} + +# Main execution +Write-Host "Finding most recent merged pull requests for path: $Path" + +$pullRequests = Get-MergedPullRequests +if (!$pullRequests) +{ + Write-Host "No valid pull request commits found." + exit 0 +} +# Check Azure DevOps Release Plan work items if LanguageShort is available +Write-Host "Checking Azure DevOps Release Plan work items for language: $LanguageShort" +$workItems = @() +foreach ($prLink in $pullRequests) +{ + Write-Host "Searching for work items related to pull request: $prLink" + $workItems += Get-ReleasePlanForPullRequest $prLink + if ($LASTEXITCODE -ne 0) + { + Write-Error "Failed to search Azure DevOps work items." -ForegroundColor Red + exit 1 + } +} + +if(!$workItems) +{ + Write-Host "No active release plans found for the merged pull requests." -ForegroundColor Yellow + exit 0 +} + +# Update release status +Write-Host "Marking release completion for package, name: $PackageName version: $PackageVersion" +foreach ($workItem in $workItems) +{ + Update-ReleaseStatusInReleasePlan $workItem.id "Released" $PackageVersion + if ($LASTEXITCODE -ne 0) + { + Write-Error "Failed to update release status for work item ID $($workItem.id)." -ForegroundColor Red + exit 1 + } +} +Write-Host "Successfully marked release completion for package, name: $PackageName version: $PackageVersion." From 1fac009de9aee23f781e2d95e53cdad96f354890 Mon Sep 17 00:00:00 2001 From: Praven Kuttappan <55455725+praveenkuttappan@users.noreply.github.com> Date: Wed, 6 Aug 2025 12:55:31 -0400 Subject: [PATCH 2/5] Update eng/common/scripts/Mark-ReleasePlanCompletion.ps1 Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- eng/common/scripts/Mark-ReleasePlanCompletion.ps1 | 1 - 1 file changed, 1 deletion(-) diff --git a/eng/common/scripts/Mark-ReleasePlanCompletion.ps1 b/eng/common/scripts/Mark-ReleasePlanCompletion.ps1 index cd85ce1cfa..8d20524603 100644 --- a/eng/common/scripts/Mark-ReleasePlanCompletion.ps1 +++ b/eng/common/scripts/Mark-ReleasePlanCompletion.ps1 @@ -85,7 +85,6 @@ function Get-MergedPullRequests() Write-Host "Merged pull request: $prLink" } } - break } return $pullRequests } From 3884f8dbb11f04a17f49e61f2603c22fd3b51c56 Mon Sep 17 00:00:00 2001 From: Praveen Kuttappan Date: Wed, 6 Aug 2025 19:10:49 -0400 Subject: [PATCH 3/5] Fix spacing in the script --- .../Helpers/DevOps-WorkItem-Helpers.ps1 | 70 +++++++++---------- 1 file changed, 34 insertions(+), 36 deletions(-) diff --git a/eng/common/scripts/Helpers/DevOps-WorkItem-Helpers.ps1 b/eng/common/scripts/Helpers/DevOps-WorkItem-Helpers.ps1 index a1aa40eb44..68cbd96be9 100644 --- a/eng/common/scripts/Helpers/DevOps-WorkItem-Helpers.ps1 +++ b/eng/common/scripts/Helpers/DevOps-WorkItem-Helpers.ps1 @@ -1017,10 +1017,8 @@ function UpdateValidationStatus($pkgvalidationDetails, $BuildDefinition, $Pipeli } -function Get-LanguageDevOpsName +function Get-LanguageDevOpsName($LanguageShort) { - param([string]$LanguageShort) - switch ($LanguageShort.ToLower()) { "net" { return "Dotnet" } @@ -1037,44 +1035,44 @@ function Get-LanguageDevOpsName function Get-ReleasePlanForPullRequest($prLink) { - $devopsFieldLanguage = Get-LanguageDevOpsName -LanguageShort $LanguageShort - if (!$devopsFieldLanguage) - { - Write-Host "Unsupported language to check release plans, language [$LanguageShort]" - return $null - } - - $prFieldName = "SDKPullRequestFor$($devopsFieldLanguage)" - $releaseStatusField = "ReleaseStatusFor$($devopsFieldLanguage)" - $fields = @() - $fields += "System.ID" - $fields += "System.State" - $fields += "System.AssignedTo" - $fields += "System.Parent" - $fields += "System.Tags" + $devopsFieldLanguage = Get-LanguageDevOpsName -LanguageShort $LanguageShort + if (!$devopsFieldLanguage) + { + Write-Host "Unsupported language to check release plans, language [$LanguageShort]" + return $null + } - $fieldList = ($fields | ForEach-Object { "[$_]"}) -join ", " - $query = "SELECT ${fieldList} FROM WorkItems WHERE [Work Item Type] = 'Release Plan' AND [$prFieldName] = '${prLink}'" - $query += " AND [$releaseStatusField] <> 'Released'" - $query += " AND [System.State] NOT IN ('Finished', 'Abandoned')" - $workItems = Invoke-Query $fields $query - return $workItems + $prFieldName = "SDKPullRequestFor$($devopsFieldLanguage)" + $releaseStatusField = "ReleaseStatusFor$($devopsFieldLanguage)" + $fields = @() + $fields += "System.ID" + $fields += "System.State" + $fields += "System.AssignedTo" + $fields += "System.Parent" + $fields += "System.Tags" + + $fieldList = ($fields | ForEach-Object { "[$_]"}) -join ", " + $query = "SELECT ${fieldList} FROM WorkItems WHERE [Work Item Type] = 'Release Plan' AND [$prFieldName] = '${prLink}'" + $query += " AND [$releaseStatusField] <> 'Released'" + $query += " AND [System.State] NOT IN ('Finished', 'Abandoned')" + $workItems = Invoke-Query $fields $query + return $workItems } function Update-ReleaseStatusInReleasePlan($releasePlanWorkItemId, $status, $version) { - $devopsFieldLanguage = Get-LanguageDevOpsName -LanguageShort $LanguageShort - if (!$devopsFieldLanguage) - { - Write-Host "Unsupported language to check release plans, language [$LanguageShort]" - return $null - } + $devopsFieldLanguage = Get-LanguageDevOpsName -LanguageShort $LanguageShort + if (!$devopsFieldLanguage) + { + Write-Host "Unsupported language to check release plans, language [$LanguageShort]" + return $null + } - $fields = @() - $fields += "`"ReleaseStatusFor$($devopsFieldLanguage)=$status`"" - $fields += "`"ReleasedVersionFor$($devopsFieldLanguage)=$version`"" + $fields = @() + $fields += "`"ReleaseStatusFor$($devopsFieldLanguage)=$status`"" + $fields += "`"ReleasedVersionFor$($devopsFieldLanguage)=$version`"" - Write-Host "Updating Release Plan [$releasePlanWorkItemId] with status [$status] for language [$LanguageShort]." - $workItem = UpdateWorkItem -id $releasePlanWorkItemId -fields $fields - Write-Host "Updated release status for [$LanguageShort] in Release Plan [$releasePlanWorkItemId]" + Write-Host "Updating Release Plan [$releasePlanWorkItemId] with status [$status] for language [$LanguageShort]." + $workItem = UpdateWorkItem -id $releasePlanWorkItemId -fields $fields + Write-Host "Updated release status for [$LanguageShort] in Release Plan [$releasePlanWorkItemId]" } \ No newline at end of file From da5d9f3327bdc04fe86b8067f77afc3ee1cf7710 Mon Sep 17 00:00:00 2001 From: Praveen Kuttappan Date: Sat, 9 Aug 2025 11:47:21 -0400 Subject: [PATCH 4/5] Update script to check active release plans using package name and pr merge status --- .../Helpers/DevOps-WorkItem-Helpers.ps1 | 15 +-- .../scripts/Mark-ReleasePlanCompletion.ps1 | 109 +++--------------- 2 files changed, 19 insertions(+), 105 deletions(-) diff --git a/eng/common/scripts/Helpers/DevOps-WorkItem-Helpers.ps1 b/eng/common/scripts/Helpers/DevOps-WorkItem-Helpers.ps1 index 68cbd96be9..cd91bbbfe9 100644 --- a/eng/common/scripts/Helpers/DevOps-WorkItem-Helpers.ps1 +++ b/eng/common/scripts/Helpers/DevOps-WorkItem-Helpers.ps1 @@ -1026,14 +1026,11 @@ function Get-LanguageDevOpsName($LanguageShort) "java" { return "Java" } "go" { return "Go" } "python" { return "Python" } - "rust" { return "Rust" } - "cpp" { return "Cpp" } - "c" { return "C" } default { return $null } } } -function Get-ReleasePlanForPullRequest($prLink) +function Get-ReleasePlanForPackage($packageName) { $devopsFieldLanguage = Get-LanguageDevOpsName -LanguageShort $LanguageShort if (!$devopsFieldLanguage) @@ -1042,8 +1039,8 @@ function Get-ReleasePlanForPullRequest($prLink) return $null } - $prFieldName = "SDKPullRequestFor$($devopsFieldLanguage)" - $releaseStatusField = "ReleaseStatusFor$($devopsFieldLanguage)" + $prStatusFieldName = "SDKPullRequestStatusFor$($devopsFieldLanguage)" + $packageNameFieldName = "$($devopsFieldLanguage) Package Name" $fields = @() $fields += "System.ID" $fields += "System.State" @@ -1052,9 +1049,9 @@ function Get-ReleasePlanForPullRequest($prLink) $fields += "System.Tags" $fieldList = ($fields | ForEach-Object { "[$_]"}) -join ", " - $query = "SELECT ${fieldList} FROM WorkItems WHERE [Work Item Type] = 'Release Plan' AND [$prFieldName] = '${prLink}'" - $query += " AND [$releaseStatusField] <> 'Released'" - $query += " AND [System.State] NOT IN ('Finished', 'Abandoned')" + $query = "SELECT ${fieldList} FROM WorkItems WHERE [Work Item Type] = 'Release Plan' AND [${packageNameFieldName}] = '${packageName}'" + $query += " AND [${prStatusFieldName}] = 'merged'" + $query += " AND [System.State] IN ('In Progress') ORDER BY [System.CreatedDate]" $workItems = Invoke-Query $fields $query return $workItems } diff --git a/eng/common/scripts/Mark-ReleasePlanCompletion.ps1 b/eng/common/scripts/Mark-ReleasePlanCompletion.ps1 index 8d20524603..4c0b888da4 100644 --- a/eng/common/scripts/Mark-ReleasePlanCompletion.ps1 +++ b/eng/common/scripts/Mark-ReleasePlanCompletion.ps1 @@ -1,16 +1,10 @@ -param( - [Parameter(Mandatory = $true)] - [string]$Path, - +param( [Parameter(Mandatory = $true)] [string]$PackageName, [Parameter(Mandatory = $true)] [string]$PackageVersion, - [Parameter(Mandatory = $true)] - [string]$GitHubRepoUrl, - [Parameter(Mandatory = $false)] [int]$MaxCount = 5 ) @@ -20,12 +14,7 @@ param( Marks release plan completion by identifying pull requests that changed files in a given path. .DESCRIPTION - This script helps mark release plan completion by searching through git commit history to find - "Merge pull request" commits that affected files in the specified path. It extracts pull request - numbers and generates GitHub links to track what changes were included in the release. - -.PARAMETER Path - The file or directory path to check for changes (required) + This script helps to mark release plan completion by finding the active release plans for a package name .PARAMETER PackageName The package name being released (required) @@ -33,9 +22,6 @@ param( .PARAMETER PackageVersion The package version being released (required) -.PARAMETER GitHubRepoUrl - The GitHub repository URL. - .PARAMETER MaxCount Maximum number of recent pull request merges to check (default: 5) @@ -47,92 +33,23 @@ Set-StrictMode -Version 3 $AzureDevOpsOrganization = "azure-sdk" $AzureDevOpsProject = "Release" - - -function Get-MergedPullRequests() -{ - Write-Host "Searching for merged pull requests..." - $gitArgs = @( - "log" - "--grep=Merge pull request" - "--oneline" - "--max-count=$MaxCount" - "--" - $Path - ) - - Write-Host "Executing: git $($gitArgs -join ' ')" - $commits = & git @gitArgs 2>$null - if ($LASTEXITCODE -ne 0) { - Write-Error "Git command failed with exit code $LASTEXITCODE" - exit 1 - } - - if (-not $commits) { - Write-Host "No merge pull request commits found for the specified path." - exit 0 - } - - $pullRequests = @() - foreach ($commit in $commits) { - # Parse commit hash and message - if ($commit -match "Merge pull request #(\d+)") { - $prNumber = $matches[1] - if ($prNumber) - { - $prLink = "$GitHubRepoUrl/pull/$prNumber" - $pullRequests += $prLink - Write-Host "Merged pull request: $prLink" - } - } - } - return $pullRequests -} - - -# Verify the path exists -if (-not (Test-Path $Path)) { - Write-Warning "Path '$Path' does not exist in the current repository" -} - -# Main execution -Write-Host "Finding most recent merged pull requests for path: $Path" - -$pullRequests = Get-MergedPullRequests -if (!$pullRequests) -{ - Write-Host "No valid pull request commits found." - exit 0 -} # Check Azure DevOps Release Plan work items if LanguageShort is available -Write-Host "Checking Azure DevOps Release Plan work items for language: $LanguageShort" -$workItems = @() -foreach ($prLink in $pullRequests) -{ - Write-Host "Searching for work items related to pull request: $prLink" - $workItems += Get-ReleasePlanForPullRequest $prLink - if ($LASTEXITCODE -ne 0) - { - Write-Error "Failed to search Azure DevOps work items." -ForegroundColor Red - exit 1 - } -} - +Write-Host "Checking active release plan work items for package: $PackageName" +$workItems = Get-ReleasePlanForPackage $PackageName if(!$workItems) { - Write-Host "No active release plans found for the merged pull requests." -ForegroundColor Yellow + Write-Host "No active release plans found for package name: $PackageName." exit 0 } -# Update release status -Write-Host "Marking release completion for package, name: $PackageName version: $PackageVersion" -foreach ($workItem in $workItems) +$activeReleasePlan = $workItems +if($workItems.Count -gt 1 -and ($workItems -is [System.Array])) { - Update-ReleaseStatusInReleasePlan $workItem.id "Released" $PackageVersion - if ($LASTEXITCODE -ne 0) - { - Write-Error "Failed to update release status for work item ID $($workItem.id)." -ForegroundColor Red - exit 1 - } + $activeReleasePlan = $workItems[0] + Write-Warning "Multiple active release plans found for package name: $PackageName. Using the first release plan to update release status." } +# Update release status +Write-Host "Release plan work item ID: $($activeReleasePlan["id"])" +Write-Host "Marking release completion for package, name: $PackageName version: $PackageVersion" +Update-ReleaseStatusInReleasePlan $activeReleasePlan.id "Released" $PackageVersion Write-Host "Successfully marked release completion for package, name: $PackageName version: $PackageVersion." From 1c774c916b8bf216a745aebd8980173c2250968b Mon Sep 17 00:00:00 2001 From: Praveen Kuttappan Date: Mon, 18 Aug 2025 10:50:07 -0400 Subject: [PATCH 5/5] Log work item Ids if multiple works items are found for release plan --- eng/common/scripts/Mark-ReleasePlanCompletion.ps1 | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/eng/common/scripts/Mark-ReleasePlanCompletion.ps1 b/eng/common/scripts/Mark-ReleasePlanCompletion.ps1 index 4c0b888da4..c8998dbc12 100644 --- a/eng/common/scripts/Mark-ReleasePlanCompletion.ps1 +++ b/eng/common/scripts/Mark-ReleasePlanCompletion.ps1 @@ -44,9 +44,10 @@ if(!$workItems) $activeReleasePlan = $workItems if($workItems.Count -gt 1 -and ($workItems -is [System.Array])) -{ +{ + $concatenatedIds = ($workItems | Select-Object -ExpandProperty id) -join ',' + Write-Host "Multiple release plans found for package name: $PackageName with work item IDs: $concatenatedIds. Using the first release plan to update release status." $activeReleasePlan = $workItems[0] - Write-Warning "Multiple active release plans found for package name: $PackageName. Using the first release plan to update release status." } # Update release status Write-Host "Release plan work item ID: $($activeReleasePlan["id"])"