GitHub.psm1

#region - From private/common.ps1
$script:APIBaseURI = 'https://api.github.com'
$script:Owner = ''
$script:Repo = ''
$script:Token = ''
$Script:Version = '2022-11-28'
$script:ContentType = 'application/vnd.github+json'
#endregion - From private/common.ps1

#region - From private/Initialize-SecretVault.ps1
#Requires -Version 7.0
#Requires -Modules Microsoft.PowerShell.SecretManagement
#Requires -Modules Microsoft.PowerShell.SecretStore

function Initialize-SecretVault {
    [CmdletBinding()]
    param (
        [Parameter()]
        [string] $Name = 'SecretStore',

        [Parameter()]
        [Alias('ModuleName')]
        [string] $Type = 'Microsoft.PowerShell.SecretStore'
    )

    $secretVault = Get-SecretVault | Where-Object { $_.ModuleName -eq $Type }
    $secretVaultExists = $secretVault.count -ne 0
    Write-Verbose "A $Name exists: $secretVaultExists"
    if (-not $secretVaultExists) {
        Write-Verbose "Registering [$Name]"
        $vaultParameters = @{
            Authentication  = 'None'
            PasswordTimeout = -1
            Interaction     = 'None'
            Scope           = 'CurrentUser'
            WarningAction   = 'SilentlyContinue'
            Confirm         = $false
            Force           = $true
        }
        Reset-SecretStore @vaultParameters

        $secretVault = @{
            Name         = $Name
            ModuleName   = $Type
            DefaultVault = $true
            Description  = 'SecretStore'
        }
        Register-SecretVault @secretVault
    }
}
#endregion - From private/Initialize-SecretVault.ps1

#region - From private/Auth/DeviceFlow/Invoke-GitHubDeviceCodeLogin.ps1
function Invoke-GitHubDeviceCodeLogin {
    <#
        .SYNOPSIS
        Starts the GitHub Device Flow login process.

        .DESCRIPTION
        Starts the GitHub Device Flow login process. This will prompt the user to visit a URL and enter a code.

        .EXAMPLE
        Invoke-GitHubDeviceCodeLogin

        This will start the GitHub Device Flow login process.
        The user gets prompted to visit a URL and enter a code.

        .NOTES
        For more info about the Device Flow visit:
        https://docs.github.com/en/apps/creating-github-apps/writing-code-for-a-github-app/building-a-cli-with-a-github-app
    #>

    [OutputType([void])]
    [CmdletBinding()]
    param(
        # The Client ID of the GitHub App.
        [Parameter()]
        [string] $ClientID = 'Iv1.f26b61bc99e69405'
    )

    $deviceCodeResponse = Request-GitHubDeviceCode -ClientID $ClientID

    $deviceCode = $deviceCodeResponse.device_code
    $interval = $deviceCodeResponse.interval
    $userCode = $deviceCodeResponse.user_code
    $verificationUri = $deviceCodeResponse.verification_uri

    Write-Host "Please visit: $verificationUri"
    Write-Host "and enter code: $userCode"

    $tokenResponse = Wait-GitHubToken -DeviceCode $deviceCode -ClientID $ClientID -Interval $interval

    Write-Host 'Successfully authenticated!'
    Write-Host ""

    $tokenResponse
}
#endregion - From private/Auth/DeviceFlow/Invoke-GitHubDeviceCodeLogin.ps1

#region - From private/Auth/DeviceFlow/Request-GitHubDeviceCode.ps1
function Request-GitHubDeviceCode {
    <#
        .SYNOPSIS
        Request a GitHub Device Code.

        .DESCRIPTION
        Request a GitHub Device Code.

        .EXAMPLE
        Request-GitHubDeviceCode -ClientID $ClientID

        This will request a GitHub Device Code.

        .NOTES
        For more info about the Device Flow visit:
        https://docs.github.com/en/apps/creating-github-apps/writing-code-for-a-github-app/building-a-cli-with-a-github-app
    #>

    [OutputType([PSCustomObject])]
    [CmdletBinding()]
    param(
        # The Client ID of the GitHub App.
        [Parameter()]
        [string] $ClientID
    )
    $RESTParams = @{
        Uri     = 'https://github.com/login/device/code'
        Method  = 'POST'
        Body    = @{ 'client_id' = $ClientID }
        Headers = @{ 'Accept' = 'application/json' }
    }
    try {
        Write-Verbose ($RESTParams.GetEnumerator() | Out-String)

        $deviceCodeResponse = Invoke-RestMethod @RESTParams -Verbose:$false
        return $deviceCodeResponse
    } catch {
        Write-Error $_.Exception.Message
        throw $_
    }
}

#endregion - From private/Auth/DeviceFlow/Request-GitHubDeviceCode.ps1

