Public/Api/Git/New-PullRequest.ps1
function New-PullRequest { <# .SYNOPSIS Creates a new pull request for given repo branches, if they differ. Returns a pull request object if successful, .PARAMETER CollectionUri Url for project collection on Azure DevOps server instance. If not specified, $global:AzureDevOpsApi_CollectionUri (set by Set-AzureDevopsVariables) is used. .PARAMETER Project Project to get. Can be passed as a name, identifier, full project URI, or object with any one these properties. If not specified, $global:AzureDevOpsApi_Project (set by Set-AzureDevopsVariables) is used. .PARAMETER Repository Name of the git repository to search. No wildcards allowed .PARAMETER SourceBranch Name of the base branch .PARAMETER TargetBranch Name of the target branch .PARAMETER AutoComplete Whether the pull request should be autocompleted .NOTES https://learn.microsoft.com/en-us/rest/api/azure/devops/git/diffs/get?view=azure-devops-rest-7.1&tabs=HTTP #> [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute( 'PSUseShouldProcessForStateChangingFunctions', '', Justification = 'Does not change state, generates a new object.' )] [CmdletBinding()] param( [AllowNull()] [AllowEmptyString()] $Project, [AllowNull()] [AllowEmptyString()] $CollectionUri, [Parameter(Mandatory)] $Repository, [Parameter(Mandatory)] [Alias('Source', 'BaseBranch')] $SourceBranch, [Parameter(Mandatory)] [Alias('Target')] $TargetBranch, [switch] $AutoComplete ) process { # Get connection to project $connection = Get-ApiProjectConnection ` -CollectionUri $CollectionUri ` -Project $Project # Get the commit diffs $behindCount = ( Get-CommitDiffsCount ` -CollectionUri $connection.CollectionUri ` -Project $connection.ProjectBaseUri ` -Repository $Repository ` -SourceBranch $SourceBranch ` -TargetBranch $TargetBranch ` ).behindCount # If there are no commits, there is nothing to do if (!$behindCount -or ($behindCount -eq 0)) { Show-Host -Object ( "Repository $($Repository): no changes between target branch ($($TargetBranch))" ` + " and source branch ($($SourceBranch))." ) return } # Create the pull request # Make the final uri $uri = Join-Uri ` -Base $connection.ProjectBaseUri ` -Relative '_apis/git/repositories', $Repository, 'pullrequests' ` -NoTrailingSlash # Create the payload $body = @{ title = "$($SourceBranch)->$($TargetBranch)" description = "$($SourceBranch)->$($TargetBranch)" sourceRefName = "refs/heads/$($SourceBranch)" targetRefName = "refs/heads/$($TargetBranch)" } # Send the request try { $response = Invoke-Api ` -ApiCredential $connection.ApiCredential ` -ApiVersion $connection.ApiVersion ` -Uri $uri ` -Body ($body | ConvertTo-JsonCustom) } catch { # pending merge request for the same source and target branch if ($_.Exception.StatusCode -eq 409) { # https://dev-tfs/tfs/internal_projects/DITEC_TestovaciaSablona_CMMI-v4/_git/Heracles/pullrequests?_a=active $portalUri = Join-Uri ` -Base $connection.ProjectBaseUri ` -Relative '_git', $Repository, 'pullrequests' ` -Parameters @{ _a = 'active' } ` -NoTrailingSlash throw ( "Unable to create pull request in repository $($Repository)." ` + " An active pull request for the source ($($SourceBranch)) and target" ` + " ($($TargetBranch)) already exists. Resolve pending pull requests and try again." ` + " You can view pending pull requests here:`n$($portalUri)" ) } throw } # If AutoComplete is not requested, just return the pull request if (!$AutoComplete.IsPresent -or ($AutoComplete -ne $true)) { return $response } # If AutoComplete is requested, create the AutoComplete request $pullRequestID = $response.pullRequestId # Make the pullrequest uri $uri = Join-Uri ` -Base $connection.ProjectBaseUri ` -Relative '_apis/git/repositories', $Repository, 'pullrequests', $pullRequestID ` -NoTrailingSlash # Create the payload $body = @{ autoCompleteSetBy = @{ id = ( Get-CurrentUser ` -CollectionUri $connection.CollectionUri ` -ApiCredential $connection.ApiCredential ` ).id } completionOptions = @{ deleteSourceBranch = "false" mergeCommitMessage = "$($SourceBranch)->$($TargetBranch)" squashMerge = "false" } } # Modify the pull request by # Sending the autocomplete patch request $response = Invoke-Api ` -ApiCredential $connection.ApiCredential ` -ApiVersion $connection.ApiVersion ` -Uri $uri ` -Body ($body | ConvertTo-JsonCustom) ` -Method "PATCH" # Wait for the pull request's merge to be processed $SleepTime = 0.25 $SleepCount = 0 $TotalSleepTime = 0 while ($response.mergeStatus -eq 'queued') { Start-Sleep -Seconds $SleepTime $TotalSleepTime += $SleepTime $SleepCount += 1 $SleepTime *= 2 # If we've waited too long, give up if ($SleepCount -gt 10) { $formattedTime = "$('{0:mm} minutes and {0:ss} seconds' -f [timespan]::FromSeconds($TotalSleepTime))" Write-Warning ( "Repository $($Repository): Merge request '$($response.title)' still queued after $($formattedTime)." ) break; } # Get the pull request again $response = Invoke-Api ` -ApiCredential $connection.ApiCredential ` -ApiVersion $connection.ApiVersion ` -Uri $uri } # If the merge failed, throw an error if ($response.mergeStatus -eq 'conflicts') { # "https://dev-tfs/tfs/internal_projects/DITEC_TestovaciaSablona_CMMI-v4/_git/Heracles/pullrequest/4219" $portalUri = Join-Uri ` -Base $connection.ProjectBaseUri ` -Relative '_git', $Repository, 'pullrequests', $response.pullRequestId ` -NoTrailingSlash Write-Warning ( "Merge conflict in repository $($Repository) for pull request '$($response.title)'." ` + " Please resolve conflicts and try again." ` + " You can view the pull request here:`n$($portalUri)" ) } # Return the pull request $response } } |