diff --git a/.github/workflows/Action-Test-Prerelease.yml b/.github/workflows/Action-Test-Prerelease.yml new file mode 100644 index 0000000..89122f0 --- /dev/null +++ b/.github/workflows/Action-Test-Prerelease.yml @@ -0,0 +1,27 @@ +name: Action-Test-Prerelease + +run-name: "Action-Test - [${{ github.event.pull_request.title }} #${{ github.event.pull_request.number }}] by @${{ github.actor }}" + +on: + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: + contents: read + pull-requests: read + id-token: write + +jobs: + ActionTest: + uses: ./.github/workflows/TestWorkflow.yml + secrets: inherit + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, macos-latest, windows-latest] + with: + runs-on: ${{ matrix.os }} + Prerelease: true diff --git a/.github/workflows/Action-Test.yml b/.github/workflows/Action-Test.yml index a19cc64..7aaed5b 100644 --- a/.github/workflows/Action-Test.yml +++ b/.github/workflows/Action-Test.yml @@ -4,11 +4,6 @@ run-name: "Action-Test - [${{ github.event.pull_request.title }} #${{ github.eve on: workflow_dispatch: - inputs: - Prerelease: - type: boolean - default: false - description: Use the prerelease version of GitHub PowerShell module. pull_request: schedule: - cron: '0 0 * * *' @@ -20,6 +15,7 @@ concurrency: permissions: contents: read pull-requests: read + id-token: write jobs: ActionTest: @@ -31,4 +27,3 @@ jobs: os: [ubuntu-latest, macos-latest, windows-latest] with: runs-on: ${{ matrix.os }} - Prerelease: ${{ inputs.Prerelease }} diff --git a/.github/workflows/TestWorkflow.yml b/.github/workflows/TestWorkflow.yml index 28d7292..579c6cc 100644 --- a/.github/workflows/TestWorkflow.yml +++ b/.github/workflows/TestWorkflow.yml @@ -10,6 +10,11 @@ on: required: false type: boolean default: false + Owner: + description: The owner of the repository + required: false + type: string + default: PSModule secrets: TEST_USER_PAT: description: Personal Access Token for the test user @@ -32,10 +37,14 @@ on: TEST_APP_ORG_PRIVATE_KEY: description: Private Key for the test GitHub App for the organization required: true + KEYVAULT_KEY_REFERENCE: + description: Azure KeyVault key reference URL for GitHub App authentication + required: true permissions: contents: read pull-requests: read + id-token: write jobs: ActionTestBasic: @@ -404,7 +413,15 @@ jobs: Prerelease: ${{ inputs.Prerelease }} Script: | LogGroup 'Get-GitHubUser' { - Get-GitHubUser | Format-Table -AutoSize | Out-String + Get-GitHubUser -Debug | Format-Table -AutoSize | Out-String + } + + LogGroup 'Get-GitHubOrganization' { + Get-GitHubOrganization | Out-String + } + + LogGroup 'Get-GitHubRepository' { + Get-GitHubRepository -Owner '${{ inputs.Owner }}' | Out-String } ActionTestWithUSERFGPAT: @@ -425,6 +442,14 @@ jobs: Get-GitHubUser | Format-Table -AutoSize | Out-String } + LogGroup 'Get-GitHubOrganization' { + Get-GitHubOrganization | Out-String + } + + LogGroup 'Get-GitHubRepository' { + Get-GitHubRepository -Owner '${{ inputs.Owner }}' | Out-String + } + ActionTestWithORGFGPAT: name: WithOrgFGPAT runs-on: ${{ inputs.runs-on }} @@ -443,6 +468,14 @@ jobs: Get-GitHubUser | Format-Table -AutoSize | Out-String } + LogGroup 'Get-GitHubOrganization' { + Get-GitHubOrganization | Out-String + } + + LogGroup 'Get-GitHubRepository' { + Get-GitHubRepository -Owner '${{ inputs.Owner }}' | Out-String + } + ActionTestWithGitHubAppEnt: name: GitHubAppEnt runs-on: ${{ inputs.runs-on }} @@ -459,19 +492,23 @@ jobs: Prerelease: ${{ inputs.Prerelease }} Script: | LogGroup 'Get-GitHubApp' { - Get-GitHubApp | Format-Table -AutoSize | Out-String + Get-GitHubApp | Format-List | Out-String } LogGroup 'Get-GitHubAppInstallation' { Get-GitHubAppInstallation | Format-Table -AutoSize | Out-String } - LogGroup 'Do something as an installation' { - Get-GithubAppInstallation | New-GitHubAppInstallationAccessToken | ForEach-Object { - Connect-GitHub -Token $_.token -Silent - Get-GitHubContext | Format-Table -AutoSize | Out-String - Get-GitHubGitConfig | Format-Table -AutoSize | Out-String - } + LogGroup 'Connect to all installations of the app' { + Connect-GitHubApp + } + + LogGroup 'Contexts' { + Get-GitHubContext -ListAvailable | Format-Table -AutoSize | Out-String + } + + LogGroup 'GitHubConfig' { + Get-GitHubConfig | Format-List | Out-String } ActionTestWithGitHubAppOrg: @@ -490,19 +527,121 @@ jobs: Prerelease: ${{ inputs.Prerelease }} Script: | LogGroup 'Get-GitHubApp' { - Get-GitHubApp | Format-Table -AutoSize | Out-String + Get-GitHubApp | Format-List | Out-String } LogGroup 'Get-GitHubAppInstallation' { Get-GitHubAppInstallation | Format-Table -AutoSize | Out-String } - LogGroup 'Do something as an installation' { - Get-GithubAppInstallation | New-GitHubAppInstallationAccessToken | ForEach-Object { - Connect-GitHub -Token $_.token -Silent - Get-GitHubContext | Format-Table -AutoSize | Out-String - Get-GitHubGitConfig | Format-Table -AutoSize | Out-String - } + LogGroup 'Connect to all installations of the app' { + Connect-GitHubApp + } + + LogGroup 'Contexts' { + Get-GitHubContext -ListAvailable | Format-Table -AutoSize | Out-String + } + + LogGroup 'GitHubConfig' { + Get-GitHubConfig | Format-List | Out-String + } + + ActionTestWithKeyVaultKeyReference: + name: WithKeyVaultKeyReference + environment: azure + runs-on: ${{ inputs.runs-on }} + steps: + # Need to check out as part of the test, as its a local action + - name: Checkout repo + uses: actions/checkout@v4 + + # Login to Azure to enable KeyVault access + - name: Login to Azure + uses: azure/login@v2 + with: + client-id: ${{ vars.AZURE_CLIENT_ID }} + tenant-id: ${{ vars.AZURE_TENANT_ID }} + subscription-id: ${{ vars.AZURE_SUBSCRIPTION_ID }} + allow-no-subscriptions: true + + - name: Action-Test + uses: ./ + with: + ClientID: ${{ secrets.TEST_APP_ORG_CLIENT_ID }} + KeyVaultKeyReference: 'https://psmodule-test-vault.vault.azure.net/keys/psmodule-org-app/569ae34250e64adca6a2b2d159d454a5' + Prerelease: ${{ inputs.Prerelease }} + Script: | + LogGroup 'Context details' { + Get-GitHubContext | Select-Object * | Out-String + } + + LogGroup 'Get-GitHubApp' { + Get-GitHubApp | Format-List | Out-String + } + + LogGroup 'Get-GitHubAppInstallation' { + Get-GitHubAppInstallation | Format-Table -AutoSize | Out-String + } + + LogGroup 'Connect to all installations of the app' { + Connect-GitHubApp + } + + LogGroup 'Contexts' { + Get-GitHubContext -ListAvailable | Format-Table -AutoSize | Out-String + } + + LogGroup 'GitHubConfig' { + Get-GitHubConfig | Format-List | Out-String + } + + ActionTestWithKeyVaultKeyReferenceLatest: + name: WithKeyVaultKeyReferenceLatest + environment: azure + runs-on: ${{ inputs.runs-on }} + steps: + # Need to check out as part of the test, as its a local action + - name: Checkout repo + uses: actions/checkout@v4 + + # Login to Azure to enable KeyVault access + - name: Login to Azure + uses: azure/login@v2 + with: + client-id: ${{ vars.AZURE_CLIENT_ID }} + tenant-id: ${{ vars.AZURE_TENANT_ID }} + subscription-id: ${{ vars.AZURE_SUBSCRIPTION_ID }} + allow-no-subscriptions: true + + - name: Action-Test + uses: ./ + with: + ClientID: ${{ secrets.TEST_APP_ORG_CLIENT_ID }} + KeyVaultKeyReference: 'https://psmodule-test-vault.vault.azure.net/keys/psmodule-org-app/' + Prerelease: ${{ inputs.Prerelease }} + Script: | + LogGroup 'Context details' { + Get-GitHubContext | Select-Object * | Out-String + } + + LogGroup 'Get-GitHubApp' { + Get-GitHubApp | Format-List | Out-String + } + + LogGroup 'Get-GitHubAppInstallation' { + Get-GitHubAppInstallation | Format-Table -AutoSize | Out-String + } + + LogGroup 'Connect to all installations of the app' { + Connect-GitHubApp + } + + LogGroup 'Contexts' { + Get-GitHubContext -ListAvailable | Format-Table -AutoSize | Out-String + } + + LogGroup 'GitHubConfig' { + Get-GitHubConfig | Format-List | Out-String } ActionTestPreserveCredentialsFalse: diff --git a/README.md b/README.md index 1033cf2..d1adf3c 100644 --- a/README.md +++ b/README.md @@ -15,8 +15,9 @@ To get started with your own GitHub PowerShell based action, [create a new repos | `Token` | Log in using an Installation Access Token (IAT). | false | `${{ github.token }}` | | `ClientID` | Log in using a GitHub App, with the App's Client ID and Private Key. | false | | | `PrivateKey` | Log in using a GitHub App, with the App's Client ID and Private Key. | false | | -| `Debug` | Enable debug output. | false | `'false'` | -| `Verbose` | Enable verbose output. | false | `'false'` | +| `KeyVaultKeyReference` | Log in using a GitHub App, with the App's Client ID and KeyVault Key Reference. | false | | +| `Debug` | Enable debug output for the whole action. | false | `'false'` | +| `Verbose` | Enable verbose output for the whole action. | false | `'false'` | | `Version` | Specifies the exact version of the GitHub module to install. | false | | | `Prerelease` | Allow prerelease versions if available. | false | `'false'` | | `ErrorView` | Configure the PowerShell `$ErrorView` variable. You can use full names ('NormalView', 'CategoryView', 'ConciseView', 'DetailedView'). It matches on partials. | false | `'NormalView'` | @@ -176,7 +177,35 @@ jobs: } ``` -#### Example 5: Using outputs from the script +#### Example 5: Run a GitHub PowerShell script with a GitHub App using a Client ID and KeyVault Key Reference + +Runs a script that uses the GitHub PowerShell module with a GitHub App authenticated via Azure KeyVault. This example retrieves the GitHub App details. + +> [!NOTE] +> This authentication method requires the `azure/login` action to authenticate with Azure first. The KeyVault Key Reference should be a URL pointing to the private key stored in Azure KeyVault. + +```yaml +jobs: + Run-Script: + runs-on: ubuntu-latest + steps: + - name: Login to Azure + uses: azure/login@v1 + with: + creds: ${{ secrets.AZURE_CREDENTIALS }} + + - name: Run script + uses: PSModule/GitHub-Script@v1 + with: + ClientID: ${{ secrets.CLIENT_ID }} + KeyVaultKeyReference: ${{ secrets.KEYVAULT_KEY_REFERENCE }} + Script: | + LogGroup "Get-GitHubApp" { + Get-GitHubApp + } +``` + +#### Example 6: Using outputs from the script Runs a script that uses the GitHub PowerShell module and outputs the result. @@ -201,7 +230,7 @@ Runs a script that uses the GitHub PowerShell module and outputs the result. Write-GitHubNotice -Message $result.Zen -Title 'GitHub Zen' ``` -#### Example 6: Run a script with credential cleanup +#### Example 7: Run a script with credential cleanup Runs a script with `PreserveCredentials` set to `false` to automatically disconnect GitHub credentials after execution. diff --git a/action.yml b/action.yml index 431813c..a4614ec 100644 --- a/action.yml +++ b/action.yml @@ -23,12 +23,15 @@ inputs: PrivateKey: description: Log in using a GitHub App, using the App's Client ID and Private Key. required: false + KeyVaultKeyReference: + description: Log in using a GitHub App, using the App's Client ID and KeyVault Key Reference. + required: false Debug: - description: Enable debug output. + description: Enable debug output for the whole action. required: false default: 'false' Verbose: - description: Enable verbose output. + description: Enable verbose output for the whole action. required: false default: 'false' Version: @@ -80,6 +83,7 @@ runs: PSMODULE_GITHUB_SCRIPT_INPUT_Token: ${{ inputs.Token }} PSMODULE_GITHUB_SCRIPT_INPUT_ClientID: ${{ inputs.ClientID }} PSMODULE_GITHUB_SCRIPT_INPUT_PrivateKey: ${{ inputs.PrivateKey }} + PSMODULE_GITHUB_SCRIPT_INPUT_KeyVaultKeyReference: ${{ inputs.KeyVaultKeyReference }} PSMODULE_GITHUB_SCRIPT_INPUT_Debug: ${{ inputs.Debug }} PSMODULE_GITHUB_SCRIPT_INPUT_Verbose: ${{ inputs.Verbose }} PSMODULE_GITHUB_SCRIPT_INPUT_Version: ${{ inputs.Version }} @@ -92,6 +96,8 @@ runs: run: | # ${{ inputs.Name }} $ErrorView = $env:PSMODULE_GITHUB_SCRIPT_INPUT_ErrorView + $DebugPreference = $env:PSMODULE_GITHUB_SCRIPT_INPUT_Debug -eq 'true' ? 'Continue' : 'SilentlyContinue' + $VerbosePreference = $env:PSMODULE_GITHUB_SCRIPT_INPUT_Verbose -eq 'true' ? 'Continue' : 'SilentlyContinue' try { ${{ github.action_path }}/scripts/init.ps1 ${{ github.action_path }}/scripts/info.ps1 diff --git a/scripts/info.ps1 b/scripts/info.ps1 index 30b2ba0..77539b9 100644 --- a/scripts/info.ps1 +++ b/scripts/info.ps1 @@ -51,6 +51,10 @@ process { Get-GitHubConfig | Format-List | Out-String } + LogGroup ' - Event Information' { + Get-GitHubEventData | Format-List | Out-String + } + $fenceEnd = '┗' + ('━' * ($fenceStart.Length - 2)) + '┛' Write-Output $fenceEnd } catch { @@ -60,6 +64,4 @@ process { end { Write-Debug "[$scriptName] - End" - $DebugPreference = $env:PSMODULE_GITHUB_SCRIPT_INPUT_Debug -eq 'true' ? 'Continue' : 'SilentlyContinue' - $VerbosePreference = $env:PSMODULE_GITHUB_SCRIPT_INPUT_Verbose -eq 'true' ? 'Continue' : 'SilentlyContinue' } diff --git a/scripts/init.ps1 b/scripts/init.ps1 index 2cd7566..76663fd 100644 --- a/scripts/init.ps1 +++ b/scripts/init.ps1 @@ -78,15 +78,28 @@ process { $providedToken = -not [string]::IsNullOrEmpty($env:PSMODULE_GITHUB_SCRIPT_INPUT_Token) $providedClientID = -not [string]::IsNullOrEmpty($env:PSMODULE_GITHUB_SCRIPT_INPUT_ClientID) $providedPrivateKey = -not [string]::IsNullOrEmpty($env:PSMODULE_GITHUB_SCRIPT_INPUT_PrivateKey) + $providedKeyVaultKeyReference = -not [string]::IsNullOrEmpty($env:PSMODULE_GITHUB_SCRIPT_INPUT_KeyVaultKeyReference) + + # Validate mutual exclusion of PrivateKey and KeyVaultKeyReference + if ($providedPrivateKey -and $providedKeyVaultKeyReference) { + throw 'Only one of PrivateKey or KeyVaultKeyReference can be provided.' + } + + # Validate that if ClientID is provided, exactly one of PrivateKey or KeyVaultKeyReference is also provided + if ($providedClientID -and -not ($providedPrivateKey -or $providedKeyVaultKeyReference)) { + throw 'When ClientID is provided, either PrivateKey or KeyVaultKeyReference must also be provided.' + } + $moduleStatus = [pscustomobject]@{ - Name = $Name - Version = [string]::IsNullOrEmpty($Version) ? 'latest' : $Version - Prerelease = $Prerelease - 'Already installed' = $null -ne $alreadyInstalled - 'Already imported' = $null -ne $alreadyImported - 'Provided Token' = $providedToken - 'Provided ClientID' = $providedClientID - 'Provided PrivateKey' = $providedPrivateKey + Name = $Name + Version = [string]::IsNullOrEmpty($Version) ? 'latest' : $Version + Prerelease = $Prerelease + 'Already installed' = $null -ne $alreadyInstalled + 'Already imported' = $null -ne $alreadyImported + 'Provided Token' = $providedToken + 'Provided ClientID' = $providedClientID + 'Provided PrivateKey' = $providedPrivateKey + 'Provided KeyVaultKeyReference' = $providedKeyVaultKeyReference } if ($showInit) { Write-Output 'Module status:' @@ -101,6 +114,13 @@ process { Silent = (-not $showInit) } Connect-GitHub @params + } elseif ($providedClientID -and $providedKeyVaultKeyReference) { + $params = @{ + ClientID = $env:PSMODULE_GITHUB_SCRIPT_INPUT_ClientID + KeyVaultKeyReference = $env:PSMODULE_GITHUB_SCRIPT_INPUT_KeyVaultKeyReference + Silent = (-not $showInit) + } + Connect-GitHub @params } elseif ($providedToken) { $params = @{ Token = $env:PSMODULE_GITHUB_SCRIPT_INPUT_Token diff --git a/scripts/outputs.ps1 b/scripts/outputs.ps1 index 0c4fd70..d5ca5a1 100644 --- a/scripts/outputs.ps1 +++ b/scripts/outputs.ps1 @@ -3,8 +3,6 @@ [CmdletBinding()] param() -$DebugPreference = 'SilentlyContinue' -$VerbosePreference = 'SilentlyContinue' $scriptName = $MyInvocation.MyCommand.Name Write-Debug "[$scriptName] - Start"