#region - From private/Auth/DeviceFlow/Request-GitHubToken.ps1
function Request-GitHubToken {
    <#
        .SYNOPSIS
        Request a GitHub token using the Device Flow.

        .DESCRIPTION
        Request a GitHub token using the Device Flow.
        This will poll the GitHub API until the user has entered the code.

        .EXAMPLE
        Request-GitHubToken -DeviceCode $deviceCode -ClientID $ClientID

        This will poll the GitHub API until the user has entered the code.

        .NOTES
        For more info about the Device Flow visit:
        https://docs.github.com/en/apps/creating-github-apps/writing-code-for-a-github-app/building-a-cli-with-a-github-app
    #>

    [OutputType([PSCustomObject])]
    [CmdletBinding()]
    param(
        # The `device_code` used to request the access token.
        [Parameter(Mandatory)]
        [string] $DeviceCode,

        # The Client ID of the GitHub App.
        [Parameter()]
        [string] $ClientID,

        # The refresh token used create a new access token.
        [Parameter()]
        [string] $RefreshToken
    )
    
    $RESTParams = @{
        Uri     = 'https://github.com/login/oauth/access_token'
        Method  = 'POST'
        Body    = @{
            'client_id'   = $ClientID
            'device_code' = $DeviceCode
            'grant_type'  = 'urn:ietf:params:oauth:grant-type:device_code'
        }
        Headers = @{ 'Accept' = 'application/json' }
    }

    try {
        Write-Verbose ($RESTParams.GetEnumerator() | Out-String)

        $tokenResponse = Invoke-RestMethod @RESTParams -Verbose:$false
        return $tokenResponse
    } catch {
        Write-Error $_.Exception.Message
        throw $_
    }
}
#endregion - From private/Auth/DeviceFlow/Request-GitHubToken.ps1

#region - From private/Auth/DeviceFlow/Wait-GitHubToken.ps1

function Wait-GitHubToken {
    <#
        .SYNOPSIS
        Waits for the GitHub Device Flow to complete.

        .DESCRIPTION
        Waits for the GitHub Device Flow to complete.
        This will poll the GitHub API until the user has entered the code.

        .EXAMPLE
        Wait-GitHubToken -DeviceCode $deviceCode -ClientID $ClientID -Interval $interval

        This will poll the GitHub API until the user has entered the code.

        .NOTES
        For more info about the Device Flow visit:
        https://docs.github.com/en/apps/creating-github-apps/writing-code-for-a-github-app/building-a-cli-with-a-github-app
    #>

    [OutputType([PSCustomObject])]
    [CmdletBinding()]
    param(
        # The `device_code` used to request the token.
        [Parameter(Mandatory)]
        [string] $DeviceCode,

        # The Client ID of the GitHub App.
        [Parameter()]
        [string] $ClientID,

        # The interval to wait between polling for the token.
        [Parameter()]
        [int] $Interval = 5
    )

    do {
        $response = Request-GitHubToken -DeviceCode $DeviceCode -ClientID $ClientID
        if ($response.error) {
            switch ($response.error) {
                'authorization_pending' {
                    # The user has not yet entered the code.
                    # Wait, then poll again.
                    Write-Verbose $response.error_description
                    Start-Sleep -Seconds $interval
                    continue
                }
                'slow_down' {
                    # The app polled too fast.
                    # Wait for the interval plus 5 seconds, then poll again.
                    Write-Verbose $response.error_description
                    Start-Sleep -Seconds ($interval + 5)
                    continue
                }
                'expired_token' {
                    # The `device_code` expired, and the process needs to restart.
                    Write-Error $response.error_description
                    exit 1
                }
                'access_denied' {
                    # The user cancelled the process. Stop polling.
                    Write-Error $response.error_description
                    exit 1
                }
                default {
                    # The response contains an access token. Stop polling.
                    Write-Error 'Unknown error:'
                    Write-Error $response.error
                    Write-Error $response.error_description
                    Write-Error $response.error_uri
                    break
                }
            }
        }
    } until ($response.access_token)
    $response
}
#endregion - From private/Auth/DeviceFlow/Wait-GitHubToken.ps1

#region - From public/loader.ps1

Initialize-SecretVault
$secrets = Get-SecretInfo -Vault 'SecretStore'

if ([string]::IsNullOrEmpty($script:Token)) {
    $script:Token = $env:GH_TOKEN
}
if ([string]::IsNullOrEmpty($script:Token)) {
    $script:Token = $env:GITHUB_TOKEN
}
if (([string]::IsNullOrEmpty($Script:Token)) -and ('GitHub.Token' -in $secrets.name)) {
    $script:Token = Get-Secret -Name 'GitHub.Token' -AsPlainText
}

if (([string]::IsNullOrEmpty($script:Owner)) -and ('GitHub.Owner' -in $secrets.name)) {
    $script:Owner = Get-Secret -Name 'GitHub.Owner' -AsPlainText
}

if (([string]::IsNullOrEmpty($script:Repo)) -and ('GitHub.Repo' -in $secrets.name)) {
    $script:Repo = Get-Secret -Name 'GitHub.Repo' -AsPlainText
}

if (([string]::IsNullOrEmpty($script:APIBaseURI)) -and ('GitHub.APIBaseURI' -in $secrets.name)) {
    $script:APIBaseURI = Get-Secret -Name 'GitHub.APIBaseURI' -AsPlainText
}

if (([string]::IsNullOrEmpty($script:Version)) -and ('GitHub.Version' -in $secrets.name)) {
    $script:Version = Get-Secret -Name 'GitHub.Version' -AsPlainText
}
#endregion - From public/loader.ps1

#region - From public/Branches/Get-GitHubRepoBranch.ps1
function Get-GitHubRepoBranch {
    [CmdletBinding()]
    param (
        [Parameter()]
        [string] $Owner = $script:Owner,

        [Parameter()]
        [string] $Repo = $script:Repo,

        [Parameter()]
        [string] $Token = $script:Token
    )

    $InputObject = @{
        Token       = $Token
        Method      = 'Get'
        APIEndpoint = "/repos/$Owner/$Repo/branches"
    }

    $Response = Invoke-GitHubAPI @InputObject

    return $Response
}
#endregion - From public/Branches/Get-GitHubRepoBranch.ps1

