Viscalyx.GitHub.psm1

#Region '.\prefix.ps1' -1

$script:dscResourceCommonModulePath = Join-Path -Path $PSScriptRoot -ChildPath 'Modules/DscResource.Common'
Import-Module -Name $script:dscResourceCommonModulePath

$script:localizedData = Get-LocalizedData -DefaultUICulture 'en-US'
#EndRegion '.\prefix.ps1' 5
#Region '.\Private\Convert-SecureStringAsPlainText.ps1' -1

<#
    .SYNOPSIS
        Converts a secure string to a plain text string.
 
    .DESCRIPTION
        This function converts a secure string to a plain text string by using
        the SecureStringToBSTR method and then converts the resulting BSTR
        to a string using PtrToStringBSTR. The function ensures that the
        memory allocated for the BSTR is properly freed to prevent memory leaks.
 
    .PARAMETER SecureString
        The secure string that should be converted to a plain text string.
 
    .EXAMPLE
        $securePassword = ConvertTo-SecureString -String 'P@ssw0rd' -AsPlainText -Force
        $plainTextPassword = Convert-SecureStringAsPlainText -SecureString $securePassword
 
        This example converts a secure string to a plain text string.
 
    .EXAMPLE
        $credential = Get-Credential
        $plainTextPassword = Convert-SecureStringAsPlainText -SecureString $credential.Password
 
        This example gets credentials from the user and converts the password
        secure string to a plain text string.
 
    .NOTES
        This function should be used with caution as it exposes secure strings
        as plain text which can be a security risk. Only use this function when
        absolutely necessary and in a secure context.
#>

function Convert-SecureStringAsPlainText
{
    [CmdletBinding()]
    [OutputType([System.String])]
    param
    (
        [Parameter(Mandatory = $true)]
        [System.Security.SecureString]
        $SecureString
    )

    Write-Verbose -Message $script:localizedData.Convert_SecureStringAsPlainText_Converting

    # cSpell: ignore BSTR
    $bstr = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($SecureString)

    try
    {
        $plainText = [System.Runtime.InteropServices.Marshal]::PtrToStringBSTR($bstr)
    }
    finally
    {
        if ($bstr -ne [IntPtr]::Zero)
        {
            [System.Runtime.InteropServices.Marshal]::ZeroFreeBSTR($bstr)
        }
    }

    return $plainText
}
#EndRegion '.\Private\Convert-SecureStringAsPlainText.ps1' 62
#Region '.\Public\Get-GitHubRelease.ps1' -1

<#
    .SYNOPSIS
        Gets releases from a GitHub repository.
 
    .DESCRIPTION
        Gets releases from a GitHub repository. By default, it returns all non-prerelease
        and non-draft releases. Use the Latest parameter to return only the most
        recent release. Use the IncludePrerelease parameter to include prerelease
        versions, and the IncludeDraft parameter to include draft releases in the
        results.
 
    .PARAMETER OwnerName
        The name of the repository owner.
 
    .PARAMETER RepositoryName
        The name of the repository.
 
    .PARAMETER Latest
        If specified, only returns the most recent release that matches other filter
        criteria.
 
    .PARAMETER IncludePrerelease
        If specified, prerelease versions will be included in the results.
 
    .PARAMETER IncludeDraft
        If specified, draft releases will be included in the results.
 
    .PARAMETER Token
        The GitHub personal access token to use for authentication. If not specified,
        the function will use anonymous access which has rate limits.
 
    .EXAMPLE
        Get-GitHubRelease -OwnerName 'PowerShell' -RepositoryName 'PowerShell'
 
        Gets all non-prerelease, non-draft releases from the PowerShell/PowerShell repository.
 
    .EXAMPLE
        Get-GitHubRelease -OwnerName 'PowerShell' -RepositoryName 'PowerShell' -Latest
 
        Gets the latest non-prerelease, non-draft release from the PowerShell/PowerShell repository.
 
    .EXAMPLE
        Get-GitHubRelease -OwnerName 'PowerShell' -RepositoryName 'PowerShell' -IncludePrerelease
 
        Gets all releases including prereleases (but excluding drafts) from the PowerShell/PowerShell repository.
 
    .EXAMPLE
        Get-GitHubRelease -OwnerName 'PowerShell' -RepositoryName 'PowerShell' -IncludePrerelease -IncludeDraft
 
        Gets all releases including prereleases and drafts from the PowerShell/PowerShell repository.
 
    .NOTES
        For more information about GitHub releases, see the GitHub REST API documentation:
        https://docs.github.com/en/rest/releases/releases
