Public/Find-AssignedPRs.ps1

<#
.SYNOPSIS
Finds pull requests assigned to a specific user in the most recently updated repositories for a given GitHub owner (organization or user).
 
.DESCRIPTION
The Find-AssignedPRs function searches the latest updated repositories (default: 15) for a specified GitHub owner (organization or user) and lists up to 3 pull requests per repository that are assigned to a specified user (default: 'copilot'). It uses the GitHub GraphQL API and requires a valid GitHub personal access token set in the GITHUB_TOKEN environment variable.
 
.PARAMETER Owner
The GitHub organization or user name whose repositories will be searched.
 
.PARAMETER Arg2
Optional. If an integer, specifies the number of repositories to search (default: 15). If a string, specifies the assignee's GitHub username (default: 'copilot').
 
.PARAMETER Arg3
Optional. If an integer, specifies the number of repositories to search (overrides Arg2 if both are integers). If a string, specifies the assignee's GitHub username (overrides Arg2 if both are strings).
 
.EXAMPLE
Find-AssignedPRs -Owner "octocat"
Searches the 15 most recently updated repositories owned by "octocat" for pull requests assigned to "copilot".
 
.EXAMPLE
Find-AssignedPRs -Owner "octocat" 20 "alice"
Searches the 20 most recently updated repositories owned by "octocat" for pull requests assigned to "alice".
 
.NOTES
- Requires the GITHUB_TOKEN environment variable to be set with a valid GitHub personal access token.
- Uses the GitHub GraphQL API.
- Outputs a formatted table of matching pull requests, including URL, title, state, assignee, repository, number, creation and update dates, and author.
 
#>

function Find-AssignedPRs {
  [CmdletBinding()]
  param(
    [Parameter(Mandatory, Position = 0)]
    [string]$Owner,
    [Parameter(Position = 1)]
    [object]$Arg2,
    [Parameter(Position = 2)]
    [object]$Arg3
  )

  # Defaults
  $RepoCount = 15
  $Assignee = 'copilot'

  # Parse flexible arguments
  if ($null -ne $Arg2) {
    if ($Arg2 -is [int]) {
      $RepoCount = $Arg2
    }
    elseif ($Arg2 -is [string]) {
      $Assignee = $Arg2
    }
  }
  if ($null -ne $Arg3) {
    if ($Arg3 -is [int]) {
      $RepoCount = $Arg3
    }
    elseif ($Arg3 -is [string]) {
      $Assignee = $Arg3
    }
  }

  Write-Host "Searching for PRs assigned to '$Assignee' in the latest $RepoCount repos for owner '$Owner'..."

  if (-not $env:GITHUB_TOKEN) {
    throw "GITHUB_TOKEN environment variable is not set. Please set it to a valid GitHub personal access token."
  }

  $headers = @{ Authorization = "Bearer $($env:GITHUB_TOKEN)" }

  # Get the 15 most recently updated repos for the owner (org or user) using correct GraphQL
  $repos = @()
  $isOrg = $true
  try {
    $query = @"
      query {
        organization(login: "$Owner") {
          repositories(first: $RepoCount, orderBy: {field: UPDATED_AT, direction: DESC}, privacy: ALL) {
            nodes { name }
          }
        }
      }
"@

    $body = @{ query = $query } | ConvertTo-Json
    $result = Invoke-RestMethod -Uri https://api.github.com/graphql -Headers $headers -Method Post -Body $body
    if ($null -eq $result.data.organization) { throw "Not an org" }
    $repos = $result.data.organization.repositories.nodes | ForEach-Object { $_.name }
  }
  catch {
    $isOrg = $false
    $query = @"
      query {
        user(login: "$Owner") {
          repositories(first: $RepoCount, orderBy: {field: UPDATED_AT, direction: DESC}, ownerAffiliations: [OWNER, COLLABORATOR, ORGANIZATION_MEMBER]) {
            nodes { name }
          }
        }
      }
"@

    $body = @{ query = $query } | ConvertTo-Json
    $result = Invoke-RestMethod -Uri https://api.github.com/graphql -Headers $headers -Method Post -Body $body
    if ($null -eq $result.data.user) {
      throw "User or organization '$Owner' does not exist."
    }
    $repos = $result.data.user.repositories.nodes | ForEach-Object { $_.name }
  }

  # For each of the 15 most recently updated repos, get the 10 most recently updated PRs and pick the first 3 assigned to the user
  $allPRs = @()
  Write-Verbose "Checking these repos: $($repos -join ', ')"
  for ($i = 0; $i -lt $repos.Count; $i++) {
    $repo = $repos[$i]
    Write-Progress -Activity "Checking PRs" -Status "Repo: $repo ($($i+1)/$($repos.Count))" -PercentComplete (($i + 1) / $repos.Count * 100)
    Write-Verbose "--- Checking repo: $repo ---"
    $query = @"
      query {
        repository(owner: "$Owner", name: "$repo") {
          pullRequests(first: 10, orderBy: {field: UPDATED_AT, direction: DESC}) {
            nodes {
              number
              title
              url
              state
              createdAt
              updatedAt
              author { login }
              assignees(first: 10) {
                nodes { login }
              }
            }
          }
        }
      }
"@

    $body = @{ query = $query } | ConvertTo-Json
    $result = Invoke-RestMethod -Uri https://api.github.com/graphql -Headers $headers -Method Post -Body $body
    $prs = $result.data.repository.pullRequests.nodes
    if ($prs) {
      foreach ($pr in $prs) {
        $assignees = $pr.assignees.nodes | ForEach-Object { $_.login }
        Write-Verbose ("PR #$($pr.number): $($pr.title) | Assignees: $($assignees -join ', ')")
      }
      $matched = @()
      foreach ($pr in $prs) {
        $assignees = $pr.assignees.nodes | ForEach-Object { $_.login }
        if ($assignees -contains $Assignee) {
          $pr | Add-Member -NotePropertyName repo -NotePropertyValue $repo -Force
          $matched += $pr
          if ($matched.Count -ge 3) { break }
        }
      }
      $allPRs += $matched
    }
  }

  # Output results
  if ($allPRs.Count -eq 0) {
    Write-Host "No PRs assigned to $Assignee found in any of the latest $RepoCount repos for owner $Owner."
  }
  else {
    $allPRs | ForEach-Object {
      $_ | Add-Member -NotePropertyName assignee -NotePropertyValue (($_.assignees.nodes | ForEach-Object { $_.login }) -join ', ') -Force
      $_
    } |
    Sort-Object updatedAt -Descending |
    Select-Object url, title, state, assignee, repo, number, createdAt, updatedAt, author |
    Format-Table -AutoSize
  }
}