Public/Complete-PSUADOPullRequest.ps1
function Complete-PSUADOPullRequest { <# .SYNOPSIS Completes (merges) a pull request in Azure DevOps using REST API. .DESCRIPTION This function completes a pull request in Azure DevOps by its ID. It supports different merge strategies including merge, squash, and rebase. You can specify the repository details or use auto-detection from git remote. .PARAMETER Organization (Optional) The Azure DevOps organization name under which the project resides. Default value is auto-detected from git remote origin URL or $env:ORGANIZATION. .PARAMETER Project (Optional) The Azure DevOps project name containing the repository. Default value is auto-detected from git remote origin URL. .PARAMETER Repository (Optional) The repository name containing the pull request. Default value is auto-detected from git remote origin URL. .PARAMETER PullRequestId (Mandatory) The ID of the pull request to complete. .PARAMETER MergeStrategy (Optional) The merge strategy to use: 'merge', 'squash', or 'rebase'. Default value is 'merge'. .PARAMETER DeleteSourceBranch (Optional) Switch parameter to delete the source branch after completion. .PARAMETER CompletionOptions (Optional) Additional completion options as a hashtable. .PARAMETER PAT (Optional) Personal Access Token for Azure DevOps authentication. Default value is $env:PAT. Set using: Set-PSUUserEnvironmentVariable -Name "PAT" -Value "value_of_PAT" .EXAMPLE Complete-PSUADOPullRequest -PullRequestId 123 Completes pull request with ID 123 using auto-detected organization, project, and repository. .EXAMPLE Complete-PSUADOPullRequest -Organization "myorg" -Project "myproject" -Repository "myrepo" -PullRequestId 123 -MergeStrategy "squash" Completes pull request with ID 123 using squash merge strategy. .EXAMPLE Complete-PSUADOPullRequest -PullRequestId 123 -DeleteSourceBranch Completes pull request with ID 123 and deletes the source branch. .OUTPUTS [PSCustomObject] .NOTES Author: Lakshmanachari Panuganti Date: 19th August 2025 Requires: Azure DevOps Personal Access Token with appropriate permissions .LINK https://github.com/lakshmanachari-panuganti/OMG.PSUtilities/tree/main/OMG.PSUtilities.AzureDevOps https://www.linkedin.com/in/lakshmanachari-panuganti/ https://www.powershellgallery.com/packages/OMG.PSUtilities.AzureDevOps https://learn.microsoft.com/en-us/rest/api/azure/devops/git/pull-requests/update #> [CmdletBinding()] [Diagnostics.CodeAnalysis.SuppressMessageAttribute( 'PSAvoidUsingWriteHost', '', Justification = 'This is intended for this function to display formatted output to the user on the console' )] param ( [Parameter()] [ValidateNotNullOrEmpty()] [string]$Organization = $(if ($env:ORGANIZATION) { $env:ORGANIZATION } else { git remote get-url origin 2>$null | ForEach-Object { if ($_ -match 'dev\.azure\.com/([^/]+)/') { $matches[1] } } }), [Parameter()] [ValidateNotNullOrEmpty()] [string]$Project = $(git remote get-url origin 2>$null | ForEach-Object { if ($_ -match 'dev\.azure\.com/[^/]+/([^/]+)/_git/') { $matches[1] } }), [Parameter()] [ValidateNotNullOrEmpty()] [string]$Repository = $(git remote get-url origin 2>$null | ForEach-Object { if ($_ -match '/_git/([^/]+?)(?:\.git)?/?$') { $matches[1] } }), [Parameter(Mandatory)] [ValidateNotNullOrEmpty()] [int]$PullRequestId, [Parameter()] [ValidateSet('merge', 'squash', 'rebase', 'rebaseMerge')] [string]$MergeStrategy = 'merge', [Parameter()] [switch]$DeleteSourceBranch, [Parameter()] [hashtable]$CompletionOptions, [Parameter()] [ValidateNotNullOrEmpty()] [string]$PAT = $env:PAT ) process { try { # Validate required parameters that have auto-detection if (-not $Organization) { throw "Organization parameter is required. Set it using: Set-PSUUserEnvironmentVariable -Name 'ORGANIZATION' -Value 'your-org' or ensure you're in a git repository with Azure DevOps remote." } if (-not $Project) { throw "Project parameter is required. Either specify it explicitly or ensure you're in a git repository with Azure DevOps remote URL." } if (-not $Repository) { throw "Repository parameter is required. Either specify it explicitly or ensure you're in a git repository with Azure DevOps remote URL." } # Get repository ID $repos = Get-PSUADORepositories -Project $Project -Organization $Organization -PAT $PAT $matchedRepo = $repos | Where-Object { $_.Name -eq $Repository } if (-not $matchedRepo) { throw "Repository '$Repository' not found in project '$Project'." } $repositoryId = $matchedRepo.Id # Compose authentication header $headers = Get-PSUAdoAuthHeader -PAT $PAT # First, get the current PR details $getPrUri = "https://dev.azure.com/$Organization/$Project/_apis/git/repositories/$repositoryId/pullrequests/$PullRequestId" + "?api-version=7.0" Write-Verbose "Getting pull request details from: $getPrUri" $currentPr = Invoke-RestMethod -Method Get -Uri $getPrUri -Headers $headers -ErrorAction Stop # Prepare completion options $completionOptions = @{} if ($MergeStrategy -eq 'merge') { $completionOptions.mergeStrategy = 'noFastForward' } elseif ($MergeStrategy -eq 'squash') { $completionOptions.mergeStrategy = 'squash' } elseif ($MergeStrategy -eq 'rebase') { $completionOptions.mergeStrategy = 'rebase' } elseif ($MergeStrategy -eq 'rebaseMerge') { $completionOptions.mergeStrategy = 'rebaseMerge' } if ($DeleteSourceBranch) { $completionOptions.deleteSourceBranch = $true } # Add any additional completion options if ($CompletionOptions) { foreach ($key in $CompletionOptions.Keys) { $completionOptions[$key] = $CompletionOptions[$key] } } # Prepare the update body $body = @{ status = "completed" lastMergeSourceCommit = @{ commitId = $currentPr.lastMergeSourceCommit.commitId } completionOptions = $completionOptions } | ConvertTo-Json -Depth 10 $escapedProject = [uri]::EscapeDataString($Project) $uri = "https://dev.azure.com/$Organization/$escapedProject/_apis/git/repositories/$repositoryId/pullrequests/$PullRequestId" + "?api-version=7.0" Write-Verbose "Completing pull request ID: $PullRequestId in project: $Project" Write-Verbose "Repository: $Repository ($repositoryId)" Write-Verbose "Merge strategy: $MergeStrategy" Write-Verbose "API URI: $uri" $response = Invoke-RestMethod -Method Patch -Uri $uri -Headers $headers -Body $body -ContentType "application/json" -ErrorAction Stop $WebUrl = "https://dev.azure.com/$Organization/$escapedProject/_git/$Repository/pullrequest/$PullRequestId" Write-Host "Pull Request ID $PullRequestId completed successfully!" -ForegroundColor Green Write-Host "PR URL: $WebUrl" -ForegroundColor Cyan Write-Host "Merge strategy: $MergeStrategy" -ForegroundColor Yellow if ($DeleteSourceBranch) { Write-Host "Source branch will be deleted." -ForegroundColor Green } # Return structured result [PSCustomObject]@{ Id = $response.pullRequestId Status = $response.status Title = $response.title MergeStrategy = $MergeStrategy SourceBranch = $response.sourceRefName TargetBranch = $response.targetRefName CompletedBy = $response.closedBy.displayName CompletionDate = $response.closedDate MergeId = $response.mergeId DeletedSourceBranch = $DeleteSourceBranch.IsPresent Organization = $Organization Project = $Project Repository = $Repository WebUrl = $WebUrl PSTypeName = 'PSU.ADO.PullRequestCompletion' } } catch { $PSCmdlet.ThrowTerminatingError($_) } } } |