#>

function Get-GitHubRelease
{
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $true)]
        [System.String]
        $OwnerName,

        [Parameter(Mandatory = $true)]
        [System.String]
        $RepositoryName,

        [Parameter()]
        [System.Management.Automation.SwitchParameter]
        $Latest,

        [Parameter()]
        [System.Management.Automation.SwitchParameter]
        $IncludePrerelease,

        [Parameter()]
        [System.Management.Automation.SwitchParameter]
        $IncludeDraft,

        [Parameter()]
        [System.Security.SecureString]
        $Token
    )

    Write-Verbose -Message ($script:localizedData.Get_GitHubRelease_ProcessingRepository -f $OwnerName, $RepositoryName)

    $apiBaseUrl = 'https://api.github.com/repos/{0}/{1}/releases' -f $OwnerName, $RepositoryName

    $headers = @{
        Accept = 'application/vnd.github.v3+json'
    }

    if ($Token)
    {
        # Convert SecureString to plain text for the authorization header
        $plainTextToken = Convert-SecureStringAsPlainText -SecureString $Token

        $headers.Authorization = "Bearer $plainTextToken"
    }

    try
    {
        $releases = Invoke-RestMethod -Uri $apiBaseUrl -Headers $headers -Method 'Get' -ErrorAction 'Stop'
    }
    catch
    {
        $writeErrorParameters = @{
            Message      = $script:localizedData.Get_GitHubRelease_Error_ApiRequest -f $_.Exception.Message
            Category     = 'ObjectNotFound'
            ErrorId      = 'GGHR0001' # cSpell: disable-line
            TargetObject = '{0}/{1}' -f $OwnerName, $RepositoryName
        }

        Write-Error @writeErrorParameters

        return $null
    }

    if (-not $releases -or $releases.Count -eq 0)
    {
        Write-Verbose -Message ($script:localizedData.Get_GitHubRelease_NoReleasesFound -f $OwnerName, $RepositoryName)

        return $null
    }

    if (-not $IncludePrerelease)
    {
        Write-Verbose -Message $script:localizedData.Get_GitHubRelease_FilteringPrerelease

        $releases = $releases |
            Where-Object -FilterScript { -not $_.prerelease }
    }

    if (-not $IncludeDraft)
    {
        Write-Verbose -Message $script:localizedData.Get_GitHubRelease_FilteringDraft

        $releases = $releases |
            Where-Object -FilterScript { -not $_.draft }
    }

    if ($Latest)
    {
        <#
            Sort by created_at descending to get the most recent release. The
            created_at attribute is the date of the commit used for the release,
            and not the date when the release was drafted or published.
        #>

        $latestRelease = $releases |
            Sort-Object -Property 'created_at' -Descending |
            Select-Object -First 1

        return $latestRelease
    }

    return $releases
}
#EndRegion '.\Public\Get-GitHubRelease.ps1' 159
#Region '.\Public\Get-GitHubReleaseAsset.ps1' -1

