Skip to content

Add Get, Update, and Comment commands for pull requests #546

@MariusStorhaug

Description

Context

The GitHub PowerShell module is used to automate repository management tasks, including workflows that create and manage pull requests. For example, in PSModule/GoogleFonts, an automated workflow updates font data and opens a PR. When a new Auto-Update PR is created, older Auto-Update PRs should be commented on (explaining they are superseded) and then closed.

Currently, no commands exist in the module for interacting with pull requests. The only workaround is shelling out to the gh CLI, which breaks consistency with the rest of the module and loses the typed object pipeline experience.

Request

The module needs commands to list, retrieve, update, and comment on pull requests. Specifically, the following capabilities are missing:

  1. List pull requests — filter by state, head branch, base branch, sort order
  2. Get a specific pull request — retrieve details by PR number
  3. Update a pull request — change title, body, state (open/closed), base branch, or maintainer modification flag
  4. Add a comment to a pull request — post an issue comment on a PR (since pull requests are issues in the GitHub API, this uses the issue comments endpoint)

What is expected

  • Get-GitHubPullRequest returns pull request objects with typed properties (state, head/base branch, author, labels, draft status, merge state, etc.)
  • Update-GitHubPullRequest allows changing state to closed (or reopening), updating title/body/base, with SupportsShouldProcess
  • Add-GitHubPullRequestComment posts a comment to a pull request and returns the comment object
  • All commands follow existing module conventions: pipeline input, context resolution, typed output classes

Acceptance criteria

  • Pull requests can be listed for a repository with optional filtering by state, head branch, and base branch
  • A specific pull request can be retrieved by number
  • A pull request can be closed (or reopened) by updating its state
  • A comment can be added to a pull request
  • All returned objects are strongly typed with a GitHubPullRequest class (and GitHubIssueComment class for comments)
  • Commands integrate with the existing Context resolution pattern

Use case

# List open PRs with a specific head branch
$oldPRs = Get-GitHubPullRequest -Owner 'PSModule' -Repository 'GoogleFonts' -State 'Open' -Head 'auto-update'

# Comment on each superseded PR and close it
foreach ($pr in $oldPRs) {
    $pr | Add-GitHubPullRequestComment -Body 'Superseded by a newer Auto-Update PR.'
    $pr | Update-GitHubPullRequest -State 'Closed'
}

Technical decisions

Function grouping: Pull request functions go under src/functions/public/Pull-Requests/ (public) and src/functions/private/Pull-Requests/ (private), following the convention of grouping by object type. Comment functions go under src/functions/private/Issues/ since the API endpoint is the issues comments endpoint.

API approach: Use REST API (not GraphQL) for all pull request endpoints. The Pulls REST API provides straightforward endpoints and the Pull Request response object has many fields that don't map easily to a single GraphQL query. This matches the pattern used by Get-GitHubBranchList and Remove-GitHubRepository.

Public function names and their parameter sets:

Public function Parameter sets Maps to
Get-GitHubPullRequest 'List pull requests' (default), 'Get a pull request' Private Get-GitHubPullRequestList, Get-GitHubPullRequestByNumber
Update-GitHubPullRequest Single parameter set (no name needed) Private Update-GitHubPullRequestByNumber
Add-GitHubPullRequestComment Single parameter set (no name needed) Private New-GitHubIssueComment

Private function names (one API call = one function):

Private function REST endpoint
Get-GitHubPullRequestList GET /repos/{owner}/{repo}/pulls
Get-GitHubPullRequestByNumber GET /repos/{owner}/{repo}/pulls/{pull_number}
Update-GitHubPullRequestByNumber PATCH /repos/{owner}/{repo}/pulls/{pull_number}
New-GitHubIssueComment POST /repos/{owner}/{repo}/issues/{issue_number}/comments

Class design:

  • GitHubPullRequest extends GitHubNode (provides ID and NodeID). Key properties: Number, State, Title, Body, Locked, ActiveLockReason, Draft, Head (branch ref info), Base (branch ref info), User (GitHubOwner), Labels, Milestone, Assignees, RequestedReviewers, RequestedTeams, CreatedAt, UpdatedAt, ClosedAt, MergedAt, MergeCommitSha, Merged, Mergeable, MergeableState, RebaseMergeable, MergedBy, Comments, ReviewComments, Commits, Additions, Deletions, ChangedFiles, MaintainerCanModify, AuthorAssociation, AutoMerge, Url, HtmlUrl, DiffUrl
  • GitHubIssueComment extends GitHubNode. Key properties: Body, User (GitHubOwner), CreatedAt, UpdatedAt, AuthorAssociation, HtmlUrl, IssueUrl
  • Types files: GitHubPullRequest.Types.ps1xml (alias PullRequestNumber), GitHubIssueComment.Types.ps1xml