#region - From public/Core/Connect-GitHubAccount.ps1
function Connect-GitHubAccount {
    <#
        .SYNOPSIS
        Connects to GitHub using a personal access token or device code login.

        .DESCRIPTION
        Connects to GitHub using a personal access token or device code login.

        .EXAMPLE
        Connect-GitHubAccount -Owner 'MyOrg' -Repo 'MyRepo' -Token 'ghp_####'

        Connects to GitHub using a personal access token and sets the Owner and Repo default variables.

        .EXAMPLE
        Connect-GitHubAccount -Owner 'MyOrg' -Repo 'MyRepo'

        Sets the Owner and Repo default variables.

        .EXAMPLE
        Connect-GitHubAccount

        Connects to GitHub using a device code login.

        .NOTES
        https://docs.github.com/en/rest/overview/other-authentication-methods#authenticating-for-saml-sso
    #>

    [CmdletBinding()]
    param (
        [Parameter()]
        [String] $Owner,

        [Parameter()]
        [String] $Repo,

        [Parameter()]
        [String] $Token,

        [Parameter()]
        [String] $APIBaseURI = 'https://api.github.com'
    )

    $Vault = Get-SecretVault | Where-Object -Property ModuleName -EQ 'Microsoft.PowerShell.SecretStore'

    if ($PSBoundParameters.ContainsKey('Token')) {
        Set-GithubConfig -Token $Token
        # $tokenType = 'token'
    } else {
        $tokenResponse = Invoke-GitHubDeviceCodeLogin
        $script:Token = $tokenResponse.access_token                                 # ghu_####
        $tokenExpiresIn = $tokenResponse.expires_in                                 # 28800 = 8 hours
        $tokenExpirationDate = (Get-Date).AddSeconds($tokenExpiresIn)               # 2021-09-28T21:00:00.0000000-04:00
        $refreshToken = $tokenResponse.refresh_token                                # ghr_########
        $refreshTokenExpiresIn = $tokenResponse.refresh_token_expires_in            # 15724800 = 6 months
        $refreshTokenExpirationDate = (Get-Date).AddSeconds($refreshTokenExpiresIn) # 2022-03-28T21:00:00.0000000-04:00
        # $tokenType = $tokenResponse.token_type # bearer
        # $tokenScope = $tokenResponse.scope
        Set-GithubConfig -Token $Token
        Set-Secret -Name 'GitHub.access_token' -Secret $script:Token -Vault $Vault.Name
        Set-Secret -Name 'GitHub.access_token.expirationDate' -Secret $tokenExpirationDate -Vault $Vault.Name
        Set-Secret -Name 'GitHub.refresh_token' -Secret $refreshToken -Vault $Vault.Name
        Set-Secret -Name 'GitHub.refresh_token.expirationDate' -Secret $refreshTokenExpirationDate -Vault $Vault.Name
    }

    if ($PSBoundParameters.ContainsKey('Owner')) {
        Set-GitHubConfig -Owner $Owner
    }

    if ($PSBoundParameters.ContainsKey('Repo')) {
        Set-GitHubConfig -Repo $Repo
    }

    if ($PSBoundParameters.ContainsKey('APIBaseURI')) {
        Set-GitHubConfig -APIBaseURI $APIBaseURI
    }

    if ($PSBoundParameters.ContainsKey('Version')) {
        Set-GitHubConfig -Version $Version
    }

    $user = Get-GitHubUser

    Write-Host "Logged in as $($user.name) (@$($user.login))!"
}
#endregion - From public/Core/Connect-GitHubAccount.ps1

#region - From public/Core/Invoke-GitHubAPI.ps1
function Invoke-GitHubAPI {
    [CmdletBinding(DefaultParameterSetName = 'Body')]
    param (
        [Parameter()]
        [ValidateSet('GET', 'POST', 'PATCH', 'DELETE', 'PUT')]
        [String] $Method = 'GET',

        [Parameter()]
        [string] $APIEndpoint,

        [Parameter(ParameterSetName = 'Body')]
        [hashtable] $Body = @{},

        [Parameter(ParameterSetName = 'Data')]
        [string] $Data = '',

        [Parameter()]
        [string] $Token = $script:Token,

        [Parameter()]
        [string] $ContentType = $script:ContentType,

        [Parameter()]
        [string] $Version = $script:Version,

        [Parameter()]
        [switch] $UseWebRequest
    )

    if ([string]::IsNullOrEmpty($Token)) {
        throw 'Token is required'
    }

    if ($Token.StartsWith('ghp_')) {
        $authorization = "token $Token"
    }
    if ($Token.StartsWith('ghu_')) {
        $authorization = "Bearer $Token"
    }

    $APICall = @{
        Uri     = "$script:APIBaseURI$($APIEndpoint.Replace('\', '/').Replace('//', '/'))"
        Method  = $Method
        Body    = [string]::IsNullOrEmpty($Data) ? ($Body | ConvertTo-Json -Depth 100) : $Data
        Headers = @{
            Authorization          = $authorization
            'Content-Type'         = $ContentType
            'X-GitHub-Api-Version' = $Version
        }
    }
    try {
        Write-Verbose ($APICall.GetEnumerator() | Out-String)

        if ($UseWebRequest) {
            return Invoke-WebRequest @APICall
        }

        Invoke-RestMethod @APICall
    } catch {
        Write-Error $_.Exception.Message
        throw $_
    }
}
#endregion - From public/Core/Invoke-GitHubAPI.ps1

