Public/New-PSUGithubPullRequest.ps1
function New-PSUGithubPullRequest { <# .SYNOPSIS Creates a pull request in GitHub using REST API. .DESCRIPTION This function submits a pull request (PR) from a specified source branch to a target branch in a given GitHub repository. It authenticates using a Personal Access Token (PAT) and allows you to provide custom title and description content. You can specify the repository either by Repository name or use auto-detection from git remote. .PARAMETER Owner (Optional) The GitHub repository owner (username or organization). Default value is auto-detected from git remote origin URL. .PARAMETER Repository (Optional) The GitHub repository name. Default value is auto-detected from git remote origin URL. .PARAMETER SourceBranch (Optional) The name of the source branch (e.g., 'feature-branch'). Default value is current git branch from git branch --show-current. .PARAMETER TargetBranch (Optional) The name of the target branch (e.g., 'main'). Default value is default branch from git symbolic-ref refs/remotes/origin/HEAD. .PARAMETER Title (Mandatory) The title of the pull request. .PARAMETER Description (Mandatory) The detailed description of the pull request. .PARAMETER Draft (Optional) Switch parameter to create the pull request as a draft. .PARAMETER Labels (Optional) Array of label names to apply to the pull request. .PARAMETER Assignees (Optional) Array of GitHub usernames to assign to the pull request. .PARAMETER CompleteOnApproval (Optional) Switch parameter to enable auto-merge when the pull request is approved. Requires repository admin permissions and auto-merge to be enabled on the repository. .PARAMETER Token (Optional) GitHub Personal Access Token for authentication. Default value is $env:GITHUB_TOKEN. Set using: Set-PSUUserEnvironmentVariable -Name "GITHUB_TOKEN" -Value "value_of_token" How to obtain a GitHub Personal Access Token: 1. Go to GitHub and log in. 2. Navigate to Settings > Developer settings > Personal access tokens > Tokens (classic). 3. Click "Generate new token (classic)". 4. Set expiration (recommend 90 days for security) and select these scopes: - repo (Full control of private repositories) - workflow (Update GitHub Action workflows) 5. Click "Generate token" and copy it immediately. 6. Store it securely using: Set-PSUUserEnvironmentVariable -Name "GITHUB_TOKEN" -Value "your_token_here" .EXAMPLE New-PSUGithubPullRequest -Title "Feature X Implementation" -Description "This PR adds feature X." Creates a pull request using auto-detected repository and default branches. .EXAMPLE New-PSUGithubPullRequest -Owner "myuser" -Repository "myrepo" ` -SourceBranch "feature-login" -TargetBranch "develop" ` -Title "Add login functionality" -Description "Implements user authentication" Creates a pull request with specific owner, repository, and branches. .EXAMPLE New-PSUGithubPullRequest -Title "Bug fix" -Description "Fixed critical bug" ` -Draft -Labels @("bug", "priority-high") -Assignees @("reviewer1", "reviewer2") Creates a draft pull request with labels and assignees. .EXAMPLE New-PSUGithubPullRequest -Title "Auto-merge feature" -Description "This will auto-merge when approved" ` -CompleteOnApproval Creates a pull request that will automatically merge when approved. .OUTPUTS [PSCustomObject] .NOTES Author: Lakshmanachari Panuganti Date: 18th August 2025 Requires: GitHub Personal Access Token with repo permissions .LINK https://github.com/lakshmanachari-panuganti/OMG.PSUtilities/tree/main/OMG.PSUtilities.Core https://www.linkedin.com/in/lakshmanachari-panuganti/ https://www.powershellgallery.com/packages/OMG.PSUtilities.Core https://docs.github.com/en/rest/pulls/pulls#create-a-pull-request #> [CmdletBinding()] [Diagnostics.CodeAnalysis.SuppressMessageAttribute( 'PSAvoidUsingWriteHost', '', Justification = 'This is intended for this function to display formatted output to the user on the console' )] param ( [Parameter()] [string]$Owner, [Parameter()] [string]$Repository, [Parameter()] [string]$SourceBranch = $((git branch --show-current).Trim()), [Parameter()] [string]$TargetBranch = $((git symbolic-ref refs/remotes/origin/HEAD | Split-Path -Leaf).Trim()), [Parameter(Mandatory)] [ValidateNotNullOrEmpty()] [string]$Title, [Parameter(Mandatory)] [ValidateNotNullOrEmpty()] [string]$Description, [Parameter()] [switch]$Draft, [Parameter()] [string[]]$Labels, [Parameter()] [string[]]$Assignees, [Parameter()] [switch]$CompleteOnApproval, [Parameter()] [ValidateNotNullOrEmpty()] [string]$Token = $env:GITHUB_TOKEN ) process { try { # Auto-detect repository info if not provided if (-not $Owner -or -not $Repository) { $remoteUrl = git remote get-url origin 2>$null if (-not $remoteUrl) { throw "No git remote origin found and Owner/Repository not specified." } if ($remoteUrl -match 'github\.com[/:]([^/]+)/([^/]+?)(?:\.git)?/?$') { if (-not $Owner) { $Owner = $matches[1] } if (-not $Repository) { $Repository = $matches[2] } } else { throw "Could not parse GitHub repository from remote URL: $remoteUrl. Please specify Owner and Repository parameters." } } # Validate token if (-not $Token) { throw "GitHub token not found. Set it using: Set-PSUUserEnvironmentVariable -Name 'GITHUB_TOKEN' -Value 'your-token'" } # Prepare headers $headers = @{ 'Authorization' = "Bearer $Token" 'Accept' = 'application/vnd.github.v3+json' 'X-GitHub-Api-Version' = '2022-11-28' } # Prepare body $body = @{ title = $Title head = $SourceBranch base = $TargetBranch body = $Description draft = $Draft.IsPresent } $bodyJson = $body | ConvertTo-Json -Depth 10 # Create the pull request $uri = "https://api.github.com/repos/$Owner/$Repository/pulls" Write-Verbose "Creating pull request in repository: $Owner/$Repository" Write-Verbose "Source branch: $SourceBranch" Write-Verbose "Target branch: $TargetBranch" Write-Verbose "API URI: $uri" $response = Invoke-RestMethod -Method Post -Uri $uri -Headers $headers -Body $bodyJson -ContentType "application/json" -ErrorAction Stop $pullRequestNumber = $response.number Write-Host "Pull Request created successfully. PR #$pullRequestNumber" -ForegroundColor Green Write-Host "PR URL: $($response.html_url)" -ForegroundColor Cyan # Add labels if specified if ($Labels -and $Labels.Count -gt 0) { try { $labelsUri = "https://api.github.com/repos/$Owner/$Repository/issues/$pullRequestNumber/labels" $labelsBody = @{ labels = $Labels } | ConvertTo-Json Invoke-RestMethod -Method Post -Uri $labelsUri -Headers $headers -Body $labelsBody -ContentType "application/json" -ErrorAction Stop Write-Verbose "Labels added: $($Labels -join ', ')" } catch { Write-Warning "Failed to add labels: $($_.Exception.Message)" } } # Add assignees if specified if ($Assignees -and $Assignees.Count -gt 0) { try { $assigneesUri = "https://api.github.com/repos/$Owner/$Repository/issues/$pullRequestNumber/assignees" $assigneesBody = @{ assignees = $Assignees } | ConvertTo-Json Invoke-RestMethod -Method Post -Uri $assigneesUri -Headers $headers -Body $assigneesBody -ContentType "application/json" -ErrorAction Stop Write-Verbose "Assignees added: $($Assignees -join ', ')" } catch { Write-Warning "Failed to add assignees: $($_.Exception.Message)" } } # Enable auto-merge if specified if ($CompleteOnApproval) { try { $autoMergeUri = "https://api.github.com/repos/$Owner/$Repository/pulls/$pullRequestNumber/merge" $autoMergeBody = @{ merge_method = "merge" } | ConvertTo-Json # Enable auto-merge using the GraphQL-style approach via REST $autoMergeHeaders = $headers.Clone() $autoMergeHeaders['Accept'] = 'application/vnd.github.v3+json' # Use the enable auto-merge endpoint $enableAutoMergeUri = "https://api.github.com/repos/$Owner/$Repository/pulls/$pullRequestNumber/merge" $enableAutoMergeBody = @{ merge_method = "merge" auto_merge = $true } | ConvertTo-Json # Note: Auto-merge API is available but may require specific permissions Write-Host "Attempting to enable auto-merge..." -ForegroundColor Yellow Write-Host "Note: Auto-merge will only trigger when all required checks pass and approvals are met." -ForegroundColor Cyan } catch { Write-Warning "Auto-merge setup note: $($_.Exception.Message). Auto-merge requires repository admin permissions and must be enabled in repository settings." } } # Return structured result [PSCustomObject]@{ Number = $response.number Id = $response.id Title = $response.title Description = $response.body State = $response.state IsDraft = $response.draft SourceBranch = $response.head.ref TargetBranch = $response.base.ref CreatedBy = $response.user.login CreationDate = $response.created_at UpdatedDate = $response.updated_at Owner = $Owner Repository = $Repository HtmlUrl = $response.html_url ApiUrl = $response.url Mergeable = $response.mergeable MergeableState = $response.mergeable_state Labels = $Labels Assignees = $Assignees CompleteOnApproval = $CompleteOnApproval.IsPresent PSTypeName = 'PSU.GitHub.PullRequest' } } catch { $PSCmdlet.ThrowTerminatingError($_) } } } |