Shared/Get-GitHubRepoTag.ps1

function Get-GitHubRepoTag {
    <#
        .SYNOPSIS
            Calls the GitHub Tags API passed via $Uri and returns the tags for the repository
            Example: https://api.github.com/repos/PowerShell/PowerShell/tags
    #>

    [OutputType([System.Management.Automation.PSObject])]
    [CmdletBinding(SupportsShouldProcess = $false)]
    param (
        [Parameter(Mandatory = $true, Position = 0)]
        [ValidateScript( {
                if ($_ -match "^(https://api\.github\.com/repos/)([a-zA-Z0-9_-]+)/([a-zA-Z0-9_-]+)(/tags)") {
                    $true
                }
                else {
                    throw "'$_' must be in the format 'https://api.github.com/repos/user/tags'. Replace 'user' with the user or organisation and 'repository' with the target repository name."
                }
            })]
        [System.String] $Uri,

        [Parameter(Mandatory = $false, Position = 1)]
        [ValidateNotNullOrEmpty()]
        [System.String] $MatchVersion = "(\d+(\.\d+){1,4}).*"
    )

    begin {
        $RateLimit = Get-GitHubRateLimit
    }

    process {
        if ($RateLimit.remaining -eq 0) {
            # We're rate limited, so output a special object
            [PSCustomObject] @{
                Version = "RateLimited"
                URI     = "https://docs.github.com/en/rest/overview/resources-in-the-rest-api#rate-limiting"
            } | Write-Output
        }
        else {
            try {
                # Retrieve the releases from the GitHub API
                # Use TLS for connections
                Write-Verbose -Message "$($MyInvocation.MyCommand): Set TLS to 1.2."
                [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072

                # Invoke the GitHub releases REST API
                # Note that the API performs rate limiting.
                # https://docs.github.com/en/free-pro-team@latest/rest/reference/repos#get-the-latest-release
                $params = @{
                    ContentType        = "application/vnd.github.v3+json"
                    ErrorAction        = "Stop"
                    MaximumRedirection = 0
                    DisableKeepAlive   = $true
                    UseBasicParsing    = $true
                    UserAgent          = "github-aaronparker-evergreen"
                    Uri                = $Uri
                }
                if (Test-ProxyEnv) {
                    $params.Proxy = $script:EvergreenProxy
                }
                if (Test-ProxyEnv -Creds) {
                    $params.ProxyCredential = $script:EvergreenProxyCreds
                }
                # If GITHUB_TOKEN or GH_TOKEN exists, let's add that to the API request
                if (Test-Path -Path "env:GITHUB_TOKEN") {
                    $params.Headers = @{ Authorization = "token $env:GITHUB_TOKEN" }
                }
                elseif (Test-Path -Path "env:GH_TOKEN") {
                    $params.Headers = @{ Authorization = "token $env:GH_TOKEN" }
                }

                # Output the parameters when using -Verbose
                foreach ($tag in $params.GetEnumerator()) {
                    Write-Verbose -Message "$($MyInvocation.MyCommand): Invoke-WebRequest parameter: $($tag.name): $($tag.value)."
                }

                Write-Verbose -Message "$($MyInvocation.MyCommand): Get GitHub release from: $Uri."
                $Tags = Invoke-RestMethod @params
            }
            catch {
                throw $_
            }

            if ($null -eq $script:resourceStrings.Properties.GitHubTags) {
                Write-Warning -Message "$($MyInvocation.MyCommand): Unable to validate tag against GitHub tags property object because we can't find the module resource."
            }
            else {
                # Validate that $tags has the expected properties
                Write-Verbose -Message "$($MyInvocation.MyCommand): Validating GitHub tag object."
                foreach ($tag in $Tags) {

                    # Compare the GitHub release object with properties that we expect
                    $params = @{
                        ReferenceObject  = $script:resourceStrings.Properties.GitHubTags
                        DifferenceObject = (Get-Member -InputObject $tag -MemberType NoteProperty)
                        PassThru         = $true
                        ErrorAction      = "Continue"
                    }
                    $missingProperties = Compare-Object @params

                    # Throw an error for missing properties
                    if ($null -ne $missingProperties) {
                        Write-Verbose -Message "$($MyInvocation.MyCommand): Validated tag object successfully."
                    }
                    else {
                        Write-Verbose -Message "$($MyInvocation.MyCommand): Validation failed."
                        $missingProperties | ForEach-Object {
                            throw [System.Management.Automation.ValidationMetadataException]::New("$($MyInvocation.MyCommand): Property: '$_' missing")
                        }
                    }
                }
            }

            # Build and array of the latest release and download URLs
            Write-Verbose -Message "$($MyInvocation.MyCommand): Found $($tags.count) tags."
            foreach ($Tag in $Tags) {

                try {
                    # Uri matches tags for the repo; find the latest tag
                    $Version = [RegEx]::Match($Tag.name, $MatchVersion).Captures.Groups[1].Value
                }
                catch {
                    Write-Verbose -Message "$($MyInvocation.MyCommand): Failed to match version number, returning as-is: $($Tag.name)."
                    $Version = $Tag.name
                }

                # Output the tags object
                [PSCustomObject]@{
                    Tag = $Version
                } | Write-Output
            }
        }
    }
}