#region - From public/Core/Set-GitHubConfig.ps1
function Set-GitHubConfig {
    [CmdletBinding()]
    param (
        [Parameter()]
        [String] $Owner,

        [Parameter()]
        [String] $Repo,

        [Parameter()]
        [String] $Token,

        [Parameter()]
        [String] $APIBaseURI = 'https://api.github.com',

        [Parameter()]
        [string] $Version = '2022-11-28'
    )

    $Vault = Get-SecretVault | Where-Object -Property ModuleName -EQ 'Microsoft.PowerShell.SecretStore'

    if ($PSBoundParameters.ContainsKey('Token')) {
        $script:Token = $Token
        Set-Secret -Name 'GitHub.access_token' -Secret $script:Token -Vault $Vault.Name
        Remove-Secret -Name 'GitHub.access_token.expirationDate' -Vault $Vault.Name
        Remove-Secret -Name 'GitHub.refresh_token' -Vault $Vault.Name
        Remove-Secret -Name 'GitHub.refresh_token.expirationDate' -Vault $Vault.Name
    }

    if ($PSBoundParameters.ContainsKey('Owner')) {
        $script:Owner = $Owner
        Set-Secret -Name 'GitHub.Owner' -Secret $script:Owner -Vault $Vault.Name
    }

    if ($PSBoundParameters.ContainsKey('Repo')) {
        $script:Repo = $Repo
        Set-Secret -Name 'GitHub.Repo' -Secret $script:Repo -Vault $Vault.Name
    }

    if ($PSBoundParameters.ContainsKey('APIBaseURI')) {
        $script:APIBaseURI = $APIBaseURI
        Set-Secret -Name 'GitHub.APIBaseURI' -Secret $script:APIBaseURI -Vault $Vault.Name
    }

    if ($PSBoundParameters.ContainsKey('Version')) {
        $script:Version = $Version
        Set-Secret -Name 'GitHub.Version' -Secret $script:Version -Vault $Vault.Name
    }
}
#endregion - From public/Core/Set-GitHubConfig.ps1

#region - From public/Users/Get-GitHubUser.ps1
function Get-GitHubUser {
    <#
        .SYNOPSIS
        Get the authenticated user

        .DESCRIPTION
        If the authenticated user is authenticated through basic authentication or OAuth with the user scope, then the response lists public and private profile information.
        If the authenticated user is authenticated through OAuth without the user scope, then the response lists only public profile information.

        .PARAMETER Token
        Parameter description

        .EXAMPLE
        An example

        .NOTES
        https://docs.github.com/en/rest/users/users#get-the-authenticated-user
    #>

    [CmdletBinding()]
    [Alias('Get-GitHubContext')]
    param (
        [Parameter()]
        [string] $Token = $script:Token
    )

    $InputObject = @{
        APIEndpoint = '/user'
        Method      = 'GET'
        Token       = $Token
    }

    $Response = Invoke-GitHubAPI @InputObject

    $Response
}
#endregion - From public/Users/Get-GitHubUser.ps1

#region - From public/Users/Set-GitHubUser.ps1
function Set-GitHubUser {
    <#
        .NOTES
        https://docs.github.com/en/rest/users/users#update-the-authenticated-user
    #>

    [CmdletBinding()]
    [Alias('Update-GitHubUser')]
    param (
        # The new name of the user.
        [Parameter()]
        [string] $Name,

        # The publicly visible email address of the user.
        [Parameter()]
        [string] $Email,

        # The new blog URL of the user.
        [Parameter()]
        [string] $Blog,

        # The new Twitter username of the user.
        [Parameter()]
        [string] $TwitterUsername,

        # The new company of the user.
        [Parameter()]
        [string] $Company,

        # The new location of the user.
        [Parameter()]
        [string] $Location,

        # The new hiring availability of the user.
        [Parameter()]
        [boolean] $Hireable,

        # The new short biography of the user.
        [Parameter()]
        [string] $Bio,

        [Parameter()]
        [string] $Token = $script:Token
    )

    $Body = @{}

    $PSBoundParameters['Name'] ? ($Body.Name = $Name) : $null
    $PSBoundParameters['Email'] ? ($Body.Email = $Email) : $null
    $PSBoundParameters['Blog'] ? ($Body.Blog = $Blog) : $null
    $PSBoundParameters['TwitterUsername'] ? ($Body.TwitterUsername = $TwitterUsername) : $null
    $PSBoundParameters['Company'] ? ($Body.Company = $Company) : $null
    $PSBoundParameters['Location'] ? ($Body.Location = $Location) : $null
    $PSBoundParameters['Hireable'] ? ($Body.Hireable = $Hireable) : $null
    $PSBoundParameters['Bio'] ? ($Body.Bio = $Bio) : $null

    $InputObject = @{
        APIEndpoint = '/user'
        Body        = $Body
        Method      = 'PATCH'
        Token       = $Token
    }

    $Response = Invoke-GitHubAPI @InputObject

    $Response
}
#endregion - From public/Users/Set-GitHubUser.ps1

