GitHub.psm1
#region - From classes/Data/App.ps1 $script:App = [pscustomobject]@{ GitHubApp = [pscustomobject]@{ ClientID = 'Iv1.f26b61bc99e69405' # $script:App.GitHubApp.ClientID } OAuthApp = [pscustomobject]@{ ClientID = '7204ae9b0580f2cb8288' # $script:App.OAuthApp.ClientID } } #endregion - From classes/Data/App.ps1 #region - From classes/Data/Config.ps1 $script:ConfigTemplate = [pscustomobject]@{ App = [pscustomobject]@{ API = [pscustomobject]@{ BaseURI = 'https://api.github.com' # $script:ConfigTemplate.App.API.BaseURI Version = '2022-11-28' # $script:ConfigTemplate.App.API.Version } Defaults = [pscustomobject]@{} } User = [pscustomobject]@{ Auth = [pscustomobject]@{ AccessToken = [pscustomobject]@{ Value = '' # $script:ConfigTemplate.User.Auth.AccessToken.Value ExpirationDate = [datetime]::MinValue # $script:ConfigTemplate.User.Auth.AccessToken.ExpirationDate } ClientID = '' # $script:ConfigTemplate.User.Auth.ClientID Mode = '' # $script:ConfigTemplate.User.Auth.Mode RefreshToken = [pscustomobject]@{ Value = '' # $script:ConfigTemplate.User.Auth.RefreshToken.Value ExpirationDate = [datetime]::MinValue # $script:ConfigTemplate.User.Auth.RefreshToken.ExpirationDate } Scope = '' # $script:ConfigTemplate.User.Auth.Scope } Defaults = [pscustomobject]@{ Owner = '' # $script:ConfigTemplate.User.Defaults.Owner Repo = '' # $script:ConfigTemplate.User.Defaults.Repo } } } $script:Config = $script:ConfigTemplate #endregion - From classes/Data/Config.ps1 #region - From classes/Data/SecretVault.ps1 $script:SecretVault = [pscustomobject]@{ Name = 'GitHub' # $script:SecretVault.Name Type = 'Microsoft.PowerShell.SecretStore' # $script:SecretVault.Type } $script:Secret = [pscustomobject]@{ Name = 'Config' # $script:Secret.Name } #endregion - From classes/Data/SecretVault.ps1 #region - From private/Config/Initialize-SecretVault.ps1 #Requires -Version 7.0 #Requires -Modules Microsoft.PowerShell.SecretManagement #Requires -Modules Microsoft.PowerShell.SecretStore function Initialize-SecretVault { <# .SYNOPSIS Initialize a secret vault. .DESCRIPTION Initialize a secret vault. If the vault does not exist, it will be created. .EXAMPLE Initialize-SecretVault -Name 'SecretStore' -Type 'Microsoft.PowerShell.SecretStore' Initializes a secret vault named 'SecretStore' using the 'Microsoft.PowerShell.SecretStore' module. .NOTES For more information aobut secret vaults, see https://learn.microsoft.com/en-us/powershell/utility-modules/secretmanagement/overview?view=ps-modules #> [OutputType([void])] [CmdletBinding()] param ( # The name of the secret vault. [Parameter()] [string] $Name, # The type of the secret vault. [Parameter()] [Alias('ModuleName')] [string] $Type ) $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]" switch ($Type) { 'Microsoft.PowerShell.SecretStore' { $vaultParameters = @{ Authentication = 'None' PasswordTimeout = -1 Interaction = 'None' Scope = 'CurrentUser' WarningAction = 'SilentlyContinue' Confirm = $false Force = $true } Reset-SecretStore @vaultParameters } } } $secretStore = Get-SecretVault | Where-Object { $_.Name -eq $Name } $secretStoreExists = $secretStore.count -ne 0 if (-not $secretStoreExists) { $secretVault = @{ Name = $Name ModuleName = $Type DefaultVault = $true Description = 'SecretStore' } Register-SecretVault @secretVault } } #endregion - From private/Config/Initialize-SecretVault.ps1 #region - From private/Auth/DeviceFlow/Check-GitHubAccessToken.ps1 function Check-GitHubAccessToken { [DateTime]$accessTokenExirationDate = $script:ConfigTemplate.User.Auth.AccessToken.ExpirationDate $accessTokenValid = $accessTokenExirationDate -gt (Get-Date) if (-not $accessTokenValid) { Write-Warning 'Your access token has expired. Refreshing it...' Connect-GitHubAccount -Refresh } $TimeSpan = New-TimeSpan -Start (Get-Date) -End $accessTokenExirationDate Write-Host "Your access token will expire in $($TimeSpan.Days)-$($TimeSpan.Hours):$($TimeSpan.Minutes):$($TimeSpan.Seconds)." } #endregion - From private/Auth/DeviceFlow/Check-GitHubAccessToken.ps1 #region - From private/Auth/DeviceFlow/Invoke-GitHubDeviceFlowLogin.ps1 function Invoke-GitHubDeviceFlowLogin { <# .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-GitHubDeviceFlowLogin 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 https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/authorizing-oauth-apps#device-flow #> [OutputType([void])] [CmdletBinding()] param( # The Client ID of the GitHub App. [Parameter(Mandatory)] [string] $ClientID, # The scope of the access token, when using OAuth authentication. # Provide the list of scopes as space-separated values. # For more information on scopes visit: # https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/scopes-for-oauth-apps [Parameter()] [string] $Scope, # The refresh token to use for re-authentication. [Parameter()] [string] $RefreshToken ) do { if ($RefreshToken) { $tokenResponse = Wait-GitHubAccessToken -ClientID $ClientID -RefreshToken $RefreshToken } else { $deviceCodeResponse = Request-GitHubDeviceCode -ClientID $ClientID -Scope $Scope $deviceCode = $deviceCodeResponse.device_code $interval = $deviceCodeResponse.interval $userCode = $deviceCodeResponse.user_code $verificationUri = $deviceCodeResponse.verification_uri Write-Host '! ' -ForegroundColor DarkYellow -NoNewline Write-Host "We added the code to your clipboard: [$userCode]" $userCode | Set-Clipboard Read-Host 'Press Enter to open github.com in your browser...' Start-Process $verificationUri $tokenResponse = Wait-GitHubAccessToken -DeviceCode $deviceCode -ClientID $ClientID -Interval $interval } } while ($tokenResponse.error) $tokenResponse } #endregion - From private/Auth/DeviceFlow/Invoke-GitHubDeviceFlowLogin.ps1 #region - From private/Auth/DeviceFlow/Request-GitHubAccessToken.ps1 function Request-GitHubAccessToken { <# .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-GitHubAccessToken -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(DefaultParameterSetName = 'DeviceFlow')] param( # The Client ID of the GitHub App. [Parameter(Mandatory)] [string] $ClientID, # The 'device_code' used to request the access token. [Parameter( Mandatory, ParameterSetName = 'DeviceFlow' )] [string] $DeviceCode, # The refresh token used create a new access token. [Parameter( Mandatory, ParameterSetName = 'RefreshToken' )] [string] $RefreshToken ) $body = @{ 'client_id' = $ClientID } if ($PSBoundParameters.ContainsKey('RefreshToken')) { $body += @{ 'refresh_token' = $RefreshToken 'grant_type' = 'refresh_token' } } if ($PSBoundParameters.ContainsKey('DeviceCode')) { $body += @{ 'device_code' = $DeviceCode 'grant_type' = 'urn:ietf:params:oauth:grant-type:device_code' } } $RESTParams = @{ Uri = 'https://github.com/login/oauth/access_token' Method = 'POST' Body = $body Headers = @{ 'Accept' = 'application/json' } } try { Write-Verbose ($RESTParams.GetEnumerator() | Out-String) $tokenResponse = Invoke-RestMethod @RESTParams -Verbose:$false return $tokenResponse } catch { Write-Error $_ throw $_ } } #endregion - From private/Auth/DeviceFlow/Request-GitHubAccessToken.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 -Mode $Mode 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(Mandatory)] [string] $ClientID, # The scope of the access token, when using OAuth authentication. # Provide the list of scopes as space-separated values. # For more information on scopes visit: # https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/scopes-for-oauth-apps [Parameter()] [string] $Scope = 'gist, read:org, repo, workflow' ) $headers = @{ Accept = 'application/json' } $body = @{ client_id = $ClientID scope = $Scope } $RESTParams = @{ Uri = 'https://github.com/login/device/code' Method = 'POST' Body = $body Headers = $headers } try { Write-Verbose ($RESTParams.GetEnumerator() | Out-String) $deviceCodeResponse = Invoke-RestMethod @RESTParams -Verbose:$false return $deviceCodeResponse } catch { Write-Error $_ throw $_ } } #endregion - From private/Auth/DeviceFlow/Request-GitHubDeviceCode.ps1 #region - From private/Auth/DeviceFlow/Wait-GitHubAccessToken.ps1 function Wait-GitHubAccessToken { <# .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-GitHubAccessToken -DeviceCode $deviceCode -ClientID $ClientID -Interval $interval This will poll the GitHub API until the user has entered the code. .EXAMPLE Wait-GitHubAccessToken -Refresh -ClientID $ClientID .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(DefaultParameterSetName = 'DeviceFlow')] param( # The Client ID of the GitHub App. [Parameter(Mandatory)] [string] $ClientID, # The device code used to request the access token. [Parameter( Mandatory, ParameterSetName = 'DeviceFlow' )] [string] $DeviceCode, # The refresh token used to request a new access token. [Parameter( Mandatory, ParameterSetName = 'RefreshToken' )] [string] $RefreshToken, # The interval to wait between polling for the token. [Parameter()] [int] $Interval = 5 ) do { if ($Refresh) { $response = Request-GitHubAccessToken -ClientID $ClientID -RefreshToken $RefreshToken } else { $response = Request-GitHubAccessToken -ClientID $ClientID -DeviceCode $DeviceCode } 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 } 'unsupported_grant_type' { # The 'grant_type' is not supported. Write-Error $response.error_description exit 1 } 'incorrect_client_credentials' { # The 'client_id' is not valid. Write-Error $response.error_description exit 1 } 'incorrect_device_code' { # The 'device_code' is not valid. Write-Error $response.error_description exit 2 } 'access_denied' { # The user cancelled the process. Stop polling. Write-Error $response.error_description exit 1 } 'device_flow_disabled' { # The GitHub App does not support the Device Flow. 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-GitHubAccessToken.ps1 #region - From public/loader.ps1 Initialize-SecretVault -Name $script:SecretVault.Name -Type $script:SecretVault.Type Restore-GitHubConfig if (-not [string]::IsNullOrEmpty($env:GH_TOKEN)) { Write-Verbose 'Logging on using GH_TOKEN' Connect-GitHubAccount -AccessToken $env:GH_TOKEN } if (-not [string]::IsNullOrEmpty($env:GITHUB_TOKEN)) { Write-Verbose 'Logging on using GITHUB_TOKEN' Connect-GitHubAccount -AccessToken $env:GITHUB_TOKEN } #endregion - From public/loader.ps1 #region - From public/Config/Get-GitHubConfig.ps1 function Get-GitHubConfig { <# .SYNOPSIS Get the current GitHub configuration. .DESCRIPTION Get the current GitHub configuration. If the Refresh switch is used, the configuration will be refreshed from the configuration file. .EXAMPLE Get-GitHubConfig Returns the current GitHub configuration. .EXAMPLE Get-GitHubConfig -Refresh Refreshes the current GitHub configuration from the configuration store beofre returning it. #> [Alias('Get-GHConfig')] [OutputType([PSCustomObject])] [CmdletBinding()] param ( # Refresh the configuration from the configuration store before returning it. [Parameter()] [switch] $Refresh ) if ($Refresh) { Restore-GitHubConfig } $script:Config } #endregion - From public/Config/Get-GitHubConfig.ps1 #region - From public/Config/Reset-GitHubConfig.ps1 function Reset-GitHubConfig { <# .SYNOPSIS Reset the GitHub configuration. .DESCRIPTION Reset the GitHub configuration. Specific scopes can be reset by using the Scope parameter. .EXAMPLE Reset-GitHubConfig Resets the entire GitHub configuration. .EXAMPLE Reset-GitHubConfig -Scope 'App.API' Resets the App.API scope of the GitHub configuration. #> [Alias('Reset-GHConfig')] [OutputType([void])] [CmdletBinding()] param( # Reset the GitHub configuration for a specific scope. [Parameter()] [ValidateSet('App', 'App.API', 'App.Defaults', 'User', 'User.Auth', 'User.Defaults', 'All')] [string] $Scope = 'All' ) switch ($Scope) { 'App' { $script:Config.App = $script:ConfigTemplate.App } 'App.API' { $script:Config.App.API = $script:ConfigTemplate.App.API } 'App.Defaults' { $script:Config.App.Defaults = $script:ConfigTemplate.App.Defaults } 'User' { $script:Config.User = $script:ConfigTemplate.User } 'User.Auth' { $script:Config.User.Auth = $script:ConfigTemplate.User.Auth } 'User.Defaults' { $script:Config.User.Defaults = $script:ConfigTemplate.User.Defaults } 'All' { $script:Config = $script:ConfigTemplate } } Save-GitHubConfig } #endregion - From public/Config/Reset-GitHubConfig.ps1 #region - From public/Config/Restore-GitHubConfig.ps1 #Requires -Version 7.0 #Requires -Modules Microsoft.PowerShell.SecretManagement function Restore-GitHubConfig { <# .SYNOPSIS Restore the GitHub configuration from the configuration store. .DESCRIPTION Restore the GitHub configuration from the configuration store. .EXAMPLE Restore-GitHubConfig Restores the GitHub configuration from the configuration store. #> [Alias('Load-GitHubConfig')] [Alias('Load-GHConfig')] [Alias('Restore-GHConfig')] [OutputType([void])] [CmdletBinding()] param() $vault = Get-SecretVault -Name $script:SecretVault.Name $vaultExists = $vault.count -eq 1 if ($vaultExists) { $secretExists = Get-SecretInfo -Name $script:Secret.Name -Vault $script:SecretVault.Name if ($secretExists) { $script:Config = Get-Secret -Name $script:Secret.Name -AsPlainText -Vault $script:SecretVault.Name | ConvertFrom-Json } else { Write-Warning "Unable to restore configuration." Write-Warning "The secret [$($script:Secret.Name)] does not exist in the vault [$($script:SecretVault.Name)]." } } else { Write-Warning "Unable to restore configuration." Write-Warning "The vault [$($script:SecretVault.Name)] does not exist." } } #endregion - From public/Config/Restore-GitHubConfig.ps1 #region - From public/Config/Save-GitHubConfig.ps1 #Requires -Version 7.0 #Requires -Modules Microsoft.PowerShell.SecretManagement function Save-GitHubConfig { <# .SYNOPSIS Save the GitHub configuration to the configuration store. .DESCRIPTION Save the GitHub configuration to the configuration store. .EXAMPLE Save-GitHubConfig Saves the GitHub configuration to the configuration store. #> [Alias('Save-GHConfig')] [OutputType([void])] [CmdletBinding()] param() $config = $script:Config | ConvertTo-Json -Depth 100 Set-Secret -Name $script:Secret.Name -Secret $config -Vault $script:SecretVault.Name } #endregion - From public/Config/Save-GitHubConfig.ps1 #region - From public/Config/Set-GitHubConfig.ps1 function Set-GitHubConfig { <# .SYNOPSIS Set the GitHub configuration. .DESCRIPTION Set the GitHub configuration. Specific scopes can be set by using the parameters. .EXAMPLE Set-GitHubConfig -APIBaseURI 'https://api.github.com' -APIVersion '2022-11-28' Sets the App.API scope of the GitHub configuration. #> [Alias('Set-GHConfig')] [CmdletBinding()] param ( # Set the API Base URI. [Parameter()] [string] $APIBaseURI, # Set the GitHub API Version. [Parameter()] [string] $APIVersion, # Set the default for the Owner parameter. [Parameter()] [string] $Owner, # Set the default for the Repo parameter. [Parameter()] [string] $Repo ) switch ($PSBoundParameters.Keys) { 'APIBaseURI' { $script:ConfigTemplate.App.API.BaseURI = $APIBaseURI } 'APIVersion' { $script:ConfigTemplate.App.API.Version = $APIVersion } 'Owner' { $script:ConfigTemplate.User.Defaults.Owner = $Owner } 'Repo' { $script:ConfigTemplate.User.Defaults.Repo = $Repo } } Save-GitHubConfig } #endregion - From public/Config/Set-GitHubConfig.ps1 #region - From public/Auth/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. For device flow / device code login: PowerShell requests device and user verification codes and gets the authorization URL where you will enter the user verification code. In GitHub you will be asked to enter a user verification code at https://github.com/login/device. PowerShell will keep polling GitHub for the user authentication status. Once you have authorized the device, the app will be able to make API calls with a new access token. .EXAMPLE Connect-GitHubAccount Connects to GitHub using a device flow login. .EXAMPLE Connect-GitHubAccount -AccessToken 'ghp_####' Connects to GitHub using a personal access token (PAT). .EXAMPLE Connect-GitHubAccount -Refresh Refreshes the access token. .EXAMPLE Connect-GitHubAccount -Mode 'OAuthApp' -Scope 'gist read:org repo workflow' Connects to GitHub using a device flow login and sets the scope of the access token. .NOTES https://docs.github.com/en/rest/overview/other-authentication-methods#authenticating-for-saml-sso #> [Alias('Connect-GHAccount')] [Alias('Connect-GitHub')] [Alias('Connect-GH')] [Alias('Login-GitHubAccount')] [Alias('Login-GHAccount')] [Alias('Login-GitHub')] [Alias('Login-GH')] [OutputType([void])] [CmdletBinding(DefaultParameterSetName = 'DeviceFlow')] param ( # Choose between authentication methods, either OAuthApp or GitHubApp. # For more info about the types of authentication visit: # https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/differences-between-github-apps-and-oauth-apps [Parameter(ParameterSetName = 'DeviceFlow')] [ValidateSet('OAuthApp', 'GitHubApp')] [string] $Mode = 'GitHubApp', # The scope of the access token, when using OAuth authentication. # Provide the list of scopes as space-separated values. # For more information on scopes visit: # https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/scopes-for-oauth-apps [Parameter(ParameterSetName = 'DeviceFlow')] [string] $Scope, # Refresh the access token. [Parameter( Mandatory, ParameterSetName = 'Refresh' )] [switch] $Refresh, # The personal access token to use for authentication. [Parameter( Mandatory, ParameterSetName = 'PAT' )] [String] $AccessToken ) $vault = Get-SecretVault | Where-Object -Property ModuleName -EQ $script:SecretVault.Type if ($null -eq $vault) { Initialize-SecretVault -Name $script:SecretVault.Name -Type $script:SecretVault.Type $vault = Get-SecretVault | Where-Object -Property ModuleName -EQ $script:SecretVault.Type } $clientID = $script:App.$Mode.ClientID switch ($PSCmdlet.ParameterSetName) { 'Refresh' { Write-Verbose 'Refreshing access token...' $tokenResponse = Invoke-GitHubDeviceFlowLogin -ClientID $clientID -RefreshToken $script:Config.User.Auth.RefreshToken.Value } 'DeviceFlow' { Write-Verbose 'Logging in using device flow...' if ([string]::IsNullOrEmpty($Scope) -and ($Mode -eq 'OAuthApp')) { $Scope = 'gist read:org repo workflow' } Reset-GitHubConfig -Scope 'User.Auth' $tokenResponse = Invoke-GitHubDeviceFlowLogin -ClientID $clientID -Scope $Scope $script:Config.User.Auth.Mode = $Mode $script:Config.User.Auth.ClientID = $clientID } 'PAT' { Write-Verbose 'Logging in using personal access token...' Reset-GitHubConfig -Scope 'User.Auth' $script:Config.User.Auth.AccessToken.Value = $Token $script:Config.User.Auth.Mode = 'PAT' Save-GitHubConfig Write-Host '✓ ' -ForegroundColor Green -NoNewline Write-Host 'Logged in using a personal access token (PAT)!' return } } if ($tokenResponse) { $script:Config.User.Auth.AccessToken.Value = $tokenResponse.access_token $script:Config.User.Auth.AccessToken.ExpirationDate = (Get-Date).AddSeconds($tokenResponse.expires_in) $script:Config.User.Auth.RefreshToken.Value = $tokenResponse.refresh_token $script:Config.User.Auth.RefreshToken.ExpirationDate = (Get-Date).AddSeconds($tokenResponse.refresh_token_expires_in) $script:Config.User.Auth.Scope = $tokenResponse.scope } Save-GitHubConfig Write-Host '✓ ' -ForegroundColor Green -NoNewline Write-Host "Logged in to GitHub!" } #endregion - From public/Auth/Connect-GitHubAccount.ps1 #region - From public/Auth/Disconnect-GitHubAccount.ps1 function Disconnect-GitHubAccount { <# .SYNOPSIS Disconnects from GitHub and removes the current GitHub configuration. .DESCRIPTION Disconnects from GitHub and removes the current GitHub configuration. .EXAMPLE Disconnect-GitHubAccount Disconnects from GitHub and removes the current GitHub configuration. #> [Alias('Disconnect-GHAccount')] [Alias('Disconnect-GitHub')] [Alias('Disconnect-GH')] [Alias('Logout-GitHubAccount')] [Alias('Logout-GHAccount')] [Alias('Logout-GitHub')] [Alias('Logout-GH')] [Alias('Logoff-GitHubAccount')] [Alias('Logoff-GHAccount')] [Alias('Logoff-GitHub')] [Alias('Logoff-GH')] [OutputType([void])] [CmdletBinding()] param () Reset-GitHubConfig Write-Host "✓ " -ForegroundColor Green -NoNewline Write-Host "Logged out of GitHub!" } #endregion - From public/Auth/Disconnect-GitHubAccount.ps1 Export-ModuleMember -Function 'Connect-GitHubAccount','Disconnect-GitHubAccount','Get-GitHubConfig','Reset-GitHubConfig','Restore-GitHubConfig','Save-GitHubConfig','Set-GitHubConfig','' -Cmdlet '' -Variable '' -Alias '*' |