Private/Save-GitHubRepoFile.ps1
|
function Save-GitHubRepoFile { <# .SYNOPSIS Downloads a single file from a public GitHub repository to disk. .DESCRIPTION Downloads raw file content from a public GitHub repository using the raw.githubusercontent.com URL and saves it to a local path. Automatically creates the parent directory if it does not exist. 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 download from. Defaults to 'master'. .PARAMETER Path The file path within the repository. For example, 'prowler/providers/azure/services/entra/entra_service.py'. .PARAMETER Destination The local file path to save the downloaded content to. .OUTPUTS [string] The Destination path on success, for pipeline chaining. Returns nothing on error (unless -ErrorAction Stop is specified). .EXAMPLE Save-GitHubRepoFile -Owner prowler-cloud -Repo prowler -Path 'README.md' -Destination '/tmp/readme.md' # Downloads the README from master and saves to /tmp/readme.md. .EXAMPLE Save-GitHubRepoFile -Owner prowler-cloud -Repo prowler -Ref 'v4.0.0' -Path 'setup.py' -Destination './setup.py' -Verbose # Downloads setup.py from the v4.0.0 tag. .EXAMPLE $file = Save-GitHubRepoFile -Owner prowler-cloud -Repo prowler -Path 'config.yaml' -Destination '/tmp/config.yaml' Get-Content $file # Downloads and then reads the file via pipeline chaining. #> [CmdletBinding()] [OutputType([string])] param( [Parameter(Mandatory)] [ValidateNotNullOrEmpty()] [string]$Owner, [Parameter(Mandatory)] [ValidateNotNullOrEmpty()] [string]$Repo, [Parameter()] [ValidateNotNullOrEmpty()] [string]$Ref = 'master', [Parameter(Mandatory)] [ValidateNotNullOrEmpty()] [string]$Path, [Parameter(Mandatory)] [ValidateNotNullOrEmpty()] [string]$Destination ) # Capture caller's ErrorAction before we override $shouldThrow = $ErrorActionPreference -eq 'Stop' $ErrorActionPreference = 'Stop' $uri = "https://raw.githubusercontent.com/$Owner/$Repo/$Ref/$Path" $headers = @{ 'User-Agent' = 'Devolutions-CIEM' } Write-Verbose "Downloading $Owner/$Repo/$Path (ref: $Ref) to $Destination..." # Create parent directory if it does not exist $parentDir = Split-Path -Path $Destination -Parent if ($parentDir -and -not (Test-Path -Path $parentDir)) { Write-Verbose "Creating directory: $parentDir" New-Item -Path $parentDir -ItemType Directory -Force | Out-Null } try { Invoke-WebRequest -Uri $uri -Headers $headers -OutFile $Destination -ErrorAction Stop } catch [Microsoft.PowerShell.Commands.HttpResponseException] { $statusCode = [int]$_.Exception.Response.StatusCode switch ($statusCode) { 404 { $msg = "File not found in repo: $Owner/$Repo/$Path (ref: $Ref)" if ($shouldThrow) { throw $msg } Write-Warning $msg return } 403 { $msg = "GitHub rate limit exceeded downloading $Owner/$Repo/$Path. Unauthenticated requests are limited to 60 per hour." if ($shouldThrow) { throw $msg } Write-Warning $msg return } default { $msg = "GitHub download error ($statusCode) for $Owner/$Repo/$Path" if ($shouldThrow) { throw $msg } Write-Warning $msg return } } } catch { $msg = "Failed to download $Owner/$Repo/${Path}: $_" if ($shouldThrow) { throw $msg } Write-Warning $msg return } Write-Verbose "Saved to $Destination" $Destination } |