#region - From public/Actions/Disable-GitHubWorkflow.ps1
Function Disable-GitHubWorkflow {
    <#
        .NOTES
        https://docs.github.com/en/rest/reference/actions#disable-a-workflow
    #>

    [CmdletBinding()]
    param (
        [Parameter()]
        [string] $Owner = $script:Owner,

        [Parameter()]
        [string] $Repo = $script:Repo,

        [Parameter()]
        [string] $Token = $script:Token,

        [Parameter(
            Mandatory,
            ValueFromPipelineByPropertyName
        )]
        [string[]] $ID
    )

    begin {}

    process {
        $InputObject = @{
            Token       = $Token
            Method      = 'PUT'
            APIEndpoint = "/repos/$Owner/$Repo/actions/workflows/$ID/disable"
        }

        $Response = Invoke-GitHubAPI @InputObject

        $Response
    }

    end {}
}
#endregion - From public/Actions/Disable-GitHubWorkflow.ps1

#region - From public/Actions/Enable-GitHubWorkflow.ps1
Function Enable-GitHubWorkflow {
    <#
        .NOTES
        https://docs.github.com/en/rest/reference/actions#enable-a-workflow
    #>

    [CmdletBinding()]
    param (
        [Parameter()]
        [string] $Owner = $script:Owner,

        [Parameter()]
        [string] $Repo = $script:Repo,

        [Parameter()]
        [string] $Token = $script:Token,

        [Parameter(
            Mandatory,
            ValueFromPipelineByPropertyName
        )]
        [string[]] $ID
    )

    begin {}

    process {
        $InputObject = @{
            Token       = $Token
            Method      = 'PUT'
            APIEndpoint = "/repos/$Owner/$Repo/actions/workflows/$ID/enable"
        }

        $Response = Invoke-GitHubAPI @InputObject

        $Response
    }

    end {}
}
#endregion - From public/Actions/Enable-GitHubWorkflow.ps1

#region - From public/Actions/Get-GitHubWorkflow.ps1
function Get-GitHubWorkflow {
    <#
        .NOTES
        https://docs.github.com/en/rest/reference/actions#list-repository-workflows
    #>

    [CmdletBinding(DefaultParameterSetName = 'ByName')]
    param (
        [Parameter()]
        [string] $Owner = $script:Owner,

        [Parameter()]
        [string] $Repo = $script:Repo,

        [Parameter()]
        [string] $Token = $script:Token,

        [Parameter(ParameterSetName = 'ByName')]
        [string] $Name,

        [Parameter(ParameterSetName = 'ByID')]
        [string] $ID,

        [Parameter()]
        [int] $PageSize = 100
    )

    $processedPages = 0
    $workflows = @()
    do {
        $processedPages++
        $InputObject = @{
            Token       = $Token
            Method      = 'GET'
            APIEndpoint = "/repos/$Owner/$Repo/actions/workflows?per_page=$PageSize&page=$processedPages"
        }
        $Response = Invoke-GitHubAPI @InputObject -Verbose
        $workflows += $Response.workflows | Where-Object name -Match $name | Where-Object id -Match $id
    } while ($workflows.count -ne $Response.total_count)
    $workflows
}
#endregion - From public/Actions/Get-GitHubWorkflow.ps1

#region - From public/Actions/Get-GitHubWorkflowRun.ps1
Function Get-GitHubWorkflowRun {
    <#
        .NOTES
        https://docs.github.com/en/rest/reference/actions#list-workflow-runs-for-a-repository
    #>

    [CmdletBinding()]
    param (
        [Parameter()]
        [string] $Owner = $script:Owner,

        [Parameter()]
        [string] $Repo = $script:Repo,

        [Parameter()]
        [string] $Token = $script:Token,

        [Parameter(ParameterSetName = 'ByName')]
        [string] $Name,

        [Parameter(ParameterSetName = 'ByID')]
        [string] $ID,

        [Parameter()]
        [int] $PageSize = 100
    )

    $processedPages = 0
    $workflowRuns = @()
    do {
        $processedPages++
        $InputObject = @{
            Token       = $Token
            Method      = 'GET'
            APIEndpoint = "/repos/$Owner/$Repo/actions/runs?per_page=$PageSize&page=$processedPages"
        }
        $Response = Invoke-GitHubAPI @InputObject
        $workflowRuns += $Response.workflows | Where-Object name -Match $name | Where-Object id -Match $id
    } until ($workflowRuns.count -eq $Response.total_count)
    $workflowRuns


    do {
        $WorkflowRuns = $Response.workflow_runs
        $Results += $WorkflowRuns
    } while ($WorkflowRuns.count -eq 100)
    return $Results | Where-Object Name -Match $Name | Where-Object workflow_id -Match $ID
}
#endregion - From public/Actions/Get-GitHubWorkflowRun.ps1

#region - From public/Actions/Get-GitHubWorkflowUsage.ps1
Function Get-GitHubWorkflowUsage {
    [CmdletBinding(
        DefaultParameterSetName = 'ByName'
    )]
    param (
        [Parameter()]
        [string] $Owner = $script:Owner,

        [Parameter()]
        [string] $Repo = $script:Repo,

        [Parameter()]
        [string] $Token = $script:Token,
        
        [Parameter(
            Mandatory,
            ValueFromPipelineByPropertyName
        )]
        [string[]] $ID
    )

    begin {}
    process {
        # API Reference
        # https://docs.github.com/en/rest/reference/actions#get-workflow-usage
        $APICall = @{
            Uri     = "$APIBaseURI/repos/$Owner/$Repo/actions/workflows/$ID/timing"
            Headers = @{
                Authorization  = "token $Token"
                'Content-Type' = 'application/json'
            }
            Method  = 'GET'
            Body    = @{} | ConvertTo-Json -Depth 100
        }
        try {
            if ($PSBoundParameters.ContainsKey('Verbose')) {
                $APICall
            }
            $Response = Invoke-RestMethod @APICall
        } catch {
            throw $_
        }
        return $Response.billable
    }
    end {}
}
#endregion - From public/Actions/Get-GitHubWorkflowUsage.ps1

