From 8265e0d0885295db3a7e8ebafcaf8ae677cc15e4 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sat, 23 Sep 2023 08:26:17 +0200 Subject: [PATCH 01/30] Add an icon that might be the new icon --- media/github.png | Bin 0 -> 22902 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 media/github.png diff --git a/media/github.png b/media/github.png new file mode 100644 index 0000000000000000000000000000000000000000..9629d048cc775ec3e5f4a1c594d8ea642a0f3b65 GIT binary patch literal 22902 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 literal 0 HcmV?d00001 From d28fd3b31acb3a5103c3e32d83bdd15b3d0af8d3 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sat, 23 Sep 2023 09:09:22 +0200 Subject: [PATCH 02/30] Added Auth component --- .github/workflows/Test.GitHub.yml | 81 +++++++++++++ README.md | 29 ++++- src/GitHub/classes/Data/App.ps1 | 8 ++ src/GitHub/classes/Data/Config.ps1 | 21 +++- src/GitHub/en_US/about_Config.help.txt | 29 +++-- .../DeviceFlow/Check-GitHubAccessToken.ps1 | 12 ++ .../Invoke-GitHubDeviceFlowLogin.ps1 | 60 +++++++++ .../DeviceFlow/Request-GitHubAccessToken.ps1 | 75 ++++++++++++ .../DeviceFlow/Request-GitHubDeviceCode.ps1 | 59 +++++++++ .../DeviceFlow/Wait-GitHubAccessToken.ps1 | 114 ++++++++++++++++++ .../public/Auth/Connect-GitHubAccount.ps1 | 110 +++++++++++++++++ .../public/Auth/Disconnect-GitHubAccount.ps1 | 11 ++ .../public/Config/Reset-GitHubConfig.ps1 | 14 ++- src/GitHub/public/Config/Set-GitHubConfig.ps1 | 17 ++- src/GitHub/public/loader.ps1 | 9 ++ tools/utilities/Local-Testing.ps1 | 6 + 16 files changed, 639 insertions(+), 16 deletions(-) create mode 100644 .github/workflows/Test.GitHub.yml create mode 100644 src/GitHub/classes/Data/App.ps1 create mode 100644 src/GitHub/private/Auth/DeviceFlow/Check-GitHubAccessToken.ps1 create mode 100644 src/GitHub/private/Auth/DeviceFlow/Invoke-GitHubDeviceFlowLogin.ps1 create mode 100644 src/GitHub/private/Auth/DeviceFlow/Request-GitHubAccessToken.ps1 create mode 100644 src/GitHub/private/Auth/DeviceFlow/Request-GitHubDeviceCode.ps1 create mode 100644 src/GitHub/private/Auth/DeviceFlow/Wait-GitHubAccessToken.ps1 create mode 100644 src/GitHub/public/Auth/Connect-GitHubAccount.ps1 create mode 100644 src/GitHub/public/Auth/Disconnect-GitHubAccount.ps1 diff --git a/.github/workflows/Test.GitHub.yml b/.github/workflows/Test.GitHub.yml new file mode 100644 index 000000000..a88266690 --- /dev/null +++ b/.github/workflows/Test.GitHub.yml @@ -0,0 +1,81 @@ +name: Test [GitHub] + +on: + workflow_dispatch: + +permissions: write-all + +jobs: + TestGitHub: + name: Test GitHub + if: always() + strategy: + matrix: + os: [ubuntu-latest, windows-latest, macos-latest] + runs-on: ${{ matrix.os }} + env: + GH_TOKEN: ${{ github.token }} # Used for GitHub CLI authentication + steps: + - name: Test Authentication using auto PAT + if: always() + shell: pwsh + run: | + Write-Output '::group::[Debug info] - Environment variables' + $env:GITHUB_REPOSITORY_NAME = $env:GITHUB_REPOSITORY.Split('/')[1] + Get-ChildItem Env: + Write-Output '::endgroup::' + + Write-Output '::group::[Debug info] - File structure' + Write-Verbose "Current directory: $((Get-Location).Path)" -Verbose + Write-Verbose "------------------------------------" -Verbose + Write-Verbose "Current directory content:" -Verbose + Get-ChildItem -Path . -Recurse | Select-Object -ExpandProperty FullName | Sort-Object + Write-Output '::endgroup::' + + Write-Output '::group::Install-Module -Name GitHub -Verbose -Force' + Install-Module -Name GitHub -Verbose -Force + Write-Output '::endgroup::' + + Write-Output '::group::Get-GitHubConfig' + Get-GitHubConfig + Write-Output '::endgroup::' + + Write-Output '::group::Get-GitHubWorkflow -Repo $env:GITHUB_REPOSITORY_NAME -Owner $env:GITHUB_REPOSITORY_OWNER -Verbose' + Get-GitHubWorkflow -Repo $env:GITHUB_REPOSITORY_NAME -Owner $env:GITHUB_REPOSITORY_OWNER -Verbose + Write-Output '::endgroup::' + + - name: Test Authentication using Specific PAT + if: always() + shell: pwsh + run: | + Write-Output '::group::[Debug info] - Environment variables' + $env:GITHUB_REPOSITORY_NAME = $env:GITHUB_REPOSITORY.Split('/')[1] + Get-ChildItem Env: + Write-Output '::endgroup::' + + Write-Output '::group::[Debug info] - File structure' + Write-Verbose "Current directory: $((Get-Location).Path)" -Verbose + Write-Verbose "------------------------------------" -Verbose + Write-Verbose "Current directory content:" -Verbose + Get-ChildItem -Path . -Recurse | Select-Object -ExpandProperty FullName | Sort-Object + Write-Output '::endgroup::' + + Write-Output '::group::Install-Module -Name GitHub -Verbose -Force' + Install-Module -Name GitHub -Verbose -Force + Write-Output '::endgroup::' + + Write-Output '::group::Get-GitHubConfig' + Get-GitHubConfig + Write-Output '::endgroup::' + + Write-Output '::group::Connect-GitHubAccount -AccessToken $env:GH_TOKEN -Verbose' + Connect-GitHubAccount -AccessToken $env:GH_TOKEN -Verbose + Write-Output '::endgroup::' + + Write-Output '::group::Get-GitHubConfig' + Get-GitHubConfig + Write-Output '::endgroup::' + + Write-Output '::group::Get-GitHubWorkflow -Repo $env:GITHUB_REPOSITORY_NAME -Owner $env:GITHUB_REPOSITORY_OWNER -Verbose' + Get-GitHubWorkflow -Repo $env:GITHUB_REPOSITORY_NAME -Owner $env:GITHUB_REPOSITORY_OWNER -Verbose + Write-Output '::endgroup::' diff --git a/README.md b/README.md index f114995de..293c5a4aa 100644 --- a/README.md +++ b/README.md @@ -26,11 +26,34 @@ To dive into the world of GitHub automation with PowerShell, follow these steps: 1. **Installation**: Download and install the GitHub PowerShell module from the provided link or the PowerShell Gallery. -2. **Authentication**: Authenticate using your GitHub credentials or access tokens to begin executing commands. + ```powershell + Install-Module -Name GitHub -Force -AllowClobber + ``` -3. **Command Exploration**: Familiarize yourself with the available cmdlets using the module's comprehensive documentation or inline help. +1. **Authentication**: Authenticate using your GitHub credentials or access tokens to begin executing commands. -4. **Sample Scripts**: Check out sample scripts and usage patterns to jumpstart your automation tasks on GitHub. +Logging in using device flow: +```powershell +Connect-GitHubAccount + +Please visit: https://github.com/login/device +and enter code: ABCD-1234 +Successfully authenticated! +``` + +Logging in using PAT token: +```powershell +>_ Connect-GitHubAccount -AccessToken 'ghp_abcdefghklmnopqrstuvwxyz123456789123' +>_ +``` + +2. **Command Exploration**: Familiarize yourself with the available cmdlets using the module's comprehensive documentation or inline help. + + ```powershell + Get-Command -Module GitHub + ``` + +3. **Sample Scripts**: Check out sample scripts and usage patterns to jumpstart your automation tasks on GitHub. ## More Information & Resources diff --git a/src/GitHub/classes/Data/App.ps1 b/src/GitHub/classes/Data/App.ps1 new file mode 100644 index 000000000..ba32860f0 --- /dev/null +++ b/src/GitHub/classes/Data/App.ps1 @@ -0,0 +1,8 @@ +$script:App = [pscustomobject]@{ + GitHubApp = [pscustomobject]@{ + ClientID = 'Iv1.f26b61bc99e69405' # $script:App.GitHubApp.ClientID + } + OAuthApp = [pscustomobject]@{ + ClientID = '7204ae9b0580f2cb8288' # $script:App.OAuthApp.ClientID + } +} diff --git a/src/GitHub/classes/Data/Config.ps1 b/src/GitHub/classes/Data/Config.ps1 index aba83cdc6..e0686728b 100644 --- a/src/GitHub/classes/Data/Config.ps1 +++ b/src/GitHub/classes/Data/Config.ps1 @@ -4,7 +4,26 @@ BaseURI = 'https://api.github.com' # $script:ConfigTemplate.App.API.BaseURI Version = '2022-11-28' # $script:ConfigTemplate.App.API.Version } - Defaults = [pscustomobject]@{} # $script:ConfigTemplate.App.Defaults + 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 diff --git a/src/GitHub/en_US/about_Config.help.txt b/src/GitHub/en_US/about_Config.help.txt index 61b5602f0..aca48b2cc 100644 --- a/src/GitHub/en_US/about_Config.help.txt +++ b/src/GitHub/en_US/about_Config.help.txt @@ -20,17 +20,30 @@ LONG DESCRIPTION | Secret | pscustomobject | {Name} | | | Secret.Name | string | 'Config' | The name of the secret. | - Name: Config Path: \classes\Data\Config.ps1 - | Name | Type | Static Value | Description | - | --------------- | ----------------- | ------------------------ | ------------------------ | - | App | pscustomobject | {API, Defaults} | | - | App.API | pscustomobject | {BaseURI, Version} | | - | App.API.BaseURI | string | 'https://api.github.com' | The GitHub API Base URI. | - | App.API.Version | string | '2022-11-28' | The GitHub API version. | - | App.Defaults | pscustomobject | {} | | + | Name | Type | Static Value | Description | + | --------------------------------------- | ----------------- | --------------------------------------------------- | --------------------------------- | + | App | pscustomobject | {API, Defaults} | | + | App.API | pscustomobject | {BaseURI, Version} | | + | App.API.BaseURI | string | 'https://api.github.com' | The GitHub API Base URI. | + | App.API.Version | string | '2022-11-28' | The GitHub API version. | + | App.Defaults | pscustomobject | {} | | + | User | pscustomobject | {Auth, Defaults} | | + | User.Auth | pscustomobject | {AccessToken, ClientID, Mode, RefreshToken, Scope} | | + | User.Auth.AccessToken | pscustomobject | {Value, ExpirationDate} | The access token. | + | User.Auth.AccessToken.Value | string | '' | The access token value. | + | User.Auth.AccessToken.ExpirationDate | datetime | [datetime]::MinValue | The access token expiration date. | + | User.Auth.ClientID | string | '' | The client ID. | + | User.Auth.Mode | string | '' | The authentication mode. | + | User.Auth.RefreshToken | pscustomobject | {Value, ExpirationDate} | The refresh token. | + | User.Auth.RefreshToken.Value | string | '' | The refresh token value. | + | User.Auth.RefreshToken.ExpirationDate | datetime | [datetime]::MinValue | The refresh token expiration date.| + | User.Auth.Scope | string | '' | The scope. | + | User.Defaults | pscustomobject | {Owner, Repo} | | + | User.Defaults.Owner | string | '' | The default owner. | + | User.Defaults.Repo | string | '' | The default repository. | Functions provided in the module: diff --git a/src/GitHub/private/Auth/DeviceFlow/Check-GitHubAccessToken.ps1 b/src/GitHub/private/Auth/DeviceFlow/Check-GitHubAccessToken.ps1 new file mode 100644 index 000000000..675caa9ac --- /dev/null +++ b/src/GitHub/private/Auth/DeviceFlow/Check-GitHubAccessToken.ps1 @@ -0,0 +1,12 @@ +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)." +} diff --git a/src/GitHub/private/Auth/DeviceFlow/Invoke-GitHubDeviceFlowLogin.ps1 b/src/GitHub/private/Auth/DeviceFlow/Invoke-GitHubDeviceFlowLogin.ps1 new file mode 100644 index 000000000..17a8df42d --- /dev/null +++ b/src/GitHub/private/Auth/DeviceFlow/Invoke-GitHubDeviceFlowLogin.ps1 @@ -0,0 +1,60 @@ +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, + + # Refresh the access token. + [Parameter()] + [switch] $Refresh + ) + + do { + if ($Refresh) { + $tokenResponse = Wait-GitHubAccessToken -ClientID $ClientID -Refresh + } 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 +} diff --git a/src/GitHub/private/Auth/DeviceFlow/Request-GitHubAccessToken.ps1 b/src/GitHub/private/Auth/DeviceFlow/Request-GitHubAccessToken.ps1 new file mode 100644 index 000000000..fda7406cb --- /dev/null +++ b/src/GitHub/private/Auth/DeviceFlow/Request-GitHubAccessToken.ps1 @@ -0,0 +1,75 @@ +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 $_ + } +} diff --git a/src/GitHub/private/Auth/DeviceFlow/Request-GitHubDeviceCode.ps1 b/src/GitHub/private/Auth/DeviceFlow/Request-GitHubDeviceCode.ps1 new file mode 100644 index 000000000..bdc393b51 --- /dev/null +++ b/src/GitHub/private/Auth/DeviceFlow/Request-GitHubDeviceCode.ps1 @@ -0,0 +1,59 @@ +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 $_ + } +} + diff --git a/src/GitHub/private/Auth/DeviceFlow/Wait-GitHubAccessToken.ps1 b/src/GitHub/private/Auth/DeviceFlow/Wait-GitHubAccessToken.ps1 new file mode 100644 index 000000000..6a0e00025 --- /dev/null +++ b/src/GitHub/private/Auth/DeviceFlow/Wait-GitHubAccessToken.ps1 @@ -0,0 +1,114 @@ + +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 +} diff --git a/src/GitHub/public/Auth/Connect-GitHubAccount.ps1 b/src/GitHub/public/Auth/Connect-GitHubAccount.ps1 new file mode 100644 index 000000000..8493c9b8d --- /dev/null +++ b/src/GitHub/public/Auth/Connect-GitHubAccount.ps1 @@ -0,0 +1,110 @@ +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' -AccessToken '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, + + # 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 + } + + switch ($PSCmdlet.ParameterSetName) { + 'Refresh' { + Write-Verbose 'Refreshing access token...' + $tokenResponse = Invoke-GitHubDeviceFlowLogin -ClientID $clientID -Refresh + } + 'DeviceFlow' { + Write-Verbose 'Logging in using device flow...' + if ([string]::IsNullOrEmpty($Scope) -and ($Mode -eq 'OAuthApp')) { + $Scope = 'gist read:org repo workflow' + } + $clientID = $script:App.$Mode.ClientID + $tokenResponse = Invoke-GitHubDeviceFlowLogin -ClientID $clientID -Scope $Scope + $script:Config.User.Auth.Mode = $Mode + } + '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 + + $user = Get-GitHubUser + Write-Host '✓ ' -ForegroundColor Green -NoNewline + Write-Host "Logged in as $($user.name) (@$($user.login))!" +} diff --git a/src/GitHub/public/Auth/Disconnect-GitHubAccount.ps1 b/src/GitHub/public/Auth/Disconnect-GitHubAccount.ps1 new file mode 100644 index 000000000..384329d25 --- /dev/null +++ b/src/GitHub/public/Auth/Disconnect-GitHubAccount.ps1 @@ -0,0 +1,11 @@ +function Disconnect-GitHubAccount { + [OutputType([void])] + [CmdletBinding()] + param () + + $user = Get-GitHubUser + Reset-GitHubConfig + + Write-Host "✓ " -ForegroundColor Green -NoNewline + Write-Host "Logged out of account $($user.name) (@$($user.login))!" +} diff --git a/src/GitHub/public/Config/Reset-GitHubConfig.ps1 b/src/GitHub/public/Config/Reset-GitHubConfig.ps1 index beb9bd323..2e0a1c2ab 100644 --- a/src/GitHub/public/Config/Reset-GitHubConfig.ps1 +++ b/src/GitHub/public/Config/Reset-GitHubConfig.ps1 @@ -20,12 +20,13 @@ [OutputType([void])] [CmdletBinding()] param( + # Reset the GitHub configuration for a specific scope. [Parameter()] - [ValidateSet('App', 'App.API', 'App.Defaults', 'All')] + [ValidateSet('App', 'App.API', 'App.Defaults', 'User', 'User.Auth', 'User.Defaults', 'All')] [string] $Scope = 'All' ) - switch($Scope) { + switch ($Scope) { 'App' { $script:Config.App = $script:ConfigTemplate.App } @@ -35,6 +36,15 @@ '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:ConfigTemplateDefaults } diff --git a/src/GitHub/public/Config/Set-GitHubConfig.ps1 b/src/GitHub/public/Config/Set-GitHubConfig.ps1 index e802cdc35..e50bf085d 100644 --- a/src/GitHub/public/Config/Set-GitHubConfig.ps1 +++ b/src/GitHub/public/Config/Set-GitHubConfig.ps1 @@ -20,17 +20,30 @@ # Set the GitHub API Version. [Parameter()] - [string] $APIVersion + [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 } diff --git a/src/GitHub/public/loader.ps1 b/src/GitHub/public/loader.ps1 index 100a83fbe..657c13126 100644 --- a/src/GitHub/public/loader.ps1 +++ b/src/GitHub/public/loader.ps1 @@ -1,2 +1,11 @@ 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 +} diff --git a/tools/utilities/Local-Testing.ps1 b/tools/utilities/Local-Testing.ps1 index 84fdbd8ef..c67038d21 100644 --- a/tools/utilities/Local-Testing.ps1 +++ b/tools/utilities/Local-Testing.ps1 @@ -7,4 +7,10 @@ Get-SecretVault | Unregister-SecretVault Get-Module -Name GitHub -ListAvailable Install-Module -Name GitHub -Verbose -Force -AllowPrerelease Clear-Host +Connect-GitHubAccount Get-GitHubConfig +Get-GitHubContext + +Connect-GitHubAccount -Refresh -Verbose + +Disconnect-GitHubAccount -Verbose From 8ba1a4420e147695036e27aa23581ceca675d9af Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sat, 23 Sep 2023 09:39:45 +0200 Subject: [PATCH 03/30] Add docs and started on Auth topic --- src/GitHub/en_US/about_Auth.help.txt | 16 +++++++++++ .../public/Auth/Connect-GitHubAccount.ps1 | 28 +++++++++++++------ .../public/Auth/Disconnect-GitHubAccount.ps1 | 26 +++++++++++++++-- tools/utilities/Local-Testing.ps1 | 4 ++- 4 files changed, 63 insertions(+), 11 deletions(-) create mode 100644 src/GitHub/en_US/about_Auth.help.txt diff --git a/src/GitHub/en_US/about_Auth.help.txt b/src/GitHub/en_US/about_Auth.help.txt new file mode 100644 index 000000000..4fd600d06 --- /dev/null +++ b/src/GitHub/en_US/about_Auth.help.txt @@ -0,0 +1,16 @@ +TOPIC + about_Auth + +SHORT DESCRIPTION + +LONG DESCRIPTION + +EXAMPLES + +KEYWORDS + GitHub + PowerShell + SecretManagement + SecretStore + +SEE ALSO diff --git a/src/GitHub/public/Auth/Connect-GitHubAccount.ps1 b/src/GitHub/public/Auth/Connect-GitHubAccount.ps1 index 8493c9b8d..5f6ba0246 100644 --- a/src/GitHub/public/Auth/Connect-GitHubAccount.ps1 +++ b/src/GitHub/public/Auth/Connect-GitHubAccount.ps1 @@ -13,23 +13,36 @@ the app will be able to make API calls with a new access token. .EXAMPLE - Connect-GitHubAccount -Owner 'MyOrg' -Repo 'MyRepo' -AccessToken 'ghp_####' + Connect-GitHubAccount - Connects to GitHub using a personal access token and sets the Owner and Repo default variables. + Connects to GitHub using a device flow login. .EXAMPLE - Connect-GitHubAccount -Owner 'MyOrg' -Repo 'MyRepo' + Connect-GitHubAccount -AccessToken 'ghp_####' - Sets the Owner and Repo default variables. + Connects to GitHub using a personal access token (PAT). .EXAMPLE - Connect-GitHubAccount + Connect-GitHubAccount -Refresh + + Refreshes the access token. + + .EXAMPLE + Connect-GitHubAccount -Mode 'OAuthApp' -Scope 'gist read:org repo workflow' - Connects to GitHub using a device code login. + 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. @@ -104,7 +117,6 @@ Save-GitHubConfig - $user = Get-GitHubUser Write-Host '✓ ' -ForegroundColor Green -NoNewline - Write-Host "Logged in as $($user.name) (@$($user.login))!" + Write-Host "Logged in to GitHub!" } diff --git a/src/GitHub/public/Auth/Disconnect-GitHubAccount.ps1 b/src/GitHub/public/Auth/Disconnect-GitHubAccount.ps1 index 384329d25..6df6137a1 100644 --- a/src/GitHub/public/Auth/Disconnect-GitHubAccount.ps1 +++ b/src/GitHub/public/Auth/Disconnect-GitHubAccount.ps1 @@ -1,11 +1,33 @@ 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 () - $user = Get-GitHubUser Reset-GitHubConfig Write-Host "✓ " -ForegroundColor Green -NoNewline - Write-Host "Logged out of account $($user.name) (@$($user.login))!" + Write-Host "Logged out of GitHub!" } diff --git a/tools/utilities/Local-Testing.ps1 b/tools/utilities/Local-Testing.ps1 index c67038d21..631dda1a3 100644 --- a/tools/utilities/Local-Testing.ps1 +++ b/tools/utilities/Local-Testing.ps1 @@ -3,10 +3,12 @@ Get-Module -Name GitHub -ListAvailable | Remove-Module -Force Get-Module -Name GitHub -ListAvailable | Uninstall-Module -Force -AllVersions Get-SecretVault | Unregister-SecretVault - +Get-SecretVault Get-Module -Name GitHub -ListAvailable Install-Module -Name GitHub -Verbose -Force -AllowPrerelease Clear-Host +Get-Command -Module GitHub +Get-Variable | Select-Object Name, Module, ModuleName Connect-GitHubAccount Get-GitHubConfig Get-GitHubContext From 4a0d85d55a77c910fa5f62765f1de6550baee1d5 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sat, 23 Sep 2023 10:13:56 +0200 Subject: [PATCH 04/30] Fix for refresh token --- .../Auth/DeviceFlow/Invoke-GitHubDeviceFlowLogin.ps1 | 8 ++++---- src/GitHub/public/Auth/Connect-GitHubAccount.ps1 | 5 +++-- tools/utilities/Local-Testing.ps1 | 4 ++-- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/GitHub/private/Auth/DeviceFlow/Invoke-GitHubDeviceFlowLogin.ps1 b/src/GitHub/private/Auth/DeviceFlow/Invoke-GitHubDeviceFlowLogin.ps1 index 17a8df42d..e0ee76f90 100644 --- a/src/GitHub/private/Auth/DeviceFlow/Invoke-GitHubDeviceFlowLogin.ps1 +++ b/src/GitHub/private/Auth/DeviceFlow/Invoke-GitHubDeviceFlowLogin.ps1 @@ -31,14 +31,14 @@ [Parameter()] [string] $Scope, - # Refresh the access token. + # The refresh token to use for re-authentication. [Parameter()] - [switch] $Refresh + [switch] $RefreshToken ) do { - if ($Refresh) { - $tokenResponse = Wait-GitHubAccessToken -ClientID $ClientID -Refresh + if ($RefreshToken) { + $tokenResponse = Wait-GitHubAccessToken -ClientID $ClientID -RefreshToken $RefreshToken } else { $deviceCodeResponse = Request-GitHubDeviceCode -ClientID $ClientID -Scope $Scope diff --git a/src/GitHub/public/Auth/Connect-GitHubAccount.ps1 b/src/GitHub/public/Auth/Connect-GitHubAccount.ps1 index 5f6ba0246..1ffbc4525 100644 --- a/src/GitHub/public/Auth/Connect-GitHubAccount.ps1 +++ b/src/GitHub/public/Auth/Connect-GitHubAccount.ps1 @@ -81,17 +81,18 @@ $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 -Refresh + $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' } - $clientID = $script:App.$Mode.ClientID $tokenResponse = Invoke-GitHubDeviceFlowLogin -ClientID $clientID -Scope $Scope $script:Config.User.Auth.Mode = $Mode } diff --git a/tools/utilities/Local-Testing.ps1 b/tools/utilities/Local-Testing.ps1 index 631dda1a3..b249824de 100644 --- a/tools/utilities/Local-Testing.ps1 +++ b/tools/utilities/Local-Testing.ps1 @@ -8,9 +8,9 @@ Get-Module -Name GitHub -ListAvailable Install-Module -Name GitHub -Verbose -Force -AllowPrerelease Clear-Host Get-Command -Module GitHub -Get-Variable | Select-Object Name, Module, ModuleName +Get-Variable | Where-Object -Property Module -ne $null | Select-Object Name, Module, ModuleName Connect-GitHubAccount -Get-GitHubConfig +Get-GitHubConfig | ConvertTo-Json -Depth 100 Get-GitHubContext Connect-GitHubAccount -Refresh -Verbose From 8d35b218c48933f4afa81a158129b85bf78f56fe Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sat, 23 Sep 2023 10:23:07 +0200 Subject: [PATCH 05/30] Fix datatype on refreshtoken --- .../private/Auth/DeviceFlow/Invoke-GitHubDeviceFlowLogin.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/GitHub/private/Auth/DeviceFlow/Invoke-GitHubDeviceFlowLogin.ps1 b/src/GitHub/private/Auth/DeviceFlow/Invoke-GitHubDeviceFlowLogin.ps1 index e0ee76f90..93762f95c 100644 --- a/src/GitHub/private/Auth/DeviceFlow/Invoke-GitHubDeviceFlowLogin.ps1 +++ b/src/GitHub/private/Auth/DeviceFlow/Invoke-GitHubDeviceFlowLogin.ps1 @@ -33,7 +33,7 @@ # The refresh token to use for re-authentication. [Parameter()] - [switch] $RefreshToken + [string] $RefreshToken ) do { From b7fffec2e47db7d281228116abc6b4a58030b703 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sat, 23 Sep 2023 10:34:25 +0200 Subject: [PATCH 06/30] Fix to store CLient ID + reset UserAuth when DeviceFlow --- src/GitHub/public/Auth/Connect-GitHubAccount.ps1 | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/GitHub/public/Auth/Connect-GitHubAccount.ps1 b/src/GitHub/public/Auth/Connect-GitHubAccount.ps1 index 1ffbc4525..ed59ea3e2 100644 --- a/src/GitHub/public/Auth/Connect-GitHubAccount.ps1 +++ b/src/GitHub/public/Auth/Connect-GitHubAccount.ps1 @@ -93,8 +93,10 @@ 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...' From ab8c260de146d393456252f914c9e6dc456a8942 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sat, 23 Sep 2023 11:00:17 +0200 Subject: [PATCH 07/30] Fix discon var --- src/GitHub/public/Config/Reset-GitHubConfig.ps1 | 2 +- tools/utilities/Local-Testing.ps1 | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/GitHub/public/Config/Reset-GitHubConfig.ps1 b/src/GitHub/public/Config/Reset-GitHubConfig.ps1 index 2e0a1c2ab..585f4f6de 100644 --- a/src/GitHub/public/Config/Reset-GitHubConfig.ps1 +++ b/src/GitHub/public/Config/Reset-GitHubConfig.ps1 @@ -46,7 +46,7 @@ $script:Config.User.Defaults = $script:ConfigTemplate.User.Defaults } 'All' { - $script:Config = $script:ConfigTemplateDefaults + $script:Config = $script:ConfigTemplate } } Save-GitHubConfig diff --git a/tools/utilities/Local-Testing.ps1 b/tools/utilities/Local-Testing.ps1 index b249824de..f364c71c9 100644 --- a/tools/utilities/Local-Testing.ps1 +++ b/tools/utilities/Local-Testing.ps1 @@ -6,6 +6,7 @@ Get-SecretVault | Unregister-SecretVault Get-SecretVault Get-Module -Name GitHub -ListAvailable Install-Module -Name GitHub -Verbose -Force -AllowPrerelease +Import-Module -Name GitHub -Verbose Clear-Host Get-Command -Module GitHub Get-Variable | Where-Object -Property Module -ne $null | Select-Object Name, Module, ModuleName From 577e295568342466dd278357e6b00df6ea6b25d8 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sat, 23 Sep 2023 13:24:02 +0200 Subject: [PATCH 08/30] ok changes --- src/GitHub/public/Config/Save-GitHubConfig.ps1 | 4 ++-- src/GitHub/public/Config/Set-GitHubConfig.ps1 | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/GitHub/public/Config/Save-GitHubConfig.ps1 b/src/GitHub/public/Config/Save-GitHubConfig.ps1 index 0288e3a03..2724b03c1 100644 --- a/src/GitHub/public/Config/Save-GitHubConfig.ps1 +++ b/src/GitHub/public/Config/Save-GitHubConfig.ps1 @@ -19,6 +19,6 @@ function Save-GitHubConfig { [CmdletBinding()] param() - $config = $script:Config | ConvertTo-Json -Depth 100 - Set-Secret -Name $script:Secret.Name -Secret $config -Vault $script:SecretVault.Name + $configJson = $script:Config | ConvertTo-Json -Depth 100 + Set-Secret -Name $script:Secret.Name -Secret $configJson -Vault $script:SecretVault.Name } diff --git a/src/GitHub/public/Config/Set-GitHubConfig.ps1 b/src/GitHub/public/Config/Set-GitHubConfig.ps1 index e50bf085d..8b85baf7a 100644 --- a/src/GitHub/public/Config/Set-GitHubConfig.ps1 +++ b/src/GitHub/public/Config/Set-GitHubConfig.ps1 @@ -33,16 +33,16 @@ switch ($PSBoundParameters.Keys) { 'APIBaseURI' { - $script:ConfigTemplate.App.API.BaseURI = $APIBaseURI + $script:Config.App.API.BaseURI = $APIBaseURI } 'APIVersion' { - $script:ConfigTemplate.App.API.Version = $APIVersion + $script:Config.App.API.Version = $APIVersion } 'Owner' { - $script:ConfigTemplate.User.Defaults.Owner = $Owner + $script:Config.User.Defaults.Owner = $Owner } 'Repo' { - $script:ConfigTemplate.User.Defaults.Repo = $Repo + $script:Config.User.Defaults.Repo = $Repo } } Save-GitHubConfig From 7f035e1f193d91bd215084a31ae66efe7f19055f Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sat, 23 Sep 2023 14:42:50 +0200 Subject: [PATCH 09/30] Create class for config --- src/GitHub/classes/Config.ps1 | 72 +++++++++++++++++++++++++ src/GitHub/classes/Data/App.ps1 | 10 ++-- src/GitHub/classes/Data/Config.ps1 | 29 ---------- src/GitHub/classes/Data/SecretVault.ps1 | 4 +- 4 files changed, 79 insertions(+), 36 deletions(-) create mode 100644 src/GitHub/classes/Config.ps1 delete mode 100644 src/GitHub/classes/Data/Config.ps1 diff --git a/src/GitHub/classes/Config.ps1 b/src/GitHub/classes/Config.ps1 new file mode 100644 index 000000000..7febb0161 --- /dev/null +++ b/src/GitHub/classes/Config.ps1 @@ -0,0 +1,72 @@ +Class Token { + [string] $Value + [datetime] $ExpirationDate + + Token() { + $this.Value = '' + $this.ExpirationDate = [datetime]::MinValue + } +} + +Class Auth { + [Token] $AccessToken + [string] $ClientID + [string] $Mode + [Token] $RefreshToken + [string] $Scope + + Auth() { + $this.AccessToken = [Token]::new() + $this.ClientID = '' + $this.Mode = '' + $this.RefreshToken = [Token]::new() + $this.Scope = '' + } +} + +Class UserDefaults { + [string] $Owner + [string] $Repo + + UserDefaults() { + $this.Owner = '' + $this.Repo = '' + } +} +Class User { + [Auth] $Auth + [UserDefaults] $Defaults + + User() { + $this.Auth = [Auth]::new() + $this.Defaults = [UserDefaults]::new() + } +} + +Class API { + [string] $BaseURI + [string] $Version + + API() { + $this.BaseURI = '' + $this.Version = '' + } +} + +Class App { + [API] $API + + App() { + $this.API = [API]::new() + } +} + +Class Config { + [App] $App + [User] $User + + Config() { + $this.App = [App]::new() + $this.User = [User]::new() + } +} diff --git a/src/GitHub/classes/Data/App.ps1 b/src/GitHub/classes/Data/App.ps1 index ba32860f0..4695371df 100644 --- a/src/GitHub/classes/Data/App.ps1 +++ b/src/GitHub/classes/Data/App.ps1 @@ -1,8 +1,8 @@ -$script:App = [pscustomobject]@{ - GitHubApp = [pscustomobject]@{ - ClientID = 'Iv1.f26b61bc99e69405' # $script:App.GitHubApp.ClientID +$script:App = @{ + GitHubApp = @{ + ClientID = 'Iv1.f26b61bc99e69405' # $script:App.GitHubApp.ClientID } - OAuthApp = [pscustomobject]@{ - ClientID = '7204ae9b0580f2cb8288' # $script:App.OAuthApp.ClientID + OAuthApp = @{ + ClientID = '7204ae9b0580f2cb8288' # $script:App.OAuthApp.ClientID } } diff --git a/src/GitHub/classes/Data/Config.ps1 b/src/GitHub/classes/Data/Config.ps1 deleted file mode 100644 index e0686728b..000000000 --- a/src/GitHub/classes/Data/Config.ps1 +++ /dev/null @@ -1,29 +0,0 @@ -$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 diff --git a/src/GitHub/classes/Data/SecretVault.ps1 b/src/GitHub/classes/Data/SecretVault.ps1 index a56d58174..cc8d49b5d 100644 --- a/src/GitHub/classes/Data/SecretVault.ps1 +++ b/src/GitHub/classes/Data/SecretVault.ps1 @@ -1,7 +1,7 @@ -$script:SecretVault = [pscustomobject]@{ +$script:SecretVault = @{ Name = 'GitHub' # $script:SecretVault.Name Type = 'Microsoft.PowerShell.SecretStore' # $script:SecretVault.Type } -$script:Secret = [pscustomobject]@{ +$script:Secret = @{ Name = 'Config' # $script:Secret.Name } From fbbac8155c6c5c37bd1a1c81981f8826080a88a1 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sat, 23 Sep 2023 14:44:13 +0200 Subject: [PATCH 10/30] Fixes --- .../Auth/DeviceFlow/Check-GitHubAccessToken.ps1 | 2 +- .../Auth/DeviceFlow/Wait-GitHubAccessToken.ps1 | 5 ++--- src/GitHub/public/Auth/Connect-GitHubAccount.ps1 | 1 - src/GitHub/public/Config/Get-GitHubConfig.ps1 | 2 +- src/GitHub/public/Config/Reset-GitHubConfig.ps1 | 16 +++++++++------- .../public/Config/Restore-GitHubConfig.ps1 | 8 ++++---- tools/utilities/Local-Testing.ps1 | 7 ++++++- 7 files changed, 23 insertions(+), 18 deletions(-) diff --git a/src/GitHub/private/Auth/DeviceFlow/Check-GitHubAccessToken.ps1 b/src/GitHub/private/Auth/DeviceFlow/Check-GitHubAccessToken.ps1 index 675caa9ac..1ef8db1af 100644 --- a/src/GitHub/private/Auth/DeviceFlow/Check-GitHubAccessToken.ps1 +++ b/src/GitHub/private/Auth/DeviceFlow/Check-GitHubAccessToken.ps1 @@ -1,6 +1,6 @@ function Check-GitHubAccessToken { - [DateTime]$accessTokenExirationDate = $script:ConfigTemplate.User.Auth.AccessToken.ExpirationDate + [DateTime]$accessTokenExirationDate = $script:Config.User.Auth.AccessToken.ExpirationDate $accessTokenValid = $accessTokenExirationDate -gt (Get-Date) if (-not $accessTokenValid) { diff --git a/src/GitHub/private/Auth/DeviceFlow/Wait-GitHubAccessToken.ps1 b/src/GitHub/private/Auth/DeviceFlow/Wait-GitHubAccessToken.ps1 index 6a0e00025..cc1e0411d 100644 --- a/src/GitHub/private/Auth/DeviceFlow/Wait-GitHubAccessToken.ps1 +++ b/src/GitHub/private/Auth/DeviceFlow/Wait-GitHubAccessToken.ps1 @@ -1,5 +1,4 @@ - -function Wait-GitHubAccessToken { +function Wait-GitHubAccessToken { <# .SYNOPSIS Waits for the GitHub Device Flow to complete. @@ -48,7 +47,7 @@ function Wait-GitHubAccessToken { ) do { - if ($Refresh) { + if ($RefreshToken) { $response = Request-GitHubAccessToken -ClientID $ClientID -RefreshToken $RefreshToken } else { $response = Request-GitHubAccessToken -ClientID $ClientID -DeviceCode $DeviceCode diff --git a/src/GitHub/public/Auth/Connect-GitHubAccount.ps1 b/src/GitHub/public/Auth/Connect-GitHubAccount.ps1 index ed59ea3e2..c266b6982 100644 --- a/src/GitHub/public/Auth/Connect-GitHubAccount.ps1 +++ b/src/GitHub/public/Auth/Connect-GitHubAccount.ps1 @@ -119,7 +119,6 @@ } Save-GitHubConfig - Write-Host '✓ ' -ForegroundColor Green -NoNewline Write-Host "Logged in to GitHub!" } diff --git a/src/GitHub/public/Config/Get-GitHubConfig.ps1 b/src/GitHub/public/Config/Get-GitHubConfig.ps1 index 8f2ff66ba..281aa91ba 100644 --- a/src/GitHub/public/Config/Get-GitHubConfig.ps1 +++ b/src/GitHub/public/Config/Get-GitHubConfig.ps1 @@ -18,7 +18,7 @@ Refreshes the current GitHub configuration from the configuration store beofre returning it. #> [Alias('Get-GHConfig')] - [OutputType([PSCustomObject])] + [OutputType([Config])] [CmdletBinding()] param ( # Refresh the configuration from the configuration store before returning it. diff --git a/src/GitHub/public/Config/Reset-GitHubConfig.ps1 b/src/GitHub/public/Config/Reset-GitHubConfig.ps1 index 585f4f6de..deff62db0 100644 --- a/src/GitHub/public/Config/Reset-GitHubConfig.ps1 +++ b/src/GitHub/public/Config/Reset-GitHubConfig.ps1 @@ -26,28 +26,30 @@ [string] $Scope = 'All' ) + Write-Verbose "Resetting GitHub configuration for scope '$Scope'..." switch ($Scope) { 'App' { - $script:Config.App = $script:ConfigTemplate.App + $script:Config.App = [App]::new() } 'App.API' { - $script:Config.App.API = $script:ConfigTemplate.App.API + $script:Config.App.API = [API]::new() } 'App.Defaults' { - $script:Config.App.Defaults = $script:ConfigTemplate.App.Defaults + $script:Config.App.Defaults = [AppDefaults]::new() } 'User' { - $script:Config.User = $script:ConfigTemplate.User + $script:Config.User = [User]::new() } 'User.Auth' { - $script:Config.User.Auth = $script:ConfigTemplate.User.Auth + $script:Config.User.Auth = [Auth]::new() } 'User.Defaults' { - $script:Config.User.Defaults = $script:ConfigTemplate.User.Defaults + $script:Config.User.Defaults = [UserDefaults]::new() } 'All' { - $script:Config = $script:ConfigTemplate + $script:Config = [Config]::new() } } + Save-GitHubConfig } diff --git a/src/GitHub/public/Config/Restore-GitHubConfig.ps1 b/src/GitHub/public/Config/Restore-GitHubConfig.ps1 index c4cc3188f..6bbc402e2 100644 --- a/src/GitHub/public/Config/Restore-GitHubConfig.ps1 +++ b/src/GitHub/public/Config/Restore-GitHubConfig.ps1 @@ -28,11 +28,11 @@ function Restore-GitHubConfig { 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)]." + Write-Verbose "Unable to restore configuration." + Write-Verbose "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." + Write-Verbose "Unable to restore configuration." + Write-Verbose "The vault [$($script:SecretVault.Name)] does not exist." } } diff --git a/tools/utilities/Local-Testing.ps1 b/tools/utilities/Local-Testing.ps1 index f364c71c9..cb62e62d7 100644 --- a/tools/utilities/Local-Testing.ps1 +++ b/tools/utilities/Local-Testing.ps1 @@ -6,14 +6,19 @@ Get-SecretVault | Unregister-SecretVault Get-SecretVault Get-Module -Name GitHub -ListAvailable Install-Module -Name GitHub -Verbose -Force -AllowPrerelease + +Import-Module -Name 'C:\Repos\GitHub\PSModule\Modules\GitHub\src\GitHub\GitHub.psm1' -Verbose -Force + Import-Module -Name GitHub -Verbose Clear-Host Get-Command -Module GitHub Get-Variable | Where-Object -Property Module -ne $null | Select-Object Name, Module, ModuleName Connect-GitHubAccount -Get-GitHubConfig | ConvertTo-Json -Depth 100 +Get-GitHubConfig -Refresh | ConvertTo-Json -Depth 100 +Restore-GitHubConfig -Verbose Get-GitHubContext Connect-GitHubAccount -Refresh -Verbose Disconnect-GitHubAccount -Verbose +Reset-GitHubConfig -Verbose From fedc4b74344aaa88c83aee25dc98ad79ee4e6415 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sat, 23 Sep 2023 15:19:24 +0200 Subject: [PATCH 11/30] Additional changes :D --- src/GitHub/Data/Auth.psd1 | 8 ++++++++ src/GitHub/Data/SecretVault.psd1 | 7 +++++++ src/GitHub/classes/Config.ps1 | 1 + src/GitHub/classes/Data/App.ps1 | 8 -------- src/GitHub/classes/Data/SecretVault.ps1 | 7 ------- src/GitHub/private/Config/Initialize-SecretVault.ps1 | 4 ++-- src/GitHub/public/Auth/Connect-GitHubAccount.ps1 | 4 ++-- src/GitHub/public/Config/Restore-GitHubConfig.ps1 | 6 +++--- src/GitHub/public/Config/Save-GitHubConfig.ps1 | 2 +- src/GitHub/public/loader.ps1 | 7 ++++++- 10 files changed, 30 insertions(+), 24 deletions(-) create mode 100644 src/GitHub/Data/Auth.psd1 create mode 100644 src/GitHub/Data/SecretVault.psd1 delete mode 100644 src/GitHub/classes/Data/App.ps1 delete mode 100644 src/GitHub/classes/Data/SecretVault.ps1 diff --git a/src/GitHub/Data/Auth.psd1 b/src/GitHub/Data/Auth.psd1 new file mode 100644 index 000000000..c1701ac68 --- /dev/null +++ b/src/GitHub/Data/Auth.psd1 @@ -0,0 +1,8 @@ +@{ + GitHubApp = @{ + ClientID = 'Iv1.f26b61bc99e69405' # $script:Auth.GitHubApp.ClientID + } + OAuthApp = @{ + ClientID = '7204ae9b0580f2cb8288' # $script:Auth.OAuthApp.ClientID + } +} diff --git a/src/GitHub/Data/SecretVault.psd1 b/src/GitHub/Data/SecretVault.psd1 new file mode 100644 index 000000000..e119766f1 --- /dev/null +++ b/src/GitHub/Data/SecretVault.psd1 @@ -0,0 +1,7 @@ +@{ + Name = 'GitHub' # $script:SecretVault.Name + Type = 'Microsoft.PowerShell.SecretStore' # $script:SecretVault.Type + Secret = @{ + Name = 'Config' # $script:SecretVault.Secret.Name + } +} diff --git a/src/GitHub/classes/Config.ps1 b/src/GitHub/classes/Config.ps1 index 7febb0161..2cef1bfed 100644 --- a/src/GitHub/classes/Config.ps1 +++ b/src/GitHub/classes/Config.ps1 @@ -33,6 +33,7 @@ Class UserDefaults { $this.Repo = '' } } + Class User { [Auth] $Auth [UserDefaults] $Defaults diff --git a/src/GitHub/classes/Data/App.ps1 b/src/GitHub/classes/Data/App.ps1 deleted file mode 100644 index 4695371df..000000000 --- a/src/GitHub/classes/Data/App.ps1 +++ /dev/null @@ -1,8 +0,0 @@ -$script:App = @{ - GitHubApp = @{ - ClientID = 'Iv1.f26b61bc99e69405' # $script:App.GitHubApp.ClientID - } - OAuthApp = @{ - ClientID = '7204ae9b0580f2cb8288' # $script:App.OAuthApp.ClientID - } -} diff --git a/src/GitHub/classes/Data/SecretVault.ps1 b/src/GitHub/classes/Data/SecretVault.ps1 deleted file mode 100644 index cc8d49b5d..000000000 --- a/src/GitHub/classes/Data/SecretVault.ps1 +++ /dev/null @@ -1,7 +0,0 @@ -$script:SecretVault = @{ - Name = 'GitHub' # $script:SecretVault.Name - Type = 'Microsoft.PowerShell.SecretStore' # $script:SecretVault.Type -} -$script:Secret = @{ - Name = 'Config' # $script:Secret.Name -} diff --git a/src/GitHub/private/Config/Initialize-SecretVault.ps1 b/src/GitHub/private/Config/Initialize-SecretVault.ps1 index 9dc04b7cd..05b619d5c 100644 --- a/src/GitHub/private/Config/Initialize-SecretVault.ps1 +++ b/src/GitHub/private/Config/Initialize-SecretVault.ps1 @@ -33,9 +33,9 @@ function Initialize-SecretVault { $secretVault = Get-SecretVault | Where-Object { $_.ModuleName -eq $Type } $secretVaultExists = $secretVault.count -ne 0 - Write-Verbose "A $Name exists: $secretVaultExists" + Write-Verbose "[$Name] - exists - [$secretVaultExists]" if (-not $secretVaultExists) { - Write-Verbose "Registering [$Name]" + Write-Verbose "[$Name] - Registering" switch ($Type) { 'Microsoft.PowerShell.SecretStore' { diff --git a/src/GitHub/public/Auth/Connect-GitHubAccount.ps1 b/src/GitHub/public/Auth/Connect-GitHubAccount.ps1 index c266b6982..a8569072e 100644 --- a/src/GitHub/public/Auth/Connect-GitHubAccount.ps1 +++ b/src/GitHub/public/Auth/Connect-GitHubAccount.ps1 @@ -81,7 +81,7 @@ $vault = Get-SecretVault | Where-Object -Property ModuleName -EQ $script:SecretVault.Type } - $clientID = $script:App.$Mode.ClientID + $clientID = $script:Auth.$Mode.ClientID switch ($PSCmdlet.ParameterSetName) { 'Refresh' { @@ -120,5 +120,5 @@ Save-GitHubConfig Write-Host '✓ ' -ForegroundColor Green -NoNewline - Write-Host "Logged in to GitHub!" + Write-Host 'Logged in to GitHub!' } diff --git a/src/GitHub/public/Config/Restore-GitHubConfig.ps1 b/src/GitHub/public/Config/Restore-GitHubConfig.ps1 index 6bbc402e2..dfa0de586 100644 --- a/src/GitHub/public/Config/Restore-GitHubConfig.ps1 +++ b/src/GitHub/public/Config/Restore-GitHubConfig.ps1 @@ -24,12 +24,12 @@ function Restore-GitHubConfig { $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 + $secretExists = Get-SecretInfo -Name $script:SecretVault.Secret.Name -Vault $script:SecretVault.Name if ($secretExists) { - $script:Config = Get-Secret -Name $script:Secret.Name -AsPlainText -Vault $script:SecretVault.Name | ConvertFrom-Json + $script:Config = [Config](Get-Secret -Name $script:SecretVault.Secret.Name -AsPlainText -Vault $script:SecretVault.Name | ConvertFrom-Json -AsHashtable) } else { Write-Verbose "Unable to restore configuration." - Write-Verbose "The secret [$($script:Secret.Name)] does not exist in the vault [$($script:SecretVault.Name)]." + Write-Verbose "The secret [$($script:SecretVault.Secret.Name)] does not exist in the vault [$($script:SecretVault.Name)]." } } else { Write-Verbose "Unable to restore configuration." diff --git a/src/GitHub/public/Config/Save-GitHubConfig.ps1 b/src/GitHub/public/Config/Save-GitHubConfig.ps1 index 2724b03c1..76a861eb7 100644 --- a/src/GitHub/public/Config/Save-GitHubConfig.ps1 +++ b/src/GitHub/public/Config/Save-GitHubConfig.ps1 @@ -20,5 +20,5 @@ function Save-GitHubConfig { param() $configJson = $script:Config | ConvertTo-Json -Depth 100 - Set-Secret -Name $script:Secret.Name -Secret $configJson -Vault $script:SecretVault.Name + Set-Secret -Name $script:SecretVault.Secret.Name -Secret $configJson -Vault $script:SecretVault.Name } diff --git a/src/GitHub/public/loader.ps1 b/src/GitHub/public/loader.ps1 index 657c13126..ff2fa23dc 100644 --- a/src/GitHub/public/loader.ps1 +++ b/src/GitHub/public/loader.ps1 @@ -1,4 +1,9 @@ -Initialize-SecretVault -Name $script:SecretVault.Name -Type $script:SecretVault.Type +$script:Config = [Config]::new() + +Join-Path $PSScriptRoot 'Data' | Get-ChildItem -Recurse -File -Force | ForEach-Object { + New-Variable -Name $_.BaseName -Value (Import-PowerShellDataFile -Path $_.FullName) -Force -Scope Script +} +Initialize-SecretVault -Name $script:SecretVault.Name -Type $script:SecretVault.Type Restore-GitHubConfig if (-not [string]::IsNullOrEmpty($env:GH_TOKEN)) { From 1b6d3195dbc417cfe08cf999c38b58f4a6726076 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sat, 23 Sep 2023 16:52:33 +0200 Subject: [PATCH 12/30] Move data import to module loader --- src/GitHub/{public/loader.ps1 => GitHub.ps1} | 4 --- src/GitHub/GitHub.psm1 | 29 +++++++++++++------- 2 files changed, 19 insertions(+), 14 deletions(-) rename src/GitHub/{public/loader.ps1 => GitHub.ps1} (69%) diff --git a/src/GitHub/public/loader.ps1 b/src/GitHub/GitHub.ps1 similarity index 69% rename from src/GitHub/public/loader.ps1 rename to src/GitHub/GitHub.ps1 index ff2fa23dc..65f010b5b 100644 --- a/src/GitHub/public/loader.ps1 +++ b/src/GitHub/GitHub.ps1 @@ -1,8 +1,4 @@ $script:Config = [Config]::new() - -Join-Path $PSScriptRoot 'Data' | Get-ChildItem -Recurse -File -Force | ForEach-Object { - New-Variable -Name $_.BaseName -Value (Import-PowerShellDataFile -Path $_.FullName) -Force -Scope Script -} Initialize-SecretVault -Name $script:SecretVault.Name -Type $script:SecretVault.Type Restore-GitHubConfig diff --git a/src/GitHub/GitHub.psm1 b/src/GitHub/GitHub.psm1 index 14391165c..2123ef891 100644 --- a/src/GitHub/GitHub.psm1 +++ b/src/GitHub/GitHub.psm1 @@ -1,28 +1,37 @@ [Cmdletbinding()] param() -$sciptName = $MyInvocation.MyCommand.Name +$scriptName = $MyInvocation.MyCommand.Name + +Write-Verbose "[$scriptName] Importing subcomponents" + +Join-Path $PSScriptRoot 'Data' | Get-ChildItem -Recurse -File -Force | ForEach-Object { + Write-Verbose "[$scriptName] - [$PSScriptRoot] - [Data] - Done" + New-Variable -Name $_.BaseName -Value (Import-PowerShellDataFile -Path $_.FullName) -Force +} -Write-Verbose "[$sciptName] Importing subcomponents" -$folders = 'classes', 'private', 'public' # Import everything in these folders +$folders = 'init', 'classes', 'private', 'public' foreach ($folder in $folders) { - Write-Verbose "[$sciptName] - Processing folder [$folder]" + Write-Verbose "[$scriptName] - Processing folder [$folder]" $folderPath = Join-Path -Path $PSScriptRoot -ChildPath $folder - Write-Verbose "[$sciptName] - [$folderPath]" + Write-Verbose "[$scriptName] - [$folderPath]" if (Test-Path -Path $folderPath) { - Write-Verbose "[$sciptName] - [$folderPath] - Getting all files" - $files = $null + Write-Verbose "[$scriptName] - [$folderPath] - Getting all files" $files = Get-ChildItem -Path $folderPath -Include '*.ps1', '*.psm1' -Recurse - # dot source each file foreach ($file in $files) { - Write-Verbose "[$sciptName] - [$folderPath] - [$($file.Name)] - Importing" + Write-Verbose "[$scriptName] - [$folderPath] - [$($file.Name)] - Importing" Import-Module $file - Write-Verbose "[$sciptName] - [$folderPath] - [$($file.Name)] - Done" + Write-Verbose "[$scriptName] - [$folderPath] - [$($file.Name)] - Done" } } } +$PSScriptRoot | Get-ChildItem -Path $folderPath -Include '*.ps1' | ForEach-Object { + Write-Verbose "[$scriptName] - [$PSScriptRoot] - [$(_.Name)] - Done" + Import-Module $_.FullName +} + $foldersToProcess = Get-ChildItem -Path $PSScriptRoot -Directory | Where-Object -Property Name -In $folders $moduleFiles = $foldersToProcess | Get-ChildItem -Include '*.ps1' -Recurse -File -Force $functions = $moduleFiles.BaseName From 7fb2f53ddc6fff550216db96ae309d7d6f60c379 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sat, 23 Sep 2023 17:09:45 +0200 Subject: [PATCH 13/30] Fix loader --- src/GitHub/GitHub.psm1 | 27 +++++++++++++++------------ tools/utilities/Local-Testing.ps1 | 1 + 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/src/GitHub/GitHub.psm1 b/src/GitHub/GitHub.psm1 index 2123ef891..dd62122d2 100644 --- a/src/GitHub/GitHub.psm1 +++ b/src/GitHub/GitHub.psm1 @@ -4,33 +4,36 @@ param() $scriptName = $MyInvocation.MyCommand.Name Write-Verbose "[$scriptName] Importing subcomponents" - -Join-Path $PSScriptRoot 'Data' | Get-ChildItem -Recurse -File -Force | ForEach-Object { - Write-Verbose "[$scriptName] - [$PSScriptRoot] - [Data] - Done" +Write-Verbose "[$scriptName] - [Data] - Processing folder" +Get-ChildItem -Path (Join-Path $PSScriptRoot 'Data') -Recurse -File -Force | ForEach-Object { + Write-Verbose "[$scriptName] - [Data] - [$($_.Name)]" New-Variable -Name $_.BaseName -Value (Import-PowerShellDataFile -Path $_.FullName) -Force + Write-Verbose "[$scriptName] - [Data] - [$($_.Name)] - Done" } # Import everything in these folders $folders = 'init', 'classes', 'private', 'public' foreach ($folder in $folders) { - Write-Verbose "[$scriptName] - Processing folder [$folder]" + Write-Verbose "[$scriptName] - [$folder] - Processing folder" $folderPath = Join-Path -Path $PSScriptRoot -ChildPath $folder - Write-Verbose "[$scriptName] - [$folderPath]" if (Test-Path -Path $folderPath) { - Write-Verbose "[$scriptName] - [$folderPath] - Getting all files" $files = Get-ChildItem -Path $folderPath -Include '*.ps1', '*.psm1' -Recurse foreach ($file in $files) { - Write-Verbose "[$scriptName] - [$folderPath] - [$($file.Name)] - Importing" - Import-Module $file - Write-Verbose "[$scriptName] - [$folderPath] - [$($file.Name)] - Done" + Write-Verbose "[$scriptName] - [$folder] - [$($file.Name)] - Importing" + Import-Module $file -Verbose:$false + Write-Verbose "[$scriptName] - [$folder] - [$($file.Name)] - Done" } } + Write-Verbose "[$scriptName] - [$folder] - Done" } -$PSScriptRoot | Get-ChildItem -Path $folderPath -Include '*.ps1' | ForEach-Object { - Write-Verbose "[$scriptName] - [$PSScriptRoot] - [$(_.Name)] - Done" - Import-Module $_.FullName +Write-Verbose "[$scriptName] - [Root] - Processing folder" +Get-ChildItem -Path $PSScriptRoot -Filter '*.ps1' | ForEach-Object { + Write-Verbose "[$scriptName] - [Root] - [$($_.Name)] - Importing" + Import-Module $_ -Verbose:$false + Write-Verbose "[$scriptName] - [Root] - [$($_.Name)] - Done" } +Write-Verbose "[$scriptName] - [Root] - Done" $foldersToProcess = Get-ChildItem -Path $PSScriptRoot -Directory | Where-Object -Property Name -In $folders $moduleFiles = $foldersToProcess | Get-ChildItem -Include '*.ps1' -Recurse -File -Force diff --git a/tools/utilities/Local-Testing.ps1 b/tools/utilities/Local-Testing.ps1 index cb62e62d7..c219521b1 100644 --- a/tools/utilities/Local-Testing.ps1 +++ b/tools/utilities/Local-Testing.ps1 @@ -7,6 +7,7 @@ Get-SecretVault Get-Module -Name GitHub -ListAvailable Install-Module -Name GitHub -Verbose -Force -AllowPrerelease +$VerbosePreference = 'Continue' Import-Module -Name 'C:\Repos\GitHub\PSModule\Modules\GitHub\src\GitHub\GitHub.psm1' -Verbose -Force Import-Module -Name GitHub -Verbose From f9f9e6ee2c8fc497c2a4f7359e75ddea104d2302 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sat, 23 Sep 2023 17:59:34 +0200 Subject: [PATCH 14/30] Fix loader --- src/GitHub/GitHub.psm1 | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/GitHub/GitHub.psm1 b/src/GitHub/GitHub.psm1 index dd62122d2..ba2fde62a 100644 --- a/src/GitHub/GitHub.psm1 +++ b/src/GitHub/GitHub.psm1 @@ -2,22 +2,26 @@ param() $scriptName = $MyInvocation.MyCommand.Name - Write-Verbose "[$scriptName] Importing subcomponents" -Write-Verbose "[$scriptName] - [Data] - Processing folder" -Get-ChildItem -Path (Join-Path $PSScriptRoot 'Data') -Recurse -File -Force | ForEach-Object { - Write-Verbose "[$scriptName] - [Data] - [$($_.Name)]" + +#region - Data import +Write-Verbose "[$scriptName] - [data] - Processing folder" +Get-ChildItem -Path (Join-Path $PSScriptRoot 'data') -Recurse -File -Force | ForEach-Object { + Write-Verbose "[$scriptName] - [data] - [$($_.Name)]" New-Variable -Name $_.BaseName -Value (Import-PowerShellDataFile -Path $_.FullName) -Force - Write-Verbose "[$scriptName] - [Data] - [$($_.Name)] - Done" + Write-Verbose "[$scriptName] - [data] - [$($_.Name)] - Done" } +Write-Verbose "[$scriptName] - [data] - Done" +#endregion - Data import # Import everything in these folders +#region - Script import $folders = 'init', 'classes', 'private', 'public' foreach ($folder in $folders) { Write-Verbose "[$scriptName] - [$folder] - Processing folder" $folderPath = Join-Path -Path $PSScriptRoot -ChildPath $folder if (Test-Path -Path $folderPath) { - $files = Get-ChildItem -Path $folderPath -Include '*.ps1', '*.psm1' -Recurse + $files = Get-ChildItem -Path $folderPath -Include '*.ps1', '*.psm1' -Recurse | Sort-Object -Property FullName foreach ($file in $files) { Write-Verbose "[$scriptName] - [$folder] - [$($file.Name)] - Importing" Import-Module $file -Verbose:$false @@ -26,7 +30,9 @@ foreach ($folder in $folders) { } Write-Verbose "[$scriptName] - [$folder] - Done" } +#endregion - Script import +#region - Root import Write-Verbose "[$scriptName] - [Root] - Processing folder" Get-ChildItem -Path $PSScriptRoot -Filter '*.ps1' | ForEach-Object { Write-Verbose "[$scriptName] - [Root] - [$($_.Name)] - Importing" @@ -34,6 +40,7 @@ Get-ChildItem -Path $PSScriptRoot -Filter '*.ps1' | ForEach-Object { Write-Verbose "[$scriptName] - [Root] - [$($_.Name)] - Done" } Write-Verbose "[$scriptName] - [Root] - Done" +#endregion - Root import $foldersToProcess = Get-ChildItem -Path $PSScriptRoot -Directory | Where-Object -Property Name -In $folders $moduleFiles = $foldersToProcess | Get-ChildItem -Include '*.ps1' -Recurse -File -Force From 62f1c62f76fc76f969c177ed1c65916f645f4909 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sat, 23 Sep 2023 18:14:36 +0200 Subject: [PATCH 15/30] Fix path --- src/GitHub/GitHub.psm1 | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/GitHub/GitHub.psm1 b/src/GitHub/GitHub.psm1 index ba2fde62a..459633c2f 100644 --- a/src/GitHub/GitHub.psm1 +++ b/src/GitHub/GitHub.psm1 @@ -6,6 +6,8 @@ Write-Verbose "[$scriptName] Importing subcomponents" #region - Data import Write-Verbose "[$scriptName] - [data] - Processing folder" +$dataFolder = (Join-Path $PSScriptRoot 'data') +Write-Verbose "[$scriptName] - [data] - [$dataFolder]" Get-ChildItem -Path (Join-Path $PSScriptRoot 'data') -Recurse -File -Force | ForEach-Object { Write-Verbose "[$scriptName] - [data] - [$($_.Name)]" New-Variable -Name $_.BaseName -Value (Import-PowerShellDataFile -Path $_.FullName) -Force From 95113b53b1c713da7379f676d84e5934342b78f1 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sat, 23 Sep 2023 18:19:34 +0200 Subject: [PATCH 16/30] Fix Path --- src/GitHub/GitHub.psm1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/GitHub/GitHub.psm1 b/src/GitHub/GitHub.psm1 index 459633c2f..c874609b4 100644 --- a/src/GitHub/GitHub.psm1 +++ b/src/GitHub/GitHub.psm1 @@ -8,7 +8,7 @@ Write-Verbose "[$scriptName] Importing subcomponents" Write-Verbose "[$scriptName] - [data] - Processing folder" $dataFolder = (Join-Path $PSScriptRoot 'data') Write-Verbose "[$scriptName] - [data] - [$dataFolder]" -Get-ChildItem -Path (Join-Path $PSScriptRoot 'data') -Recurse -File -Force | ForEach-Object { +Get-ChildItem -Path "$dataFolder" -Recurse -File -Force | ForEach-Object { Write-Verbose "[$scriptName] - [data] - [$($_.Name)]" New-Variable -Name $_.BaseName -Value (Import-PowerShellDataFile -Path $_.FullName) -Force Write-Verbose "[$scriptName] - [data] - [$($_.Name)] - Done" From 3ee357f3956e39c7b0c252b84feaf2bfe21d9075 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sat, 23 Sep 2023 18:33:42 +0200 Subject: [PATCH 17/30] Fix path --- src/GitHub/GitHub.psm1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/GitHub/GitHub.psm1 b/src/GitHub/GitHub.psm1 index c874609b4..28f8a9358 100644 --- a/src/GitHub/GitHub.psm1 +++ b/src/GitHub/GitHub.psm1 @@ -8,8 +8,8 @@ Write-Verbose "[$scriptName] Importing subcomponents" Write-Verbose "[$scriptName] - [data] - Processing folder" $dataFolder = (Join-Path $PSScriptRoot 'data') Write-Verbose "[$scriptName] - [data] - [$dataFolder]" -Get-ChildItem -Path "$dataFolder" -Recurse -File -Force | ForEach-Object { - Write-Verbose "[$scriptName] - [data] - [$($_.Name)]" +Get-ChildItem -Path "$dataFolder" -Recurse -Force -Include '*.psd1' | ForEach-Object { + Write-Verbose "[$scriptName] - [data] - [$($_.Name)] - Importing" New-Variable -Name $_.BaseName -Value (Import-PowerShellDataFile -Path $_.FullName) -Force Write-Verbose "[$scriptName] - [data] - [$($_.Name)] - Done" } From 04abb1915eeb50e83adb02cdda69b60a2c3d859e Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sat, 23 Sep 2023 18:37:44 +0200 Subject: [PATCH 18/30] Fix pathing --- src/GitHub/{Data => 123}/Auth.psd1 | 0 src/GitHub/{Data => 123}/SecretVault.psd1 | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename src/GitHub/{Data => 123}/Auth.psd1 (100%) rename src/GitHub/{Data => 123}/SecretVault.psd1 (100%) diff --git a/src/GitHub/Data/Auth.psd1 b/src/GitHub/123/Auth.psd1 similarity index 100% rename from src/GitHub/Data/Auth.psd1 rename to src/GitHub/123/Auth.psd1 diff --git a/src/GitHub/Data/SecretVault.psd1 b/src/GitHub/123/SecretVault.psd1 similarity index 100% rename from src/GitHub/Data/SecretVault.psd1 rename to src/GitHub/123/SecretVault.psd1 From 3352b72624bbc6059b523a560ea5a58f4256943d Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sat, 23 Sep 2023 18:38:02 +0200 Subject: [PATCH 19/30] Fix pathing --- src/GitHub/{123 => data}/Auth.psd1 | 0 src/GitHub/{123 => data}/SecretVault.psd1 | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename src/GitHub/{123 => data}/Auth.psd1 (100%) rename src/GitHub/{123 => data}/SecretVault.psd1 (100%) diff --git a/src/GitHub/123/Auth.psd1 b/src/GitHub/data/Auth.psd1 similarity index 100% rename from src/GitHub/123/Auth.psd1 rename to src/GitHub/data/Auth.psd1 diff --git a/src/GitHub/123/SecretVault.psd1 b/src/GitHub/data/SecretVault.psd1 similarity index 100% rename from src/GitHub/123/SecretVault.psd1 rename to src/GitHub/data/SecretVault.psd1 From 8afa76b523c76a46781ab8bfec6ce4cb8cc9c75c Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sat, 23 Sep 2023 22:19:38 +0200 Subject: [PATCH 20/30] Add Config as a hashtable again --- src/GitHub/GitHub.ps1 | 2 +- src/GitHub/classes/Config.ps1 | 73 ------------------- src/GitHub/private/Config/Config.ps1 | 28 +++++++ src/GitHub/private/Utilities/Copy-Object.ps1 | 15 ++++ src/GitHub/public/Config/Get-GitHubConfig.ps1 | 2 +- .../public/Config/Reset-GitHubConfig.ps1 | 14 ++-- .../public/Config/Restore-GitHubConfig.ps1 | 2 +- 7 files changed, 53 insertions(+), 83 deletions(-) delete mode 100644 src/GitHub/classes/Config.ps1 create mode 100644 src/GitHub/private/Config/Config.ps1 create mode 100644 src/GitHub/private/Utilities/Copy-Object.ps1 diff --git a/src/GitHub/GitHub.ps1 b/src/GitHub/GitHub.ps1 index 65f010b5b..73f0f912b 100644 --- a/src/GitHub/GitHub.ps1 +++ b/src/GitHub/GitHub.ps1 @@ -1,4 +1,4 @@ -$script:Config = [Config]::new() +$script:Config = $script:ConfigTemplate | Copy-Object Initialize-SecretVault -Name $script:SecretVault.Name -Type $script:SecretVault.Type Restore-GitHubConfig diff --git a/src/GitHub/classes/Config.ps1 b/src/GitHub/classes/Config.ps1 deleted file mode 100644 index 2cef1bfed..000000000 --- a/src/GitHub/classes/Config.ps1 +++ /dev/null @@ -1,73 +0,0 @@ -Class Token { - [string] $Value - [datetime] $ExpirationDate - - Token() { - $this.Value = '' - $this.ExpirationDate = [datetime]::MinValue - } -} - -Class Auth { - [Token] $AccessToken - [string] $ClientID - [string] $Mode - [Token] $RefreshToken - [string] $Scope - - Auth() { - $this.AccessToken = [Token]::new() - $this.ClientID = '' - $this.Mode = '' - $this.RefreshToken = [Token]::new() - $this.Scope = '' - } -} - -Class UserDefaults { - [string] $Owner - [string] $Repo - - UserDefaults() { - $this.Owner = '' - $this.Repo = '' - } -} - -Class User { - [Auth] $Auth - [UserDefaults] $Defaults - - User() { - $this.Auth = [Auth]::new() - $this.Defaults = [UserDefaults]::new() - } -} - -Class API { - [string] $BaseURI - [string] $Version - - API() { - $this.BaseURI = '' - $this.Version = '' - } -} - -Class App { - [API] $API - - App() { - $this.API = [API]::new() - } -} - -Class Config { - [App] $App - [User] $User - - Config() { - $this.App = [App]::new() - $this.User = [User]::new() - } -} diff --git a/src/GitHub/private/Config/Config.ps1 b/src/GitHub/private/Config/Config.ps1 new file mode 100644 index 000000000..de2131326 --- /dev/null +++ b/src/GitHub/private/Config/Config.ps1 @@ -0,0 +1,28 @@ +$script:ConfigTemplate = [pscustomobject]@{ # $script:ConfigTemplate + App = [pscustomobject]@{ # $script:ConfigTemplate.App + API = [pscustomobject]@{ # $script:ConfigTemplate.App.API + BaseURI = 'https://api.github.com' # $script:ConfigTemplate.App.API.BaseURI + Version = '2022-11-28' # $script:ConfigTemplate.App.API.Version + } + Defaults = [pscustomobject]@{} # $script:ConfigTemplate.App.Defaults + } + User = [pscustomobject]@{ # $script:ConfigTemplate.User + Auth = [pscustomobject]@{ # $script:ConfigTemplate.User.Auth + AccessToken = [pscustomobject]@{ # $script:ConfigTemplate.User.Auth.AccessToken + 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]@{ # $script:ConfigTemplate.User.Defaults + Owner = '' # $script:ConfigTemplate.User.Defaults.Owner + Repo = '' # $script:ConfigTemplate.User.Defaults.Repo + } + } +} diff --git a/src/GitHub/private/Utilities/Copy-Object.ps1 b/src/GitHub/private/Utilities/Copy-Object.ps1 new file mode 100644 index 000000000..2e505ed97 --- /dev/null +++ b/src/GitHub/private/Utilities/Copy-Object.ps1 @@ -0,0 +1,15 @@ +function Copy-Object { + [OutputType([object])] + [CmdletBinding()] + param ( + [Parameter( + Mandatory, + ValueFromPipeline + )] + [Object] $InputObject + ) + + process { + $InputObject | ConvertTo-Json -Depth 100 | ConvertFrom-Json + } +} diff --git a/src/GitHub/public/Config/Get-GitHubConfig.ps1 b/src/GitHub/public/Config/Get-GitHubConfig.ps1 index 281aa91ba..8f2ff66ba 100644 --- a/src/GitHub/public/Config/Get-GitHubConfig.ps1 +++ b/src/GitHub/public/Config/Get-GitHubConfig.ps1 @@ -18,7 +18,7 @@ Refreshes the current GitHub configuration from the configuration store beofre returning it. #> [Alias('Get-GHConfig')] - [OutputType([Config])] + [OutputType([PSCustomObject])] [CmdletBinding()] param ( # Refresh the configuration from the configuration store before returning it. diff --git a/src/GitHub/public/Config/Reset-GitHubConfig.ps1 b/src/GitHub/public/Config/Reset-GitHubConfig.ps1 index deff62db0..6c05097d2 100644 --- a/src/GitHub/public/Config/Reset-GitHubConfig.ps1 +++ b/src/GitHub/public/Config/Reset-GitHubConfig.ps1 @@ -29,25 +29,25 @@ Write-Verbose "Resetting GitHub configuration for scope '$Scope'..." switch ($Scope) { 'App' { - $script:Config.App = [App]::new() + $script:Config.App = $script:ConfigTemplate.App | Copy-Object } 'App.API' { - $script:Config.App.API = [API]::new() + $script:Config.App.API = $script:ConfigTemplate.App.API | Copy-Object } 'App.Defaults' { - $script:Config.App.Defaults = [AppDefaults]::new() + $script:Config.App.Defaults = $script:ConfigTemplate.App.Defaults | Copy-Object } 'User' { - $script:Config.User = [User]::new() + $script:Config.User = $script:ConfigTemplate.User | Copy-Object } 'User.Auth' { - $script:Config.User.Auth = [Auth]::new() + $script:Config.User.Auth = $script:ConfigTemplate.User.Auth | Copy-Object } 'User.Defaults' { - $script:Config.User.Defaults = [UserDefaults]::new() + $script:Config.User.Defaults = $script:ConfigTemplate.User.Defaults | Copy-Object } 'All' { - $script:Config = [Config]::new() + $script:Config = $script:ConfigTemplate | Copy-Object } } diff --git a/src/GitHub/public/Config/Restore-GitHubConfig.ps1 b/src/GitHub/public/Config/Restore-GitHubConfig.ps1 index dfa0de586..48ed8d390 100644 --- a/src/GitHub/public/Config/Restore-GitHubConfig.ps1 +++ b/src/GitHub/public/Config/Restore-GitHubConfig.ps1 @@ -26,7 +26,7 @@ function Restore-GitHubConfig { if ($vaultExists) { $secretExists = Get-SecretInfo -Name $script:SecretVault.Secret.Name -Vault $script:SecretVault.Name if ($secretExists) { - $script:Config = [Config](Get-Secret -Name $script:SecretVault.Secret.Name -AsPlainText -Vault $script:SecretVault.Name | ConvertFrom-Json -AsHashtable) + $script:Config = Get-Secret -Name $script:SecretVault.Secret.Name -AsPlainText -Vault $script:SecretVault.Name | ConvertFrom-Json } else { Write-Verbose "Unable to restore configuration." Write-Verbose "The secret [$($script:SecretVault.Secret.Name)] does not exist in the vault [$($script:SecretVault.Name)]." From ea634f86539d6ed91c4f33d0401ab30043c4c21e Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sat, 23 Sep 2023 22:48:46 +0200 Subject: [PATCH 21/30] Doc update --- src/GitHub/en_US/about_Config.help.txt | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/GitHub/en_US/about_Config.help.txt b/src/GitHub/en_US/about_Config.help.txt index aca48b2cc..a774c58ca 100644 --- a/src/GitHub/en_US/about_Config.help.txt +++ b/src/GitHub/en_US/about_Config.help.txt @@ -10,15 +10,15 @@ LONG DESCRIPTION using the provided cmdlets. Name: SecretVault - Path: \classes\Data\SecretVault.ps1 - - | Name | Type | Default Value | Description | - | --------------- | -------------- | ---------------------------------- | ----------------------------- | - | SecretVault | pscustomobject | {Name, Type} | | - | SecretVault.Name | string | 'GitHub' | The name of the secret vault. | - | SecretVault.Type | string | 'Microsoft.PowerShell.SecretStore' | The type of the secret vault. | - | Secret | pscustomobject | {Name} | | - | Secret.Name | string | 'Config' | The name of the secret. | + Path: \classes\Data\SecretVault.psd1 + + | Name | Type | Default Value | Description | + | ---------------------------- | -------------- | ---------------------------------- | ----------------------------- | + | SecretVault | pscustomobject | {Name, Type, Secret} | | + | SecretVault.Name | string | 'GitHub' | The name of the secret vault. | + | SecretVault.Type | string | 'Microsoft.PowerShell.SecretStore' | The type of the secret vault. | + | SecretVault.Secret | pscustomobject | {Name} | | + | SecretVault.Secret.Name | string | 'Config' | The name of the secret. | Name: Config Path: \classes\Data\Config.ps1 From 5cf705ec853c68f49dad658c2b1d3c9f779aef34 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sat, 23 Sep 2023 23:13:16 +0200 Subject: [PATCH 22/30] Fix :) --- src/GitHub/private/Utilities/Copy-Object.ps1 | 2 +- tools/utilities/Local-Testing.ps1 | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/GitHub/private/Utilities/Copy-Object.ps1 b/src/GitHub/private/Utilities/Copy-Object.ps1 index 2e505ed97..46e6cca03 100644 --- a/src/GitHub/private/Utilities/Copy-Object.ps1 +++ b/src/GitHub/private/Utilities/Copy-Object.ps1 @@ -1,5 +1,5 @@ function Copy-Object { - [OutputType([object])] + [OutputType([pscustomobject])] [CmdletBinding()] param ( [Parameter( diff --git a/tools/utilities/Local-Testing.ps1 b/tools/utilities/Local-Testing.ps1 index c219521b1..e3b0bb031 100644 --- a/tools/utilities/Local-Testing.ps1 +++ b/tools/utilities/Local-Testing.ps1 @@ -15,6 +15,7 @@ Clear-Host Get-Command -Module GitHub Get-Variable | Where-Object -Property Module -ne $null | Select-Object Name, Module, ModuleName Connect-GitHubAccount +Get-GitHubConfig | ConvertTo-Json -Depth 100 Get-GitHubConfig -Refresh | ConvertTo-Json -Depth 100 Restore-GitHubConfig -Verbose Get-GitHubContext From 672eec0d26d45c442ad8cd63ef21ac4e422baa0f Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sat, 23 Sep 2023 23:28:20 +0200 Subject: [PATCH 23/30] Moiving the init file --- src/GitHub/{ => script}/GitHub.ps1 | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) rename src/GitHub/{ => script}/GitHub.ps1 (78%) diff --git a/src/GitHub/GitHub.ps1 b/src/GitHub/script/GitHub.ps1 similarity index 78% rename from src/GitHub/GitHub.ps1 rename to src/GitHub/script/GitHub.ps1 index 73f0f912b..55730a4fa 100644 --- a/src/GitHub/GitHub.ps1 +++ b/src/GitHub/script/GitHub.ps1 @@ -1,4 +1,6 @@ -$script:Config = $script:ConfigTemplate | Copy-Object +Write-Verbose "Initializing GitHub module..." -Verbose + +$script:Config = $script:ConfigTemplate | Copy-Object Initialize-SecretVault -Name $script:SecretVault.Name -Type $script:SecretVault.Type Restore-GitHubConfig From 23db3b1401c9134cc915253f8e24fd12e4c37b01 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sat, 23 Sep 2023 23:46:48 +0200 Subject: [PATCH 24/30] Use PSCustomObject copy :) --- src/GitHub/{script => }/GitHub.ps1 | 2 +- src/GitHub/private/Utilities/Copy-Object.ps1 | 15 --------------- src/GitHub/public/Auth/Connect-GitHubAccount.ps1 | 6 +++++- src/GitHub/public/Config/Reset-GitHubConfig.ps1 | 14 +++++++------- 4 files changed, 13 insertions(+), 24 deletions(-) rename src/GitHub/{script => }/GitHub.ps1 (89%) delete mode 100644 src/GitHub/private/Utilities/Copy-Object.ps1 diff --git a/src/GitHub/script/GitHub.ps1 b/src/GitHub/GitHub.ps1 similarity index 89% rename from src/GitHub/script/GitHub.ps1 rename to src/GitHub/GitHub.ps1 index 55730a4fa..ec4a6d3ac 100644 --- a/src/GitHub/script/GitHub.ps1 +++ b/src/GitHub/GitHub.ps1 @@ -1,6 +1,6 @@ Write-Verbose "Initializing GitHub module..." -Verbose -$script:Config = $script:ConfigTemplate | Copy-Object +$script:Config = $script:ConfigTemplate.PSObject.Copy() Initialize-SecretVault -Name $script:SecretVault.Name -Type $script:SecretVault.Type Restore-GitHubConfig diff --git a/src/GitHub/private/Utilities/Copy-Object.ps1 b/src/GitHub/private/Utilities/Copy-Object.ps1 deleted file mode 100644 index 46e6cca03..000000000 --- a/src/GitHub/private/Utilities/Copy-Object.ps1 +++ /dev/null @@ -1,15 +0,0 @@ -function Copy-Object { - [OutputType([pscustomobject])] - [CmdletBinding()] - param ( - [Parameter( - Mandatory, - ValueFromPipeline - )] - [Object] $InputObject - ) - - process { - $InputObject | ConvertTo-Json -Depth 100 | ConvertFrom-Json - } -} diff --git a/src/GitHub/public/Auth/Connect-GitHubAccount.ps1 b/src/GitHub/public/Auth/Connect-GitHubAccount.ps1 index a8569072e..af7999361 100644 --- a/src/GitHub/public/Auth/Connect-GitHubAccount.ps1 +++ b/src/GitHub/public/Auth/Connect-GitHubAccount.ps1 @@ -93,7 +93,11 @@ if ([string]::IsNullOrEmpty($Scope) -and ($Mode -eq 'OAuthApp')) { $Scope = 'gist read:org repo workflow' } - Reset-GitHubConfig -Scope 'User.Auth' + if ($script:Config.PSObject.Properties.Name -contains 'App') { + Reset-GitHubConfig -Scope 'User.Auth' + } else { + Reset-GitHubConfig -Scope 'All' + } $tokenResponse = Invoke-GitHubDeviceFlowLogin -ClientID $clientID -Scope $Scope $script:Config.User.Auth.Mode = $Mode $script:Config.User.Auth.ClientID = $clientID diff --git a/src/GitHub/public/Config/Reset-GitHubConfig.ps1 b/src/GitHub/public/Config/Reset-GitHubConfig.ps1 index 6c05097d2..43539059c 100644 --- a/src/GitHub/public/Config/Reset-GitHubConfig.ps1 +++ b/src/GitHub/public/Config/Reset-GitHubConfig.ps1 @@ -29,25 +29,25 @@ Write-Verbose "Resetting GitHub configuration for scope '$Scope'..." switch ($Scope) { 'App' { - $script:Config.App = $script:ConfigTemplate.App | Copy-Object + $script:Config.App = $script:ConfigTemplate.App.PSObject.Copy() } 'App.API' { - $script:Config.App.API = $script:ConfigTemplate.App.API | Copy-Object + $script:Config.App.API = $script:ConfigTemplate.App.API.PSObject.Copy() } 'App.Defaults' { - $script:Config.App.Defaults = $script:ConfigTemplate.App.Defaults | Copy-Object + $script:Config.App.Defaults = $script:ConfigTemplate.App.Defaults.PSObject.Copy() } 'User' { - $script:Config.User = $script:ConfigTemplate.User | Copy-Object + $script:Config.User = $script:ConfigTemplate.User.PSObject.Copy() } 'User.Auth' { - $script:Config.User.Auth = $script:ConfigTemplate.User.Auth | Copy-Object + $script:Config.User.Auth = $script:ConfigTemplate.User.Auth.PSObject.Copy() } 'User.Defaults' { - $script:Config.User.Defaults = $script:ConfigTemplate.User.Defaults | Copy-Object + $script:Config.User.Defaults = $script:ConfigTemplate.User.Defaults.PSObject.Copy() } 'All' { - $script:Config = $script:ConfigTemplate | Copy-Object + $script:Config = $script:ConfigTemplate.PSObject.Copy() } } From 1558c73a945de250b4c8c1e6fa56873a03f9a6d9 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sat, 23 Sep 2023 23:52:27 +0200 Subject: [PATCH 25/30] temp --- .../Config/Get-GitHubConfigTemplate.ps1 | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 src/GitHub/public/Config/Get-GitHubConfigTemplate.ps1 diff --git a/src/GitHub/public/Config/Get-GitHubConfigTemplate.ps1 b/src/GitHub/public/Config/Get-GitHubConfigTemplate.ps1 new file mode 100644 index 000000000..a4da91453 --- /dev/null +++ b/src/GitHub/public/Config/Get-GitHubConfigTemplate.ps1 @@ -0,0 +1,25 @@ +function Get-GitHubConfigTemplate { + <# + .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. + #> + [OutputType([PSCustomObject])] + [CmdletBinding()] + param () + + $script:ConfigTemplate +} From 2fe65515031bb1d8d41e2165447413790025341f Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sat, 23 Sep 2023 23:54:28 +0200 Subject: [PATCH 26/30] fix --- src/GitHub/GitHub.ps1 | 2 +- src/GitHub/public/Config/Reset-GitHubConfig.ps1 | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/GitHub/GitHub.ps1 b/src/GitHub/GitHub.ps1 index ec4a6d3ac..6122c7dd3 100644 --- a/src/GitHub/GitHub.ps1 +++ b/src/GitHub/GitHub.ps1 @@ -1,6 +1,6 @@ Write-Verbose "Initializing GitHub module..." -Verbose -$script:Config = $script:ConfigTemplate.PSObject.Copy() +$script:Config = $script:ConfigTemplate | ConvertTo-Json -Depth 100 | ConvertFrom-Json Initialize-SecretVault -Name $script:SecretVault.Name -Type $script:SecretVault.Type Restore-GitHubConfig diff --git a/src/GitHub/public/Config/Reset-GitHubConfig.ps1 b/src/GitHub/public/Config/Reset-GitHubConfig.ps1 index 43539059c..42e5994ad 100644 --- a/src/GitHub/public/Config/Reset-GitHubConfig.ps1 +++ b/src/GitHub/public/Config/Reset-GitHubConfig.ps1 @@ -29,25 +29,25 @@ Write-Verbose "Resetting GitHub configuration for scope '$Scope'..." switch ($Scope) { 'App' { - $script:Config.App = $script:ConfigTemplate.App.PSObject.Copy() + $script:Config.App = $script:ConfigTemplate.App | ConvertTo-Json -Depth 100 | ConvertFrom-Json } 'App.API' { - $script:Config.App.API = $script:ConfigTemplate.App.API.PSObject.Copy() + $script:Config.App.API = $script:ConfigTemplate.App.API | ConvertTo-Json -Depth 100 | ConvertFrom-Json } 'App.Defaults' { - $script:Config.App.Defaults = $script:ConfigTemplate.App.Defaults.PSObject.Copy() + $script:Config.App.Defaults = $script:ConfigTemplate.App.Defaults | ConvertTo-Json -Depth 100 | ConvertFrom-Json } 'User' { - $script:Config.User = $script:ConfigTemplate.User.PSObject.Copy() + $script:Config.User = $script:ConfigTemplate.User | ConvertTo-Json -Depth 100 | ConvertFrom-Json } 'User.Auth' { - $script:Config.User.Auth = $script:ConfigTemplate.User.Auth.PSObject.Copy() + $script:Config.User.Auth = $script:ConfigTemplate.User.Auth | ConvertTo-Json -Depth 100 | ConvertFrom-Json } 'User.Defaults' { - $script:Config.User.Defaults = $script:ConfigTemplate.User.Defaults.PSObject.Copy() + $script:Config.User.Defaults = $script:ConfigTemplate.User.Defaults | ConvertTo-Json -Depth 100 | ConvertFrom-Json } 'All' { - $script:Config = $script:ConfigTemplate.PSObject.Copy() + $script:Config = $script:ConfigTemplate | ConvertTo-Json -Depth 100 | ConvertFrom-Json } } From 13324e860e1181706a57d68f5d7383205bdbda3b Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sun, 24 Sep 2023 12:55:37 +0200 Subject: [PATCH 27/30] Update to loader --- src/GitHub/GitHub.psm1 | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/src/GitHub/GitHub.psm1 b/src/GitHub/GitHub.psm1 index 28f8a9358..bbac83a75 100644 --- a/src/GitHub/GitHub.psm1 +++ b/src/GitHub/GitHub.psm1 @@ -2,22 +2,21 @@ param() $scriptName = $MyInvocation.MyCommand.Name -Write-Verbose "[$scriptName] Importing subcomponents" +Write-Verbose "[$scriptName] - Importing module" -#region - Data import +#region - Importing data files Write-Verbose "[$scriptName] - [data] - Processing folder" $dataFolder = (Join-Path $PSScriptRoot 'data') Write-Verbose "[$scriptName] - [data] - [$dataFolder]" Get-ChildItem -Path "$dataFolder" -Recurse -Force -Include '*.psd1' | ForEach-Object { - Write-Verbose "[$scriptName] - [data] - [$($_.Name)] - Importing" + Write-Verbose "[$scriptName] - [data] - [$($_.Name)] - Importing data file" New-Variable -Name $_.BaseName -Value (Import-PowerShellDataFile -Path $_.FullName) -Force Write-Verbose "[$scriptName] - [data] - [$($_.Name)] - Done" } Write-Verbose "[$scriptName] - [data] - Done" -#endregion - Data import +#endregion - Importing datas -# Import everything in these folders -#region - Script import +#region - Importing script files $folders = 'init', 'classes', 'private', 'public' foreach ($folder in $folders) { Write-Verbose "[$scriptName] - [$folder] - Processing folder" @@ -25,29 +24,30 @@ foreach ($folder in $folders) { if (Test-Path -Path $folderPath) { $files = Get-ChildItem -Path $folderPath -Include '*.ps1', '*.psm1' -Recurse | Sort-Object -Property FullName foreach ($file in $files) { - Write-Verbose "[$scriptName] - [$folder] - [$($file.Name)] - Importing" + Write-Verbose "[$scriptName] - [$folder] - [$($file.Name)] - Importing script file" Import-Module $file -Verbose:$false Write-Verbose "[$scriptName] - [$folder] - [$($file.Name)] - Done" } } Write-Verbose "[$scriptName] - [$folder] - Done" } -#endregion - Script import +#endregion - Importing script files -#region - Root import -Write-Verbose "[$scriptName] - [Root] - Processing folder" +#region - Importing root script files +Write-Verbose "[$scriptName] - [PSModuleRoot] - Processing folder" Get-ChildItem -Path $PSScriptRoot -Filter '*.ps1' | ForEach-Object { - Write-Verbose "[$scriptName] - [Root] - [$($_.Name)] - Importing" + Write-Verbose "[$scriptName] - [PSModuleRoot] - [$($_.Name)] - Importing root script files" Import-Module $_ -Verbose:$false - Write-Verbose "[$scriptName] - [Root] - [$($_.Name)] - Done" + Write-Verbose "[$scriptName] - [PSModuleRoot] - [$($_.Name)] - Done" } Write-Verbose "[$scriptName] - [Root] - Done" -#endregion - Root import +#endregion - Importing root script files +#region Export module members $foldersToProcess = Get-ChildItem -Path $PSScriptRoot -Directory | Where-Object -Property Name -In $folders $moduleFiles = $foldersToProcess | Get-ChildItem -Include '*.ps1' -Recurse -File -Force $functions = $moduleFiles.BaseName -$Param = @{ +$param = @{ Function = $functions Variable = '' Cmdlet = '' @@ -56,4 +56,7 @@ $Param = @{ Write-Verbose 'Exporting module members' -Export-ModuleMember @Param +Export-ModuleMember @param +#endregion Export module members + +Write-Verbose "[$scriptName] - Done" From 19435b9baa3c849e7307fdd114e6a7575d0185bd Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sun, 24 Sep 2023 12:55:50 +0200 Subject: [PATCH 28/30] Update docs --- src/GitHub/en_US/about_Auth.help.txt | 26 +++++++-- src/GitHub/en_US/about_Config.help.txt | 80 ++++++++++++++------------ 2 files changed, 66 insertions(+), 40 deletions(-) diff --git a/src/GitHub/en_US/about_Auth.help.txt b/src/GitHub/en_US/about_Auth.help.txt index 4fd600d06..5be89709d 100644 --- a/src/GitHub/en_US/about_Auth.help.txt +++ b/src/GitHub/en_US/about_Auth.help.txt @@ -2,15 +2,33 @@ TOPIC about_Auth SHORT DESCRIPTION + Describes the authentication methods provided in the PowerShell module for interacting with GitHub's REST API. LONG DESCRIPTION + This module provides several functions to manage authentication for GitHub's REST API. There are primarily two ways to authenticate: + + 1. GitHub Device Flow: This method involves a user being prompted to visit a specific URL on GitHub and enter a code that's provided by the module. Once the user enters the code on the GitHub website, the module retrieves the necessary access tokens to make authenticated API requests. + + 2. Personal Access Token: The user can also provide a Personal Access Token (PAT) to authenticate. This PAT gives the module the ability to interact with the API on behalf of the user. + + The module also provides a way to refresh the access token and to disconnect or logout from the GitHub account. EXAMPLES + Example 1: + Connect-GitHubAccount -AccessToken 'ghp_####' + Connects to GitHub using a personal access token (PAT). + + Example 3: + Disconnect-GitHubAccount + Disconnects from GitHub and removes the current GitHub configuration. KEYWORDS - GitHub - PowerShell - SecretManagement - SecretStore + GitHub, Authentication, Device Flow, Personal Access Token, PowerShell, REST API SEE ALSO + For more information on 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 + + For information about scopes and other authentication methods on GitHub: + - https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/scopes-for-oauth-apps + - https://docs.github.com/en/rest/overview/other-authentication-methods#authenticating-for-saml-sso diff --git a/src/GitHub/en_US/about_Config.help.txt b/src/GitHub/en_US/about_Config.help.txt index a774c58ca..315028b52 100644 --- a/src/GitHub/en_US/about_Config.help.txt +++ b/src/GitHub/en_US/about_Config.help.txt @@ -6,13 +6,16 @@ SHORT DESCRIPTION LONG DESCRIPTION The PowerShell Module provides a set of functions to manage the configuration related to the module. - This configuration is stored in a custom secret vault and can be accessed, modified, saved, and restored + The configuration is stored in a secret vault and can be accessed, modified, saved, and restored using the provided cmdlets. +DATA STRUCTURE + Name: SecretVault + Purpose: Hold static configuration data about the secret vault. Path: \classes\Data\SecretVault.psd1 - | Name | Type | Default Value | Description | + | Name | Type | Static Value | Description | | ---------------------------- | -------------- | ---------------------------------- | ----------------------------- | | SecretVault | pscustomobject | {Name, Type, Secret} | | | SecretVault.Name | string | 'GitHub' | The name of the secret vault. | @@ -21,31 +24,32 @@ LONG DESCRIPTION | SecretVault.Secret.Name | string | 'Config' | The name of the secret. | Name: Config + Purpose: Hold the current configuration data. Path: \classes\Data\Config.ps1 - | Name | Type | Static Value | Description | - | --------------------------------------- | ----------------- | --------------------------------------------------- | --------------------------------- | - | App | pscustomobject | {API, Defaults} | | - | App.API | pscustomobject | {BaseURI, Version} | | - | App.API.BaseURI | string | 'https://api.github.com' | The GitHub API Base URI. | - | App.API.Version | string | '2022-11-28' | The GitHub API version. | - | App.Defaults | pscustomobject | {} | | - | User | pscustomobject | {Auth, Defaults} | | - | User.Auth | pscustomobject | {AccessToken, ClientID, Mode, RefreshToken, Scope} | | - | User.Auth.AccessToken | pscustomobject | {Value, ExpirationDate} | The access token. | - | User.Auth.AccessToken.Value | string | '' | The access token value. | - | User.Auth.AccessToken.ExpirationDate | datetime | [datetime]::MinValue | The access token expiration date. | - | User.Auth.ClientID | string | '' | The client ID. | - | User.Auth.Mode | string | '' | The authentication mode. | - | User.Auth.RefreshToken | pscustomobject | {Value, ExpirationDate} | The refresh token. | - | User.Auth.RefreshToken.Value | string | '' | The refresh token value. | - | User.Auth.RefreshToken.ExpirationDate | datetime | [datetime]::MinValue | The refresh token expiration date.| - | User.Auth.Scope | string | '' | The scope. | - | User.Defaults | pscustomobject | {Owner, Repo} | | - | User.Defaults.Owner | string | '' | The default owner. | - | User.Defaults.Repo | string | '' | The default repository. | - - Functions provided in the module: + | Name | Type | Default Value | Description | + | ------------------------------------ | -------------- | ------------------------ | --------------------------------- | + | App | pscustomobject | | | + | App.API | pscustomobject | | | + | App.API.BaseURI | string | 'https://api.github.com' | The GitHub API Base URI. | + | App.API.Version | string | '2022-11-28' | The GitHub API version. | + | App.Defaults | pscustomobject | {} | | + | User | pscustomobject | | | + | User.Auth | pscustomobject | | | + | User.Auth.AccessToken | pscustomobject | | The access token. | + | User.Auth.AccessToken.Value | string | '' | The access token value. | + | User.Auth.AccessToken.ExpirationDate | datetime | [datetime]::MinValue | The access token expiration date. | + | User.Auth.ClientID | string | '' | The client ID. | + | User.Auth.Mode | string | '' | The authentication mode. | + | User.Auth.RefreshToken | pscustomobject | | The refresh token. | + | User.Auth.RefreshToken.Value | string | '' | The refresh token value. | + | User.Auth.RefreshToken.ExpirationDate| datetime | [datetime]::MinValue | The refresh token expiration date.| + | User.Auth.Scope | string | '' | The scope. | + | User.Defaults | pscustomobject | | | + | User.Defaults.Owner | string | '' | The default owner. | + | User.Defaults.Repo | string | '' | The default repository. | + +FUNCTIONS - Get-GitHubConfig: Fetches the current module configuration. - Reset-GitHubConfig: Resets all or specific sections to its default values. @@ -53,17 +57,19 @@ LONG DESCRIPTION - Save-GitHubConfig: Saves the current configuration to the secret vault. - Set-GitHubConfig: Allows setting specific elements of the configuration. - The configuration values are securely stored using the SecretManagement and SecretStore modules. - During the module import, the following steps are performed: - - Initialize the configuration store. - - Check for secret vault of type 'Microsoft.PowerShell.SecretStore'. - If not registered for the current user, its configuration will be reset to unattended mode. - - Check for secret vault with the name 'GitHub'. - If it does not exist, it will be created with current configuration. - If the user is already using the secret vault, the existing configuration will be kept. - - Restore saved configuration from the configuration store. - - Look for the 'GitHub' secret vault. - - Look for the secret called 'Config'. If it exists, restore the configuration from it into memory +CONFIGURATION + + The configuration values are securely stored using the SecretManagement and SecretStore modules. During the module import, the following steps are performed: + + 1. Initialize the configuration store. + - Check for secret vault of type 'Microsoft.PowerShell.SecretStore'. + If not registered for the current user, its configuration will be reset to unattended mode. + - Check for secret vault with the name 'GitHub'. + If it does not exist, it will be created with current configuration. + If the user is already using the secret vault, the existing configuration will be kept. + 2. Restore saved configuration from the configuration store. + - Look for the 'GitHub' secret vault. + - Look for the secret called 'Config'. If it exists, restore the configuration from it into memory. EXAMPLES @@ -98,12 +104,14 @@ EXAMPLES This command saves the current GitHub configuration to the secret vault. KEYWORDS + GitHub PowerShell SecretManagement SecretStore SEE ALSO + - For more information about SecretManagement and SecretStore: https://learn.microsoft.com/en-us/powershell/utility-modules/secretmanagement/overview?view=ps-modules - The GitHub repository of this module: From 240ba7c7bd0ee31fbb08306efa69725203932649 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sun, 24 Sep 2023 12:59:46 +0200 Subject: [PATCH 29/30] Added docs --- src/GitHub/en_US/about_Auth.help.txt | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/GitHub/en_US/about_Auth.help.txt b/src/GitHub/en_US/about_Auth.help.txt index 5be89709d..d7bf1793f 100644 --- a/src/GitHub/en_US/about_Auth.help.txt +++ b/src/GitHub/en_US/about_Auth.help.txt @@ -7,21 +7,32 @@ SHORT DESCRIPTION LONG DESCRIPTION This module provides several functions to manage authentication for GitHub's REST API. There are primarily two ways to authenticate: - 1. GitHub Device Flow: This method involves a user being prompted to visit a specific URL on GitHub and enter a code that's provided by the module. Once the user enters the code on the GitHub website, the module retrieves the necessary access tokens to make authenticated API requests. + 1. GitHub Device Flow: This method prompts the user to visit a specific URL on GitHub where they must enter a user verification code. Once this is done, the module retrieves the necessary access tokens to make authenticated API requests. - 2. Personal Access Token: The user can also provide a Personal Access Token (PAT) to authenticate. This PAT gives the module the ability to interact with the API on behalf of the user. + 2. Personal Access Token: The user can provide a Personal Access Token (PAT) to authenticate. This PAT allows the module to interact with the API on the user's behalf. The module can automatically use environment variables `GH_TOKEN` or `GITHUB_TOKEN` if they are present. - The module also provides a way to refresh the access token and to disconnect or logout from the GitHub account. + The module also provides functionalities to refresh the access token and to disconnect or logout from the GitHub account. EXAMPLES Example 1: + Connect-GitHubAccount + Connects to GitHub using the device flow login. You'll be prompted to visit a specific URL on GitHub and enter the provided user verification code. + + Example 2: Connect-GitHubAccount -AccessToken 'ghp_####' - Connects to GitHub using a personal access token (PAT). + Connects to GitHub using a provided personal access token (PAT). Example 3: + Connect-GitHubAccount -Refresh + Refreshes the access token for continued session validity. + + Example 4: Disconnect-GitHubAccount Disconnects from GitHub and removes the current GitHub configuration. + Example 5 (Automatic login using environment variables): + If either the `GH_TOKEN` or `GITHUB_TOKEN` environment variables are set, the module will automatically use them for authentication during module initialization. + KEYWORDS GitHub, Authentication, Device Flow, Personal Access Token, PowerShell, REST API From f93e72cd4bf500ccf4274aa550560dca2e99e03f Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sun, 24 Sep 2023 13:00:35 +0200 Subject: [PATCH 30/30] remove template checking function --- .../Config/Get-GitHubConfigTemplate.ps1 | 25 ------------------- 1 file changed, 25 deletions(-) delete mode 100644 src/GitHub/public/Config/Get-GitHubConfigTemplate.ps1 diff --git a/src/GitHub/public/Config/Get-GitHubConfigTemplate.ps1 b/src/GitHub/public/Config/Get-GitHubConfigTemplate.ps1 deleted file mode 100644 index a4da91453..000000000 --- a/src/GitHub/public/Config/Get-GitHubConfigTemplate.ps1 +++ /dev/null @@ -1,25 +0,0 @@ -function Get-GitHubConfigTemplate { - <# - .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. - #> - [OutputType([PSCustomObject])] - [CmdletBinding()] - param () - - $script:ConfigTemplate -}