Private/Get-GitHubRepoTree.ps1
|
function Get-GitHubRepoTree { <# .SYNOPSIS Lists files in a public GitHub repository using the Trees API. .DESCRIPTION Retrieves the full file tree of a public GitHub repository via the Git Trees API with recursive listing. Optionally filters results to paths starting with a given prefix. No authentication is required, but unauthenticated requests are limited to 60 per hour. By default, non-success responses result in warnings (silent failure). Use -ErrorAction Stop to throw terminating errors on non-success responses. .PARAMETER Owner The GitHub repository owner (user or organization). For example, 'prowler-cloud'. .PARAMETER Repo The GitHub repository name. For example, 'prowler'. .PARAMETER Ref The branch name, tag, or commit SHA to list. Defaults to 'master'. .PARAMETER Path Optional path prefix to filter results. Only entries whose path starts with this value are returned. For example, 'prowler/providers/azure'. .OUTPUTS [PSCustomObject[]] Objects with Path (string), Type (blob|tree), and Size (int) properties. Returns nothing on error (unless -ErrorAction Stop is specified). .EXAMPLE Get-GitHubRepoTree -Owner prowler-cloud -Repo prowler # Lists all files and directories in the prowler repo at the master branch. .EXAMPLE Get-GitHubRepoTree -Owner prowler-cloud -Repo prowler -Path 'prowler/providers/azure/services/entra' -Verbose # Lists only entries under the entra services directory. .EXAMPLE Get-GitHubRepoTree -Owner prowler-cloud -Repo prowler -Ref 'v4.0.0' -ErrorAction Stop # Lists files at tag v4.0.0, throwing on any error. #> [CmdletBinding()] [OutputType([PSCustomObject[]])] param( [Parameter(Mandatory)] [ValidateNotNullOrEmpty()] [string]$Owner, [Parameter(Mandatory)] [ValidateNotNullOrEmpty()] [string]$Repo, [Parameter()] [ValidateNotNullOrEmpty()] [string]$Ref = 'master', [Parameter()] [ValidateNotNullOrEmpty()] [string]$Path ) # Capture caller's ErrorAction before we override $shouldThrow = $ErrorActionPreference -eq 'Stop' $ErrorActionPreference = 'Stop' $uri = "https://api.github.com/repos/$Owner/$Repo/git/trees/${Ref}?recursive=1" $headers = @{ 'User-Agent' = 'Devolutions-CIEM' 'Accept' = 'application/vnd.github+json' } Write-Verbose "Fetching tree for $Owner/$Repo at ref '$Ref'..." try { $response = Invoke-RestMethod -Uri $uri -Headers $headers -ErrorAction Stop } catch [Microsoft.PowerShell.Commands.HttpResponseException] { $statusCode = [int]$_.Exception.Response.StatusCode switch ($statusCode) { 403 { $msg = "GitHub API rate limit exceeded for $Owner/$Repo. Unauthenticated requests are limited to 60 per hour." if ($shouldThrow) { throw $msg } Write-Warning $msg return } 404 { $msg = "Repository not found: $Owner/$Repo (ref: $Ref)" if ($shouldThrow) { throw $msg } Write-Warning $msg return } default { $msg = "GitHub API error ($statusCode) fetching tree for $Owner/$Repo" if ($shouldThrow) { throw $msg } Write-Warning $msg return } } } catch { $msg = "Failed to fetch GitHub tree for $Owner/${Repo}: $_" if ($shouldThrow) { throw $msg } Write-Warning $msg return } if ($response.truncated) { Write-Warning "GitHub tree response was truncated. The repository $Owner/$Repo has more files than the API can return in a single request." } $tree = $response.tree # Filter by path prefix if specified if ($Path) { $normalizedPath = $Path.TrimEnd('/') $tree = $tree | Where-Object { $_.path -like "$normalizedPath/*" -or $_.path -eq $normalizedPath } Write-Verbose "Filtered to $($tree.Count) entries matching prefix '$normalizedPath'" } foreach ($item in $tree) { [PSCustomObject]@{ Path = $item.path Type = $item.type Size = if ($item.PSObject.Properties['size']) { $item.size } else { $null } } } } |