#region - From public/Actions/Remove-GitHubWorkflowRun.ps1
function Remove-GitHubWorkflowRun {
    [CmdletBinding()]
    param (
        [Parameter()]
        [string] $Owner = $script:Owner,

        [Parameter()]
        [string] $Repo = $script:Repo,

        [Parameter()]
        [string] $Token = $script:Token,

        [Parameter(
            Mandatory,
            ValueFromPipelineByPropertyName
        )]
        [string] $ID
    )

    begin {}

    process {
        $InputObject = @{
            APIEndpoint = "repos/$Owner/$Repo/actions/runs/$ID"
            Method      = 'DELETE'
            Token       = $Token
        }

        $Response = Invoke-GitHubAPI @InputObject

        $Response
    }

    end {}
}
#endregion - From public/Actions/Remove-GitHubWorkflowRun.ps1

#region - From public/Actions/Start-GitHubWorkflow.ps1


<#
.SYNOPSIS
#

.DESCRIPTION
Long description

.PARAMETER Owner
Parameter description

.PARAMETER Repo
Parameter description

.PARAMETER Token
Parameter description

.PARAMETER ID
Parameter description

.PARAMETER Ref
Parameter description

.PARAMETER Inputs
Parameter description

.EXAMPLE
Get-GitHubWorkflow | Where-Object name -NotLike '.*' | Start-GitHubWorkflow -Inputs @{
    staticValidation = $true
    deploymentValidation = $false
    removeDeployment = $true
    prerelease = $false
}



.NOTES
General notes
#>

function Start-GitHubWorkflow {
    [CmdletBinding()]
    param (
        [Parameter()]
        [string] $Owner = $script:Owner,

        [Parameter()]
        [string] $Repo = $script:Repo,

        [Parameter()]
        [string] $Token = $script:Token,
        
        [Alias('workflow_id')]
        [Parameter(
            Mandatory,
            ValueFromPipelineByPropertyName
        )]
        [string] $ID,

        [Parameter(
            ValueFromPipelineByPropertyName
        )]
        [Alias('branch', 'tag')]
        [string] $Ref = 'main',

        [Parameter()]
        [hashtable] $Inputs = @{}
    )

    begin {}

    process {
        # API Reference
        # https://docs.github.com/en/free-pro-team@latest/rest/actions/workflows?apiVersion=2022-11-28#create-a-workflow-dispatch-event
        $APICall = @{
            Uri     = "$APIBaseURI/repos/$Owner/$Repo/actions/workflows/$ID/dispatches"
            Headers = @{
                Authorization  = "token $Token"
                'Content-Type' = 'application/json'
            }
            Method  = 'POST'
            Body    = @{
                ref    = $Ref
                inputs = $Inputs
            } | ConvertTo-Json -Depth 100
        }
        try {
            if ($PSBoundParameters.ContainsKey('Verbose')) {
                $APICall
            }
            $Response = Invoke-RestMethod @APICall
        } catch {
            throw $_
        }
        return $Response
    }

    end {}
}
#endregion - From public/Actions/Start-GitHubWorkflow.ps1

#region - From public/Actions/Start-GitHubWorkflowReRun.ps1
function Start-GitHubWorkflowReRun {
    [CmdletBinding()]
    param (
        [Parameter()]
        [string] $Owner = $script:Owner,

        [Parameter()]
        [string] $Repo = $script:Repo,

        [Parameter()]
        [string] $Token = $script:Token,

        [Alias('workflow_id')]
        [Parameter(
            Mandatory,
            ValueFromPipelineByPropertyName
        )]
        [string] $ID
    )

    begin {}

    process {
        # API Reference
        # https://docs.github.com/en/rest/reference/actions#re-run-a-workflow
        $APICall = @{
            Uri     = "$APIBaseURI/repos/$Owner/$Repo/actions/runs/$ID/rerun"
            Headers = @{
                Authorization  = "token $Token"
                'Content-Type' = 'application/json'
            }
            Method  = 'POST'
            Body    = @{} | ConvertTo-Json -Depth 100
        }
        try {
            if ($PSBoundParameters.ContainsKey('Verbose')) {
                $APICall
            }
            $Response = Invoke-RestMethod @APICall
        } catch {
            throw $_
        }
        return $Response
    }

    end {}
}
#endregion - From public/Actions/Start-GitHubWorkflowReRun.ps1

#region - From public/Actions/Stop-GitHubWorkflowRun.ps1
function Stop-GitHubWorkflowRun {
    [CmdletBinding()]
    [alias('Cancel-GitHubWorkflowRun')]
    param (
        [Parameter()]
        [string] $Owner = $script:Owner,

        [Parameter()]
        [string] $Repo = $script:Repo,

        [Parameter()]
        [string] $Token = $script:Token,

        [Alias('workflow_id')]
        [Parameter(
            Mandatory,
            ValueFromPipelineByPropertyName
        )]
        [string] $ID
    )

    begin {}

    process {
        # API Reference
        # https://docs.github.com/en/rest/reference/actions#cancel-a-workflow-run
        $APICall = @{
            Uri     = "$APIBaseURI/repos/$Owner/$Repo/actions/runs/$ID/cancel"
            Headers = @{
                Authorization  = "token $Token"
                'Content-Type' = 'application/json'
            }
            Method  = 'POST'
            Body    = @{} | ConvertTo-Json -Depth 100
        }
        try {
            if ($PSBoundParameters.ContainsKey('Verbose')) {
                $APICall
            }
            $Response = Invoke-RestMethod @APICall
        } catch {
            throw $_
        }
        return $Response
    }
    
    end {}
}
#endregion - From public/Actions/Stop-GitHubWorkflowRun.ps1

#region - From public/Meta/Get-GitHubAPIVersions.ps1
function Get-GitHubAPIVersions {
    <#
        .NOTES
        https://docs.github.com/en/rest/meta/meta?apiVersion=2022-11-28#get-all-api-versions
    #>

    [CmdletBinding()]
    [OutputType([string[]])]
    param ()

    $InputObject = @{
        APIEndpoint = '/versions'
        Method      = 'GET'
    }

    $Response = Invoke-GitHubAPI @InputObject

    $Response
}
#endregion - From public/Meta/Get-GitHubAPIVersions.ps1

#region - From public/Meta/Get-GitHubMeta.ps1
function Get-GitHubMeta {
    <#
        .NOTES
        https://docs.github.com/en/rest/reference/meta#github-api-root
    #>

    [CmdletBinding()]
    param ()

    $InputObject = @{
        APIEndpoint = '/meta'
        Method      = 'GET'
    }

    $Response = Invoke-GitHubAPI @InputObject

    $Response
}
#endregion - From public/Meta/Get-GitHubMeta.ps1

#region - From public/Meta/Get-GitHubOctocat.ps1
function Get-GitHubOctocat {
    <#
        .NOTES
        https://docs.github.com/en/rest/reference/meta#github-api-root
    #>

    [CmdletBinding()]
    param ()

    $InputObject = @{
        APIEndpoint = '/octocat'
        Method      = 'GET'
    }

    $Response = Invoke-GitHubAPI @InputObject

    $Response
}
#endregion - From public/Meta/Get-GitHubOctocat.ps1

#region - From public/Meta/Get-GitHubRoot.ps1
function Get-GitHubRoot {
    <#
        .NOTES
        https://docs.github.com/en/rest/reference/meta#github-api-root
    #>

    [CmdletBinding()]
    param ()

    $InputObject = @{
        APIEndpoint = '/'
        Method      = 'GET'
    }

    $Response = Invoke-GitHubAPI @InputObject

    return $Response
}
#endregion - From public/Meta/Get-GitHubRoot.ps1

#region - From public/Meta/Get-GitHubZen.ps1
function Get-GitHubZen {
    <#
        .NOTES
        https://docs.github.com/en/rest/reference/meta#github-api-root
    #>

    [CmdletBinding()]
    param ()

    $InputObject = @{
        APIEndpoint = '/zen'
        Method      = 'GET'
    }

    $Response = Invoke-GitHubAPI @InputObject

    $Response
}
#endregion - From public/Meta/Get-GitHubZen.ps1

#region - From public/Emojis/Get-GitHubEmojis.ps1
function Get-GitHubEmojis {
    <#
        .NOTES
        https://docs.github.com/en/rest/reference/emojis#get-emojis
    #>

    [CmdletBinding()]
    param (
        [Parameter()]
        [string] $Destination
    )

    $InputObject = @{
        APIEndpoint = '/emojis'
        Method      = 'GET'
        Token       = $Token
    }

    $Response = Invoke-GitHubAPI @InputObject

    if (Test-Path -Path $Destination) {
        $Response.PSobject.Properties | ForEach-Object -Parallel {
            Invoke-WebRequest -Uri $_.Value -OutFile "$using:Destination/$($_.Name).png"
        }
    } else {
        $Response
    }
}
#endregion - From public/Emojis/Get-GitHubEmojis.ps1

#region - From public/Markdown/Get-GitHubMarkdown.ps1
function Get-GitHubMeta {
    <#
        .NOTES
        https://docs.github.com/en/rest/reference/meta#github-api-root
    #>

    [CmdletBinding()]
    param (
        [Parameter()]
        [string] $Token = $script:Token,

        [Parameter()]
        [switch] $Text,

        [Parameter()]
        [ValidateSet('markdown', 'gfm')]
        [string] $Mode,

        [Parameter()]
        [string] $Context
    )

    $InputObject = @{
        APIEndpoint = '/markdown'
        Body        = @{
            context = $Context
            mode    = $Mode
            text    = $Text
        }
        Method      = 'POST'
        $Token      = $Token
    }

    $Response = Invoke-GitHubAPI @InputObject

    $Response
}
#endregion - From public/Markdown/Get-GitHubMarkdown.ps1

#region - From public/Markdown/Get-GitHubMarkdownRaw.ps1
function Get-GitHubMeta {
    <#
        .NOTES
        https://docs.github.com/en/rest/reference/meta#github-api-root
    #>

    [CmdletBinding()]
    param (
        [Parameter()]
        [string] $Token = $script:Token,

        [Parameter()]
        [switch] $Text,

        [Parameter()]
        [string] $Context
    )

    $InputObject = @{
        APIEndpoint = '/markdown/raw'
        ContentType = 'text/plain'
        Data        = $Text
        Method      = 'POST'
        $Token      = $Token
    }

    $Response = Invoke-GitHubAPI @InputObject

    $Response
}
#endregion - From public/Markdown/Get-GitHubMarkdownRaw.ps1

