Add support for back porting PRs from GitHub or the Private Azure Repos (#20670)

Co-authored-by: Dongbo Wang <dongbow@microsoft.com>

---------

Co-authored-by: Dongbo Wang <dongbow@microsoft.com>
This commit is contained in:
Travis Plunk 2024-08-13 13:07:53 -07:00 committed by GitHub
parent c11e4cf0d1
commit b409e5613a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 237 additions and 12 deletions

View File

@ -0,0 +1,78 @@
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.
BeforeDiscovery {
$testCases = @(
@{
remoteInfo = 'upstream git@ssh.dev.azure.com:v3/azDoOrg/PowerShellCore/PowerShell (fetch)'
upstreamRemote = 'upstream'
org = 'azDoOrg'
project = 'PowerShellCore'
UpstreamHost = 'git@ssh.dev.azure.com'
RemoteType = 'AzureRepo'
}
@{
remoteInfo = 'upstream https://azDoOrg.visualstudio.com/PowerShell/_git/PowerShell (fetch)'
upstreamRemote = 'upstream'
org = 'azDoOrg'
project = 'PowerShell'
UpstreamHost = 'azDoOrg.visualstudio.com'
RemoteType = 'AzureRepo'
}
@{
remoteInfo = 'upstream https://github.com/PowerShell/PowerShell.git (fetch)'
upstreamRemote = 'upstream'
org = 'PowerShell'
project = 'github.com'
UpstreamHost = 'github.com'
RemoteType = 'GitHub'
}
@{
remoteInfo = 'github https://github.com/PowerShell/PowerShell.git (fetch)'
upstreamRemote = 'github'
org = 'PowerShell'
project = 'github.com'
UpstreamHost = 'github.com'
RemoteType = 'GitHub'
}
@{
remoteInfo = 'asonetuhasoeu git@github.com:PowerShell/PowerShell.git (fetch)'
upstreamRemote = 'asonetuhasoeu'
org = 'PowerShell'
project = 'github.com'
UpstreamHost = 'github.com'
RemoteType = 'GitHub'
}
)
}
Describe "Get-UpstreamInfo" {
BeforeAll {
Import-Module $PSScriptRoot/../../tools/releaseTools.psm1 -force -Verbose
}
It "parses remote Info correctly: <RemoteInfo>" -TestCases $testCases -Test {
param(
[string]
$RemoteInfo,
[string]
$UpstreamRemote,
[string]
$org,
[string]
$Project,
[string]
$UpstreamHost,
[string]
$remoteType
)
$upstreamInfo = Get-UpstreamInfo -Upstream $RemoteInfo -UpstreamRemote $UpstreamRemote
$upstreamInfo | Should -Not -BeNullOrEmpty
$upstreamInfo.org | Should -Be $org
$upstreamInfo.project | Should -Be $Project
$upstreamInfo.repo | Should -Be 'PowerShell'
$upstreamInfo.host | Should -Be $UpstreamHost
$upstreamInfo.remoteType | Should -Be $remoteType
}
}

View File

@ -723,6 +723,74 @@ function Get-PRBackportReport {
$prs
}
}
enum RemoteType {
GitHub
AzureRepo
}
function Get-UpstreamInfo {
param(
[Parameter(Mandatory=$true)]
[string[]]$Upstream,
[Parameter(Mandatory=$true)]
[string]$UpstreamRemote
)
$upstreamName = '(powershell(core)?)(/_git)?/(powershell)'
$pattern = "^$UpstreamRemote\s*(.*)\:(.*/([-\w.]+)/)?$upstreamName(\.git)?.*fetch"
Write-Verbose -Verbose "searching for an upstream with regex: '$pattern'"
$Upstream = $Upstream | Where-Object { $_ -match $pattern }
Write-Verbose -Verbose "found $Upstream"
if (!$Upstream) {
throw "Please create an upstream remote that points to $upstreamName"
}
$matches | Format-Table | Out-String -Stream -Width 9999 | Write-Verbose
$org = $matches[3]
if ($org -ne 'github.com' -and $matches[1] -ne 'git@github.com') {
Write-Verbose 'parsing Azure repo remote' -Verbose
# Azure Repo remote
$project = $matches[4]
$repo = $matches[7]
$upstreamHost = $matches[1]
if ($upstreamHost -eq 'https') {
$upstreamHost = $org
}
# matches everything but `.` ending in a `.`
# in other word, matching the first part of a hostname.
# like `www.microsoft.com` it would match `www.` with `www` in a capture group.
if ($org -match '([^\..]*)\.') {
$org = $Matches[1]
}
} else {
Write-Verbose 'parsing github remote' -Verbose
# GitHub Repo remote
$org = $matches[4]
$repo = $matches[7]
$upstreamHost = 'github.com'
$project = $upstreamHost
}
$remoteType = [RemoteType]::GitHub
if ($upstreamHost -match '.*azure.com$' -or $upstreamHost -match '.*visualstudio.com$') {
[RemoteType] $remoteType = [RemoteType]::AzureRepo
}
$upstreamMatchInfo = @{
org = $org
project = $project
repo = $repo
host = $upstreamHost
remoteType = $remoteType
}
return $upstreamMatchInfo
}
# Backports a PR
# requires:
@ -748,7 +816,10 @@ function Invoke-PRBackport {
$Overwrite,
[string]
$BranchPostFix
$BranchPostFix,
[string]
$UpstreamRemote = 'upstream'
)
function script:Invoke-NativeCommand {
param(
@ -792,15 +863,14 @@ function Invoke-PRBackport {
throw "PR is not merged ($state)"
}
$upstream = $null
$upstreamName = 'powershell/powershell'
$upstream = Invoke-NativeCommand { git remote -v } | Where-Object { $_ -match "^upstream.*$upstreamName.*fetch" }
$upstream = Invoke-NativeCommand { git remote -v }
$upstreamMatchInfo = Get-UpstreamInfo -Upstream $upstream -UpstreamRemote $UpstreamRemote
$remoteType = $upstreamMatchInfo.remoteType
if (!$upstream) {
throw "Please create an upstream remote that points to $upstreamName"
}
Write-Verbose -Verbose "remotetype: $remoteType"
$upstreamMatchInfo | Format-Table | Out-String -Stream -Width 9999 | Write-Verbose -Verbose
Invoke-NativeCommand { git fetch upstream $Target }
Invoke-NativeCommand { git fetch $UpstreamRemote $Target }
$switch = '-c'
if ($Overwrite) {
@ -812,10 +882,22 @@ function Invoke-PRBackport {
$branchName += "-$BranchPostFix"
}
if ($PSCmdlet.ShouldProcess("Create branch $branchName from upstream/$Target")) {
Invoke-NativeCommand { git switch upstream/$Target $switch $branchName }
if ($PSCmdlet.ShouldProcess("Create branch $branchName from $UpstreamRemote/$Target")) {
Invoke-NativeCommand { git switch $UpstreamRemote/$Target $switch $branchName }
}
try {
$revParseParams = @(
'--verify'
"$commitId^{commit}"
)
Invoke-NativeCommand { git rev-parse --quiet $revParseParams }
}
catch {
throw "Commit does not exist. Try fetching the upstream. (git rev-parse $revParseParams)"
}
try {
Invoke-NativeCommand { git cherry-pick $commitId }
}
@ -824,7 +906,72 @@ function Invoke-PRBackport {
}
if ($PSCmdlet.ShouldProcess("Create the PR")) {
gh pr create --base $Target --title $backportTitle --body "Backport #$PrNumber" --web
$body = "Backport #$PrNumber"
switch($remoteType) {
"AzureRepo" {
Write-Verbose -Verbose "Pushing branch to $UpstreamRemote"
git push --set-upstream $UpstreamRemote HEAD
$parameters = @(
'repos'
'pr'
'create'
)
# Open in the browser
$parameters += @(
'--open'
)
$parameters += @(
'--target-branch'
$Target
)
$parameters += @(
'--title'
$backportTitle
)
$parameters += @(
'--description'
$body
)
$parameters += @(
'--squash'
'true'
)
$parameters += @(
'--auto-complete'
'true'
)
$parameters += @(
'--delete-source-branch'
'true'
)
$parameters += @(
'--org'
"https://dev.azure.com/$($upstreamMatchInfo.org)"
)
$parameters += @(
'--project'
$upstreamMatchInfo.project
)
$parameters += @(
'--source-branch'
$branchName
)
$parameters += @(
'--repository'
$upstreamMatchInfo.repo
)
Write-Verbose -Verbose "az $parameters"
$null = Invoke-NativeCommand { az $parameters }
}
"GitHub" {
Write-Verbose -Verbose "Creating PR using gh CLI"
gh pr create --base $Target --title $backportTitle --body $body --web
}
default {
throw "unknown remoteType: $remoteType"
}
}
}
}
@ -848,4 +995,4 @@ function Invoke-PRBackportApproved {
}
}
Export-ModuleMember -Function Get-ChangeLog, Get-NewOfficalPackage, Update-PsVersionInCode, Get-PRBackportReport, Invoke-PRBackport, Invoke-PRBackportApproved
Export-ModuleMember -Function Get-ChangeLog, Get-NewOfficalPackage, Update-PsVersionInCode, Get-PRBackportReport, Invoke-PRBackport, Invoke-PRBackportApproved, Get-UpstreamInfo