Parameter naming:

  • Owner with aliases Organization, User (standard)
  • Repository (not Repo, with [Alias('Repo')] if needed)
  • Number for the pull request number (not ID — since ID is the database ID per module conventions, and the PR number is not the database ID)
  • State with [ValidateSet('Open', 'Closed', 'All')] for filtering
  • Head and Base as [string] for branch filtering on list
  • Sort with [ValidateSet('Created', 'Updated', 'Popularity', 'LongRunning')]
  • Direction with [ValidateSet('Asc', 'Desc')]

ShouldProcess:

  • Get-GitHubPullRequest — no SupportsShouldProcess (read-only)
  • Update-GitHubPullRequest[CmdletBinding(SupportsShouldProcess)] (modifies state)
  • Add-GitHubPullRequestComment[CmdletBinding(SupportsShouldProcess)] (creates content)

Comment function placement: The private function New-GitHubIssueComment lives under src/functions/private/Issues/ because it calls the issues API endpoint. The public function Add-GitHubPullRequestComment lives under src/functions/public/Pull-Requests/ because the user-facing context is pull requests. This private function can later be reused by a hypothetical Add-GitHubIssueComment public function.

Endpoints intentionally not covered in this issue:

  • POST /repos/{owner}/{repo}/pulls — Create a pull request (New-GitHubPullRequest) — separate issue
  • GET /repos/{owner}/{repo}/pulls/{pull_number}/commits — List PR commits — separate issue
  • GET /repos/{owner}/{repo}/pulls/{pull_number}/files — List PR files — separate issue
  • GET /repos/{owner}/{repo}/pulls/{pull_number}/merge — Check if merged — separate issue
  • PUT /repos/{owner}/{repo}/pulls/{pull_number}/merge — Merge a PR (Merge-GitHubPullRequest) — separate issue
  • PUT /repos/{owner}/{repo}/pulls/{pull_number}/update-branch — Update PR branch — separate issue

Test approach: Unit tests with mocked API responses using Pester. One test per parameter set per function, plus edge cases (empty list, not found, etc.).


Implementation plan

Classes

  • Add GitHubPullRequest class in src/classes/public/PullRequests/GitHubPullRequest.ps1 extending GitHubNode, with constructor mapping from REST API response fields to PascalCase properties
  • Add GitHubIssueComment class in src/classes/public/Issues/GitHubIssueComment.ps1 extending GitHubNode, with constructor mapping from REST API response fields
  • Add src/types/GitHubPullRequest.Types.ps1xml with alias property PullRequestNumber
  • Add src/types/GitHubIssueComment.Types.ps1xml with alias property IssueCommentID

Private functions

  • Add Get-GitHubPullRequestList in src/functions/private/Pull-Requests/Get-GitHubPullRequestList.ps1 — calls GET /repos/{owner}/{repo}/pulls with optional State, Head, Base, Sort, Direction query parameters
  • Add Get-GitHubPullRequestByNumber in src/functions/private/Pull-Requests/Get-GitHubPullRequestByNumber.ps1 — calls GET /repos/{owner}/{repo}/pulls/{pull_number}
  • Add Update-GitHubPullRequestByNumber in src/functions/private/Pull-Requests/Update-GitHubPullRequestByNumber.ps1 — calls PATCH /repos/{owner}/{repo}/pulls/{pull_number} with body containing optional title, body, state, base, maintainer_can_modify
  • Add New-GitHubIssueComment in src/functions/private/Issues/New-GitHubIssueComment.ps1 — calls POST /repos/{owner}/{repo}/issues/{issue_number}/comments with body containing body

Public functions

  • Add Get-GitHubPullRequest in src/functions/public/Pull-Requests/Get-GitHubPullRequest.ps1 — with parameter sets 'List pull requests' (default) and 'Get a pull request', pipeline support via ValueFromPipelineByPropertyName, context resolution, and switch routing to private functions
  • Add Update-GitHubPullRequest in src/functions/public/Pull-Requests/Update-GitHubPullRequest.ps1 — with SupportsShouldProcess, pipeline support, parameters for Title, Body, State, Base, MaintainerCanModify
  • Add Add-GitHubPullRequestComment in src/functions/public/Pull-Requests/Add-GitHubPullRequestComment.ps1 — with SupportsShouldProcess, pipeline support, calls private New-GitHubIssueComment

Tests

  • Add tests for Get-GitHubPullRequest — list (with filters), get by number, empty results
  • Add tests for Update-GitHubPullRequest — update state, update title/body, ShouldProcess behavior
  • Add tests for Add-GitHubPullRequestComment — add comment, ShouldProcess behavior

Documentation

  • Add examples in function help blocks showing common scenarios (list open PRs, close a PR, comment on a PR)
  • Update endpoint coverage tracking file if one exists

Metadata

Metadata

Labels

Projects

Status

Todo

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions