GitHub.psm1
#region - From private/common.ps1 $script:GitHubAppClientID = 'Iv1.f26b61bc99e69405' $script:GitHubOAuthAppClientID = '7204ae9b0580f2cb8288' $script:ClientID = '' $script:AuthMode = '' $script:APIBaseURI = 'https://api.github.com' $script:ContentType = 'application/vnd.github+json' $Script:Version = '2022-11-28' $script:AccessToken = '' $script:RefreshToken = '' $script:Owner = '' $script:Repo = '' #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/Menu/Invoke-DrawMenu.ps1 function Invoke-DrawMenu { ## supportfunction to the Menu function below param ( $menuItems, $menuPosition, $menuTitel ) $fcolor = $host.UI.RawUI.ForegroundColor $bcolor = $host.UI.RawUI.BackgroundColor $l = $menuItems.length + 1 Clear-Host $menuwidth = $menuTitel.length + 4 Write-Host "`t" -NoNewline Write-Host ('*' * $menuwidth) -fore $fcolor -back $bcolor Write-Host "`t" -NoNewline Write-Host "* $menuTitel *" -fore $fcolor -back $bcolor Write-Host "`t" -NoNewline Write-Host ('*' * $menuwidth) -fore $fcolor -back $bcolor Write-Host '' Write-Debug "L: $l MenuItems: $menuItems MenuPosition: $menuposition" for ($i = 0; $i -le $l; $i++) { Write-Host "`t" -NoNewline if ($i -eq $menuPosition) { Write-Host "$($menuItems[$i])" -fore $bcolor -back $fcolor } else { Write-Host "$($menuItems[$i])" -fore $fcolor -back $bcolor } } } #endregion - From private/Menu/Invoke-DrawMenu.ps1 #region - From private/Menu/Invoke-Meny.ps1 function Menu { ## Generate a small "DOS-like" menu. ## Choose a menuitem using up and down arrows, select by pressing ENTER param ( [array]$menuItems, $menuTitel = 'MENU' ) $vkeycode = 0 $pos = 0 Invoke-DrawMenu $menuItems $pos $menuTitel while ($vkeycode -ne 13) { $press = $host.ui.rawui.readkey('NoEcho,IncludeKeyDown') $vkeycode = $press.virtualkeycode Write-Host "$($press.character)" -NoNewline if ($vkeycode -eq 38) { $pos-- } if ($vkeycode -eq 40) { $pos++ } if ($pos -lt 0) { $pos = 0 } if ($pos -ge $menuItems.length) { $pos = $menuItems.length - 1 } Invoke-DrawMenu $menuItems $pos $menuTitel } Write-Output $($menuItems[$pos]) } <# ? What account do you want to log into? [Use arrows to move, type to filter] > GitHub.com GitHub Enterprise Server #> #endregion - From private/Menu/Invoke-Meny.ps1 #region - From private/Auth/DeviceFlow/Get-GitHubDeviceFlowClientID.ps1 function Get-GitHubDeviceFlowClientID { [CmdletBinding()] param ( [Parameter(Mandatory = $false)] [ValidateSet('OAuthApp', 'GitHubApp')] [string] $Mode = 'OAuthApp' ) switch ($Mode) { 'OAuthApp' { $script:GitHubOAuthAppClientID } 'GitHubApp' { $script:GitHubAppClientID } } } #endregion - From private/Auth/DeviceFlow/Get-GitHubDeviceFlowClientID.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, # 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 } if ($script:AuthMode -eq 'OAuthApp') { $body += @{ 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 public/loader.ps1 # Use this to find what the intended way of working is for CLI # https://cli.github.com/manual/gh_help_environment Initialize-SecretVault $secrets = Get-SecretInfo -Vault 'SecretStore' if ([string]::IsNullOrEmpty($script:AccessToken)) { $script:AccessToken = $env:GH_TOKEN } if ([string]::IsNullOrEmpty($script:AccessToken)) { $script:AccessToken = $env:GITHUB_TOKEN } if ([string]::IsNullOrEmpty($script:AccessToken) -and ($secrets.name -contains 'GitHubPS.AccessToken')) { $script:AccessToken = Get-Secret -Name 'GitHubPS.AccessToken' -AsPlainText -ErrorAction SilentlyContinue } if ($secrets.name -contains 'GitHubPS.Owner') { $script:Owner = Get-Secret -Name 'GitHubPS.Owner' -AsPlainText } if ($secrets.name -contains 'GitHubPS.Repo') { $script:Repo = Get-Secret -Name 'GitHubPS.Repo' -AsPlainText } if ($secrets.name -contains 'GitHubPS.APIBaseURI') { $script:APIBaseURI = Get-Secret -Name 'GitHubPS.APIBaseURI' -AsPlainText } if ($secrets.name -contains 'GitHubPS.Version') { $script:Version = Get-Secret -Name 'GitHubPS.Version' -AsPlainText } if ($secrets.name -contains 'GitHubPS.ContentType') { $script:ContentType = Get-Secret -Name 'GitHubPS.ContentType' -AsPlainText } if ($secrets.name -contains 'GitHubPS.AuthMode') { $script:AuthMode = Get-Secret -Name 'GitHubPS.AuthMode' -AsPlainText } if (($secrets.name -contains 'GitHubPS.AccessToken.ExpirationDate') -and $secrets.name -contains 'GitHubPS.RefreshToken') { $script:RefreshToken = Get-Secret -Name 'GitHubPS.RefreshToken' -AsPlainText [DateTime]$accessTokenExirationDate = Get-Secret -Name 'GitHubPS.AccessToken.ExpirationDate' -AsPlainText -ErrorAction SilentlyContinue $accessTokenValid = $accessTokenExirationDate -gt (Get-Date) if (-not $accessTokenValid) { Write-Warning "Your access token has expired. Refreshing it..." Connect-GitHubAccount -Refresh } } #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 ) $InputObject = @{ Method = 'GET' APIEndpoint = "/repos/$Owner/$Repo/branches" } $Response = Invoke-GitHubAPI @InputObject $Response } #endregion - From public/Branches/Get-GitHubRepoBranch.ps1 #region - From public/Core/Get-GithubConfig.ps1 function Get-GithubConfig { [CmdletBinding()] param () [pscustomobject]@{ APIBaseURI = $script:APIBaseURI AccessToken = $script:AccessToken AuthMode = $script:AuthMode ClientID = $script:ClientID ContentType = $script:ContentType GitHubAppClientID = $script:GitHubAppClientID GitHubOAuthAppClientID = $script:GitHubOAuthAppClientID Owner = $script:Owner RefreshToken = $script:RefreshToken Repo = $script:Repo Version = $Script:Version } } #endregion - From public/Core/Get-GithubConfig.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(Mandatory)] [string] $APIEndpoint, [Parameter(ParameterSetName = 'Body')] [hashtable] $Body, [Parameter(ParameterSetName = 'Data')] [string] $Data, [Parameter()] [string] $Accept, [Parameter()] [string] $Token = $script:AccessToken, [Parameter()] [string] $ContentType = $script:ContentType, [Parameter()] [string] $Version = $script:Version, [Parameter()] [switch] $UseWebRequest ) $headers = @{} if (-not [string]::IsNullOrEmpty($ContentType)) { $headers += @{ 'Content-Type' = $ContentType } } if (-not [string]::IsNullOrEmpty($Version)) { $headers += @{ 'X-GitHub-Api-Version' = $Version } } if (-not [string]::IsNullOrEmpty($Accept)) { $headers += @{ 'Accept' = $Accept } } if (-not [string]::IsNullOrEmpty($Token)) { if ($Token.StartsWith('ghp_')) { # Classic tokens $authorization = "token $Token" } if ($Token.StartsWith('github_pat_')) { # Fine-grained PAT $authorization = "token $Token" } if ($Token.StartsWith('ghu_')) { # GitHubApp access token $authorization = "Bearer $Token" } if ($Token.StartsWith('gho_')) { # OAuth app access token $authorization = "Bearer $Token" } $headers += @{ Authorization = $authorization } } $URI = "$script:APIBaseURI$($APIEndpoint.Replace('\', '/').Replace('//', '/'))" $APICall = @{ Uri = $URI Method = $Method Headers = $Headers } if ($PSBoundParameters.ContainsKey('Body')) { $APICall += @{ Body = ($Body | ConvertTo-Json -Depth 100) } } if ($PSBoundParameters.ContainsKey('Data')) { $APICall += @{ Body = $Data } } try { Write-Verbose ($APICall.GetEnumerator() | Out-String) if ($UseWebRequest) { return Invoke-WebRequest @APICall } Invoke-RestMethod @APICall } catch { Write-Error $_ 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] $APIBaseURI, [Parameter()] [string] $Version, [Parameter()] [string] $ContentType ) $Vault = Get-SecretVault | Where-Object -Property ModuleName -EQ 'Microsoft.PowerShell.SecretStore' if ($PSBoundParameters.ContainsKey('Owner')) { $script:Owner = $Owner Set-Secret -Name 'GitHubPS.Owner' -Secret $script:Owner -Vault $Vault.Name } if ($PSBoundParameters.ContainsKey('Repo')) { $script:Repo = $Repo Set-Secret -Name 'GitHubPS.Repo' -Secret $script:Repo -Vault $Vault.Name } if ($PSBoundParameters.ContainsKey('APIBaseURI')) { $script:APIBaseURI = $APIBaseURI Set-Secret -Name 'GitHubPS.APIBaseURI' -Secret $script:APIBaseURI -Vault $Vault.Name } if ($PSBoundParameters.ContainsKey('Version')) { $script:Version = $Version Set-Secret -Name 'GitHubPS.Version' -Secret $script:Version -Vault $Vault.Name } if ($PSBoundParameters.ContainsKey('ContentType')) { $script:ContentType = $ContentType Set-Secret -Name 'GitHubPS.ContentType' -Secret $script:ContentType -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. .EXAMPLE An example .NOTES https://docs.github.com/en/rest/users/users#get-the-authenticated-user #> [CmdletBinding()] [Alias('Get-GitHubContext')] param () $InputObject = @{ APIEndpoint = '/user' Method = 'GET' } $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 ) $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' } $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( Mandatory, ValueFromPipelineByPropertyName )] [string[]] $ID ) begin {} process { $InputObject = @{ 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( Mandatory, ValueFromPipelineByPropertyName )] [string[]] $ID ) begin {} process { $InputObject = @{ 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(ParameterSetName = 'ByName')] [string] $Name, [Parameter(ParameterSetName = 'ByID')] [string] $ID, [Parameter()] [int] $PageSize = 100 ) $processedPages = 0 $workflows = @() do { $processedPages++ $InputObject = @{ Method = 'GET' APIEndpoint = "/repos/$Owner/$Repo/actions/workflows?per_page=$PageSize&page=$processedPages" } $Response = Invoke-GitHubAPI @InputObject $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(ParameterSetName = 'ByName')] [string] $Name, [Parameter(ParameterSetName = 'ByID')] [string] $ID, [Parameter()] [int] $PageSize = 100 ) $processedPages = 0 $workflowRuns = @() do { $processedPages++ $InputObject = @{ 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( Mandatory, ValueFromPipelineByPropertyName )] [string[]] $ID ) begin {} process { # API Reference # https://docs.github.com/en/rest/reference/actions#get-workflow-usage $InputObject = @{ Method = 'GET' APIEndpoint = "/repos/$Owner/$Repo/actions/workflows/$ID/timing" } $Response = Invoke-GitHubAPI @InputObject $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( Mandatory, ValueFromPipelineByPropertyName )] [string] $ID ) begin {} process { $InputObject = @{ APIEndpoint = "repos/$Owner/$Repo/actions/runs/$ID" Method = 'DELETE' } $Response = Invoke-GitHubAPI @InputObject $Response } end {} } #endregion - From public/Actions/Remove-GitHubWorkflowRun.ps1 #region - From public/Actions/Start-GitHubWorkflow.ps1 <# .SYNOPSIS Start a workflow run using the workflow's ID. .DESCRIPTION Start a workflow run using the workflow's ID. .EXAMPLE Get-GitHubWorkflow | Where-Object name -NotLike '.*' | Start-GitHubWorkflow -Inputs @{ staticValidation = $true deploymentValidation = $false removeDeployment = $true prerelease = $false } .NOTES # API Reference # https://docs.github.com/en/free-pro-team@latest/rest/actions/workflows?apiVersion=2022-11-28#create-a-workflow-dispatch-event #> function Start-GitHubWorkflow { [CmdletBinding()] param ( [Parameter()] [string] $Owner = $script:Owner, [Parameter()] [string] $Repo = $script:Repo, [Alias('workflow_id')] [Parameter( Mandatory, ValueFromPipelineByPropertyName )] [string] $ID, [Parameter( ValueFromPipelineByPropertyName )] [Alias('branch', 'tag')] [string] $Ref = 'main', [Parameter()] [hashtable] $Inputs = @{} ) begin {} process { $InputObject = @{ Method = 'POST' APIEndpoint = "/repos/$Owner/$Repo/actions/workflows/$ID/dispatches" Body = @{ ref = $Ref inputs = $Inputs } } $Response = Invoke-GitHubAPI @InputObject $Response } end {} } #endregion - From public/Actions/Start-GitHubWorkflow.ps1 #region - From public/Actions/Start-GitHubWorkflowReRun.ps1 # API Reference # https://docs.github.com/en/rest/reference/actions#re-run-a-workflow function Start-GitHubWorkflowReRun { [CmdletBinding()] param ( [Parameter()] [string] $Owner = $script:Owner, [Parameter()] [string] $Repo = $script:Repo, [Alias('workflow_id')] [Parameter( Mandatory, ValueFromPipelineByPropertyName )] [string] $ID ) begin {} process { $InputObject = @{ Method = 'POST' APIEndpoint = "/repos/$Owner/$Repo/actions/runs/$ID/rerun" } $Response = Invoke-GitHubAPI @InputObject 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, [Alias('workflow_id')] [Parameter( Mandatory, ValueFromPipelineByPropertyName )] [string] $ID ) begin {} process { # API Reference # https://docs.github.com/en/rest/reference/actions#cancel-a-workflow-run $InputObject = @{ Method = 'POST' APIEndpoint = "/repos/$Owner/$Repo/actions/runs/$ID/cancel" } $Response = Invoke-GitHubAPI @InputObject $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 -Token $null $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 $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' } $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/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( # 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()] [ValidateSet('OAuthApp', 'GitHubApp')] [string] $Mode, # 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, # Refresh the access token. [Parameter()] [switch] $Refresh ) $script:AuthMode = $Mode $script:ClientID = Get-GitHubDeviceFlowClientID -Mode $Mode do { if ($Refresh) { $tokenResponse = Wait-GitHubToken -ClientID $script:ClientID } 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-GitHubToken -DeviceCode $deviceCode -ClientID $ClientID -Interval $interval } } while ($tokenResponse.error) $tokenResponse } #endregion - From public/DeviceFlow/Invoke-GitHubDeviceFlowLogin.ps1 #region - From public/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(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 'grant_type' = 'urn:ietf:params:oauth:grant-type:device_code' } if ($PSBoundParameters.ContainsKey('RefreshToken')) { $body += @{ 'refresh_token' = $RefreshToken } } if ($PSBoundParameters.ContainsKey('DeviceCode')) { $body += @{ 'device_code' = $DeviceCode } } $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 public/DeviceFlow/Request-GitHubToken.ps1 #region - From public/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, ParameterSetName = 'DeviceFlow' )] [string] $DeviceCode, # Refresh the access token. [Parameter( Mandatory, ParameterSetName = 'Refresh' )] [switch] $Refresh, # The Client ID of the GitHub App. [Parameter()] [string] $ClientID, # The interval to wait between polling for the token. [Parameter()] [int] $Interval = 5 ) do { if ($Refresh) { $response = Request-GitHubToken -Refresh -ClientID $ClientID -RefreshToken $script:RefreshToken } else { $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 } '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 public/DeviceFlow/Wait-GitHubToken.ps1 #region - From public/Markdown/Get-GitHubMarkdown.ps1 function Get-GitHubMarkdown { <# .NOTES https://docs.github.com/en/rest/reference/meta#github-api-root #> [CmdletBinding()] param ( [Parameter()] [switch] $Text, [Parameter()] [ValidateSet('markdown', 'gfm')] [string] $Mode, [Parameter()] [string] $Context ) $InputObject = @{ APIEndpoint = '/markdown' Body = @{ context = $Context mode = $Mode text = $Text } Method = 'POST' } $Response = Invoke-GitHubAPI @InputObject $Response } #endregion - From public/Markdown/Get-GitHubMarkdown.ps1 #region - From public/Markdown/Get-GitHubMarkdownRaw.ps1 function Get-GitHubMarkdownRaw { <# .NOTES https://docs.github.com/en/rest/reference/meta#github-api-root #> [CmdletBinding()] param ( [Parameter()] [switch] $Text, [Parameter()] [string] $Context ) $InputObject = @{ APIEndpoint = '/markdown/raw' ContentType = 'text/plain' Data = $Text Method = 'POST' } $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 ) $InputObject = @{ 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 ) begin {} process { # API Reference # https://docs.github.com/en/rest/reference/repos#get-all-environments $InputObject = @{ APIEndpoint = "/repos/$Owner/$Repo/environments" Method = 'GET' } $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:AccessToken, [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 = "/repositories/$RepoID/environments/$EnvironmentName/secrets" Method = 'GET' } $Response = Invoke-GitHubAPI @InputObject $Response } 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, [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' } $Response = Invoke-GitHubAPI @InputObject $Response } end {} } #endregion - From public/Deployments/Update-GitHubEnvironment.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 -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(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 = 'gist read:org repo workflow', # Refresh the access token. [Parameter(ParameterSetName = 'Refresh')] [switch] $Refresh, # The personal access token to use for authentication. [Parameter(ParameterSetName = 'PAT')] [String] $Token ) $vault = Get-SecretVault | Where-Object -Property ModuleName -EQ 'Microsoft.PowerShell.SecretStore' if ($null -eq $vault) { Initialize-SecretVault $vault = Get-SecretVault | Where-Object -Property ModuleName -EQ 'Microsoft.PowerShell.SecretStore' } #Get the parameter set that is used switch ($PSCmdlet.ParameterSetName) { 'Refresh' { $tokenResponse = Invoke-GitHubDeviceFlowLogin -Refresh -Mode $script:AuthMode } 'DeviceFlow' { $tokenResponse = Invoke-GitHubDeviceFlowLogin -Mode $Mode -Scope $Scope } 'PAT' { $script:AccessToken = $Token Set-Secret -Name 'GitHubPS.AccessToken' -Secret $script:AccessToken -Vault $vault.Name Remove-Secret -Name 'GitHubPS.AccessToken.ExpirationDate'-Vault $vault.Name -ErrorAction SilentlyContinue Remove-Secret -Name 'GitHubPS.RefreshToken'-Vault $vault.Name -ErrorAction SilentlyContinue Remove-Secret -Name 'GitHubPS.RefreshToken.ExpirationDate'-Vault $vault.Name -ErrorAction SilentlyContinue Remove-Secret -Name 'GitHubPS.Scope' -Vault $vault.Name -ErrorAction SilentlyContinue $script:AuthMode = 'PAT' Set-Secret -Name 'GitHubPS.AuthMode' -Secret $script:AuthMode -Vault $vault.Name } } if ($tokenResponse) { $script:AccessToken = $tokenResponse.access_token $accessTokenExpirationDate = (Get-Date).AddSeconds($tokenResponse.expires_in) $script:RefreshToken = $tokenResponse.refresh_token $refreshTokenExpirationDate = (Get-Date).AddSeconds($tokenResponse.refresh_token_expires_in) $tokenScope = $tokenResponse.scope $script:AuthMode = $Mode Set-Secret -Name 'GitHubPS.AccessToken' -Secret $script:AccessToken -Vault $vault.Name Set-Secret -Name 'GitHubPS.AccessToken.ExpirationDate' -Secret $accessTokenExpirationDate.toString() -Vault $vault.Name Set-Secret -Name 'GitHubPS.RefreshToken' -Secret $script:RefreshToken -Vault $vault.Name Set-Secret -Name 'GitHubPS.RefreshToken.ExpirationDate' -Secret $refreshTokenExpirationDate.toString() -Vault $vault.Name Set-Secret -Name 'GitHubPS.Scope' -Secret $tokenScope -Vault $vault.Name Set-Secret -Name 'GitHubPS.AuthMode' -Secret $script:AuthMode -Vault $vault.Name } $user = Get-GitHubUser Write-Host '✓ ' -ForegroundColor Green -NoNewline Write-Host "Logged in as $($user.name) (@$($user.login))!" } #endregion - From public/Auth/Connect-GitHubAccount.ps1 #region - From public/Auth/Disconnect-GitHubAccount.ps1 function Disconnect-GitHubAccount { [OutputType([void])] [CmdletBinding()] param () $user = Get-GitHubUser $Vault = Get-SecretVault | Where-Object -Property ModuleName -EQ 'Microsoft.PowerShell.SecretStore' Get-SecretInfo -Name 'GitHubPS.*' -Vault $Vault.Name | ForEach-Object { Remove-Secret -Name $_.Name -Vault $_.VaultName } $script:ClientID = '' $script:AuthMode = '' $script:AccessToken = '' $script:RefreshToken = '' $script:Owner = '' $script:Repo = '' Write-Host "✓ " -ForegroundColor Green -NoNewline Write-Host "Logged out of account $($user.name) (@$($user.login))!" } #endregion - From public/Auth/Disconnect-GitHubAccount.ps1 #region - From public/Auth/Get-GitHubAccountStatus.ps1 function Get-GitHubAccountStatus { } #endregion - From public/Auth/Get-GitHubAccountStatus.ps1 Export-ModuleMember -Function 'Disable-GitHubWorkflow','Enable-GitHubWorkflow','Get-GitHubWorkflow','Get-GitHubWorkflowRun','Get-GitHubWorkflowUsage','Remove-GitHubWorkflowRun','Start-GitHubWorkflow','Start-GitHubWorkflowReRun','Stop-GitHubWorkflowRun','Connect-GitHubAccount','Disconnect-GitHubAccount','Get-GitHubAccountStatus','Get-GitHubRepoBranch','Get-GithubConfig','Invoke-GitHubAPI','Set-GitHubConfig','Get-GitHubEnvironment','Get-GitHubEnvironmentSecrets','Update-GitHubEnvironment','Invoke-GitHubDeviceFlowLogin','Request-GitHubToken','Wait-GitHubToken','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 '*' |