<#
    .SYNOPSIS
        Gets metadata for a specific asset from a GitHub repository release.
 
    .DESCRIPTION
        This command retrieves metadata information about assets from releases of
        a GitHub repository.
 
        You can use the Latest parameter to specifically target the latest release,
        and the IncludePrerelease parameter to include prerelease versions when
        determining the latest release. The IncludeDraft parameter allows including
        draft releases in the results.
 
        The command returns metadata including the asset name, size, download URL,
        release version, and other relevant information for matching assets.
 
    .PARAMETER OwnerName
        The name of the repository owner.
 
    .PARAMETER RepositoryName
        The name of the repository.
 
    .PARAMETER Latest
        If specified, only returns assets from the latest release based on semantic
        versioning.
 
    .PARAMETER IncludePrerelease
        If specified, prerelease versions will be included in the release results.
 
    .PARAMETER IncludeDraft
        If specified, draft releases will be included in the release results.
 
    .PARAMETER AssetName
        The name of the asset to retrieve metadata for.
        You can use wildcards to match the asset name.
        If not specified, all assets from the matching release will be returned.
 
    .PARAMETER Token
        The GitHub personal access token to use for authentication.
        If not specified, the command will use anonymous access which has rate limits.
 
    .EXAMPLE
        Get-GitHubReleaseAsset -OwnerName 'PowerShell' -RepositoryName 'PowerShell' -AssetName 'PowerShell-*-win-x64.msi'
 
        This example retrieves metadata for the Windows x64 MSI installer asset from the latest
        full release of the PowerShell/PowerShell repository.
 
    .EXAMPLE
        Get-GitHubReleaseAsset -OwnerName 'PowerShell' -RepositoryName 'PowerShell' -AssetName 'PowerShell-*-win-x64.msi' -IncludePrerelease
 
        This example retrieves metadata for the Windows x64 MSI installer asset from the latest
        release (including prereleases) of the PowerShell/PowerShell repository.
 
    .EXAMPLE
        Get-GitHubReleaseAsset -OwnerName 'Microsoft' -RepositoryName 'WSL' -AssetName '*.x64' -Token 'ghp_1234567890abcdef'
 
        This example retrieves metadata for the AppX bundle asset from the latest release of the
        Microsoft/WSL repository using a GitHub personal access token for authentication.
 
    .EXAMPLE
        Get-GitHubReleaseAsset -OwnerName 'PowerShell' -RepositoryName 'PowerShell' -IncludePrerelease -IncludeDraft
 
        This example retrieves metadata for all assets from all releases, including prereleases and drafts,
        of the PowerShell/PowerShell repository.
 
    .NOTES
        This command requires internet connectivity to access the GitHub API.
        GitHub API rate limits may apply for unauthenticated requests.
#>

function Get-GitHubReleaseAsset
{
    [CmdletBinding()]
    [OutputType([PSCustomObject])]
    param
    (
        [Parameter(Mandatory = $true)]
        [System.String]
        $OwnerName,

        [Parameter(Mandatory = $true)]
        [System.String]
        $RepositoryName,

        [Parameter()]
        [System.Management.Automation.SwitchParameter]
        $Latest,

        [Parameter()]
        [System.Management.Automation.SwitchParameter]
        $IncludePrerelease,

        [Parameter()]
        [System.Management.Automation.SwitchParameter]
        $IncludeDraft,

        [Parameter()]
        [System.String]
        $AssetName,

        [Parameter()]
        [System.Security.SecureString]
        $Token
    )

    $getGitHubReleaseParameters = @{} + $PSBoundParameters
    $getGitHubReleaseParameters.Remove('AssetName')

    $release = Get-GitHubRelease @getGitHubReleaseParameters

    if (-not $release)
    {
        return $null
    }

    Write-Verbose -Message ($script:localizedData.Get_GitHubReleaseAsset_FoundRelease -f ($release.name -join ', '))

    if ($AssetName)
    {
        # Find the requested asset using wildcard matching
        $matchingAssets = $release.assets |
            Where-Object -FilterScript {
                $_.name -like $AssetName
            }

        if (-not $matchingAssets -or $matchingAssets.Count -eq 0)
        {
            $writeErrorParameters = @{
                Message      = $script:localizedData.Get_GitHubReleaseAsset_MissingAssetName -f $_.Exception.Message
                Category     = 'ObjectNotFound'
                ErrorId      = 'GGHRAM0001' # cSpell: disable-line
                TargetObject = $AssetName
            }

            Write-Error @writeErrorParameters

            return $null
        }
    }
    else
    {
        $matchingAssets = $release.assets
    }

    Write-Verbose -Message (
        $script:localizedData.Get_GitHubReleaseAsset_FoundAsset -f ($matchingAssets.name -join ', ')
    )

    return $matchingAssets
}
#EndRegion '.\Public\Get-GitHubReleaseAsset.ps1' 150