Private/Save-GitHubRepoArchive.ps1

function Save-GitHubRepoArchive {
    <#
    .SYNOPSIS
        Downloads and extracts a GitHub repository zip archive.

    .DESCRIPTION
        Downloads the zip archive of a public GitHub repository and extracts it
        to a temporary directory. Returns the path to the extracted repo contents
        (inside the single top-level prefix directory that GitHub creates).

        The caller is responsible for cleaning up the returned directory when done
        (Remove-Item -Recurse -Force on the parent temp directory).

    .PARAMETER Owner
        The GitHub repository owner (user or organization).

    .PARAMETER Repo
        The GitHub repository name.

    .PARAMETER Ref
        Branch, tag, or commit SHA. Defaults to 'master'.

    .OUTPUTS
        [PSCustomObject] with:
        - ExtractedPath: Path to the extracted repo contents
        - TempDirectory: Path to the temp directory (caller must clean up)

    .EXAMPLE
        $archive = Save-GitHubRepoArchive -Owner prowler-cloud -Repo prowler
        try {
            Get-ChildItem $archive.ExtractedPath
        }
        finally {
            Remove-Item $archive.TempDirectory -Recurse -Force
        }
    #>

    [CmdletBinding()]
    [OutputType([PSCustomObject])]
    param(
        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [string]$Owner,

        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [string]$Repo,

        [Parameter()]
        [ValidateNotNullOrEmpty()]
        [string]$Ref = 'master'
    )

    $ErrorActionPreference = 'Stop'

    $tempDir = Join-Path ([System.IO.Path]::GetTempPath()) "github-archive-$([guid]::NewGuid().ToString('N').Substring(0,8))"
    New-Item -ItemType Directory -Path $tempDir -Force | Out-Null

    $zipPath = Join-Path $tempDir "$Repo-$Ref.zip"
    $extractDir = Join-Path $tempDir 'extracted'

    $uri = "https://github.com/$Owner/$Repo/archive/$Ref.zip"
    Write-Verbose "Downloading archive from $uri..."

    try {
        Invoke-WebRequest -Uri $uri -OutFile $zipPath -ErrorAction Stop
        Write-Verbose "Downloaded archive to $zipPath ($('{0:N1} MB' -f ((Get-Item $zipPath).Length / 1MB)))"

        Write-Verbose "Extracting archive..."
        Expand-Archive -Path $zipPath -DestinationPath $extractDir -Force -ErrorAction Stop

        # GitHub archives contain a single top-level directory like "prowler-master/"
        $topLevelDirs = @(Get-ChildItem -Path $extractDir -Directory)
        if ($topLevelDirs.Count -ne 1) {
            throw "Expected exactly 1 top-level directory in archive, found $($topLevelDirs.Count)"
        }

        $extractedPath = $topLevelDirs[0].FullName
        Write-Verbose "Extracted to $extractedPath"

        [PSCustomObject]@{
            ExtractedPath = $extractedPath
            TempDirectory = $tempDir
        }
    }
    catch {
        # Clean up temp directory on failure
        if (Test-Path $tempDir) {
            Remove-Item $tempDir -Recurse -Force -ErrorAction SilentlyContinue
        }
        throw
    }
    finally {
        # Always clean up the zip file (extracted directory stays)
        if (Test-Path $zipPath) {
            Remove-Item $zipPath -Force -ErrorAction SilentlyContinue
        }
    }
}