#region - From public/Teams/Get-GitHubRepoTeam.ps1
function Get-GitHubRepoTeam {
    <#
        .NOTES
        https://docs.github.com/en/rest/reference/repos#get-a-repository
    #>

    [CmdletBinding()]
    param (
        [Parameter()]
        [string] $Owner = $script:Owner,

        [Parameter()]
        [string] $Repo = $script:Repo,

        [Parameter()]
        [string] $Token = $script:Token
    )

    $InputObject = @{
        Token       = $Token
        Method      = 'Get'
        APIEndpoint = "/repos/$Owner/$Repo/teams"
    }

    $Response = Invoke-GitHubAPI @InputObject

    $Response
}
#endregion - From public/Teams/Get-GitHubRepoTeam.ps1

#region - From public/Deployments/Get-GitHubEnvironment.ps1
function Get-GitHubEnvironment {
    [CmdletBinding()]
    param (
        [Parameter()]
        [string] $Owner = $script:Owner,

        [Parameter()]
        [string] $Repo = $script:Repo,

        [Parameter()]
        [string] $Token = $script:Token
    )

    begin {}

    process {
        # API Reference
        # https://docs.github.com/en/rest/reference/repos#get-all-environments

        $InputObject = @{
            APIEndpoint = "/repos/$Owner/$Repo/environments"
            Method      = 'GET'
            Token       = $Token
        }

        $Response = Invoke-GitHubAPI @InputObject

        $Response
    }

    end {}
}
#endregion - From public/Deployments/Get-GitHubEnvironment.ps1

#region - From public/Deployments/Get-GitHubEnvironmentSecrets.ps1
function Get-GitHubEnvironmentSecrets {
    [CmdletBinding()]
    param (
        [Parameter()]
        [string] $Owner = $script:Owner,

        [Parameter()]
        [string] $Repo = $script:Repo,

        [Parameter()]
        [string] $Token = $script:Token,

        [Alias('name')]
        [Parameter(
            Mandatory,
            ValueFromPipelineByPropertyName
        )]
        [string] $EnvironmentName
    )

    begin {}

    process {
        $RepoID = (Get-GitHubRepo).id
        #/repositories/{repository_id}/environments/{environment_name}/secrets/{secret_name}
        #/repositories/{repository_id}/environments/{environment_name}/secrets
        # API Reference
        # https://docs.github.com/en/rest/reference/repos#get-all-environments

        $InputObject = @{
            APIEndpoint = "/repos/$Owner/$Repo/environments"
            Token       = $Token
            Method      = 'GET'
            Verbose     = $Verbose
        }

        $Response = Invoke-GitHubAPI @InputObject

        $Response

        $APICall = @{
            Uri     = "$APIBaseURI/repositories/$RepoID/environments/$EnvironmentName/secrets"
            Headers = @{
                Authorization  = "token $Token"
                'Content-Type' = 'application/json'
            }
            Method  = 'GET'
            Body    = @{} | ConvertTo-Json -Depth 100
        }
        try {
            if ($PSBoundParameters.ContainsKey('Verbose')) {
                $APICall
            }
            $Response = Invoke-RestMethod @APICall
        } catch {
            throw $_
        }
        return $Response.secrets
    }
    end {}
}
#endregion - From public/Deployments/Get-GitHubEnvironmentSecrets.ps1

#region - From public/Deployments/Update-GitHubEnvironment.ps1
function Update-GitHubEnvironment {
    <#
        .NOTES
        https://docs.github.com/en/rest/reference/repos#create-or-update-an-environment
    #>

    [CmdletBinding()]
    param (
        [Parameter()]
        [string] $Owner = $script:Owner,

        [Parameter()]
        [string] $Repo = $script:Repo,

        [Parameter()]
        [string] $Token = $script:Token,

        [Alias('environment_name')]
        [Parameter(
            Mandatory,
            ValueFromPipelineByPropertyName
        )]
        [string] $Name
    )

    begin {}

    process {
        $InputObject = @{
            APIEndpoint = "/repos/$Owner/$Repo/environments/$Name"
            Body        = @{
                owner            = $Owner
                repo             = $Repo
                environment_name = $Name
            }
            Method      = 'PUT'
            Token       = $Token
        }

        $Response = Invoke-GitHubAPI @InputObject

        $Response
    }

    end {}
}
#endregion - From public/Deployments/Update-GitHubEnvironment.ps1

Export-ModuleMember -Function 'Disable-GitHubWorkflow','Enable-GitHubWorkflow','Get-GitHubWorkflow','Get-GitHubWorkflowRun','Get-GitHubWorkflowUsage','Remove-GitHubWorkflowRun','Start-GitHubWorkflow','Start-GitHubWorkflowReRun','Stop-GitHubWorkflowRun','Get-GitHubRepoBranch','Connect-GitHubAccount','Invoke-GitHubAPI','Set-GitHubConfig','Get-GitHubEnvironment','Get-GitHubEnvironmentSecrets','Update-GitHubEnvironment','Get-GitHubEmojis','Get-GitHubMarkdown','Get-GitHubMarkdownRaw','Get-GitHubAPIVersions','Get-GitHubMeta','Get-GitHubOctocat','Get-GitHubRoot','Get-GitHubZen','Get-GitHubRepoTeam','Get-GitHubUser','Set-GitHubUser','loader' -Cmdlet '*' -Variable '*' -Alias '*'