From 0294114e379b6473f7165f9094f70a772fe8a460 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Mon, 15 Sep 2025 23:32:06 +0200 Subject: [PATCH 1/3] =?UTF-8?q?=F0=9F=9A=80[Feature]:=20Permission=20infra?= =?UTF-8?q?structure=20+=20Track=20GitHub=20App=20Installation=20status=20?= =?UTF-8?q?(#513)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This pull request introduces several improvements to how GitHub App permissions are represented, compared, and displayed throughout the codebase. The main focus is on replacing the use of generic objects for permissions with a strongly-typed `GitHubPermission` class, adding installation status comparison logic, and enhancing output formatting for better user experience. - Fixes #510 Key changes include: **Permission Model Refactor:** * Replaces all uses of `[pscustomobject]` for permissions in `GitHubApp`, `GitHubAppInstallation`, `GitHubAppContext`, and `GitHubAppInstallationContext` with `[GitHubPermission[]]`, ensuring permissions are strongly typed and consistently handled. * Colocate `GitHubPermissionDefinition` class with the `GitHubPermission` in the `GitHubPermission.ps1` file. **Installation Status and Comparison Logic:** * Adds a `Status` property to `GitHubAppInstallation` and implements logic to compare installation permissions with app permissions, setting status as `'Ok'` or `'Outdated'` accordingly. * Updates `Get-GitHubAppInstallationForAuthenticatedApp` to pass the authenticated app object to each installation for accurate status calculation. **Formatting and Output Enhancements:** * Updates the `GitHubAppInstallation` format XML to display the new `Status` property in both table and list views, using symbols and colors when supported for better clarity. * Adds a new format file for `GitHubPermission`, improving how permissions are displayed in the console, including hyperlinks for permission names when supported. **Configuration and Miscellaneous:** * Makes the `Context` parameter mandatory and positional in `Switch-GitHubContext` for improved usability. --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: MariusStorhaug <17722253+MariusStorhaug@users.noreply.github.com> Co-authored-by: Marius Storhaug --- src/classes/public/App/GitHubApp.ps1 | 4 +- .../public/App/GitHubAppInstallation.ps1 | 109 +- .../GitHubContext/GitHubAppContext.ps1 | 4 +- .../GitHubAppInstallationContext.ps1 | 4 +- src/classes/public/GitHubPermission.ps1 | 1344 +++++++++++++++++ .../public/GitHubPermissionDefinition.ps1 | 46 - .../GitHubAppInstallation.Format.ps1xml | 9 + src/formats/GitHubPermission.Format.ps1xml | 52 + ...tHubAppInstallationForAuthenticatedApp.ps1 | 5 +- .../Auth/Context/Set-GitHubContext.ps1 | 2 +- .../public/Auth/Connect-GitHubApp.ps1 | 6 +- .../Auth/Context/Switch-GitHubContext.ps1 | 2 +- src/functions/public/Config/completers.ps1 | 35 +- .../public/Meta/Get-GitHubApiVersion.ps1 | 5 +- .../Get-GitHubPermissionDefinition.ps1 | 2 +- .../public/Permission/completers.ps1 | 8 +- src/variables/private/GitHub.ps1 | 1181 --------------- tests/Apps.Tests.ps1 | 322 ++-- tests/GitHub.Tests.ps1 | 1 + tests/Permissions.Tests.ps1 | 51 +- 20 files changed, 1750 insertions(+), 1442 deletions(-) create mode 100644 src/classes/public/GitHubPermission.ps1 delete mode 100644 src/classes/public/GitHubPermissionDefinition.ps1 create mode 100644 src/formats/GitHubPermission.Format.ps1xml diff --git a/src/classes/public/App/GitHubApp.ps1 b/src/classes/public/App/GitHubApp.ps1 index 620f52af5..9356e308c 100644 --- a/src/classes/public/App/GitHubApp.ps1 +++ b/src/classes/public/App/GitHubApp.ps1 @@ -36,7 +36,7 @@ [System.Nullable[datetime]] $UpdatedAt # The permissions that the app is requesting. - [pscustomobject] $Permissions + [GitHubPermission[]] $Permissions # The events that the app is subscribing to on its target. [string[]] $Events @@ -59,7 +59,7 @@ $this.Url = $Object.html_url $this.CreatedAt = $Object.created_at $this.UpdatedAt = $Object.updated_at - $this.Permissions = $Object.permissions + $this.Permissions = [GitHubPermission]::NewPermissionList($Object.permissions) $this.Events = , ($Object.events) $this.Installations = $Object.installations_count } diff --git a/src/classes/public/App/GitHubAppInstallation.ps1 b/src/classes/public/App/GitHubAppInstallation.ps1 index 96fd2831c..3fecc9140 100644 --- a/src/classes/public/App/GitHubAppInstallation.ps1 +++ b/src/classes/public/App/GitHubAppInstallation.ps1 @@ -15,7 +15,7 @@ [string] $RepositorySelection # The permissions that the app has on the target. - [pscustomobject] $Permissions + [GitHubPermission[]] $Permissions # The events that the app is subscribing to. [string[]] $Events @@ -41,6 +41,9 @@ # The URL to the target's profile based on the target type. [string] $Url + # The status indicating if the installation permissions and events match the app's configuration. + [string] $Status + GitHubAppInstallation() {} GitHubAppInstallation([PSCustomObject] $Object) { @@ -55,7 +58,24 @@ $this.Target = [GitHubOwner]::new($Object.account) $this.Type = $Object.target_type $this.RepositorySelection = $Object.repository_selection - $this.Permissions = $Object.permissions + $this.Permissions = [GitHubPermission]::NewPermissionList($Object.permissions, $this.Type) + $this.Events = , ($Object.events) + $this.FilePaths = $Object.single_file_paths + $this.CreatedAt = $Object.created_at + $this.UpdatedAt = $Object.updated_at + $this.SuspendedAt = $Object.suspended_at + $this.SuspendedBy = [GitHubUser]::new($Object.suspended_by) + $this.Url = $Object.html_url + $this.Status = 'Unknown' + } + + GitHubAppInstallation([PSCustomObject] $Object, [GitHubApp] $App) { + $this.ID = $Object.id + $this.App = $App + $this.Target = [GitHubOwner]::new($Object.account) + $this.Type = $Object.target_type + $this.RepositorySelection = $Object.repository_selection + $this.Permissions = [GitHubPermission]::NewPermissionList($Object.permissions, $this.Type) $this.Events = , ($Object.events) $this.FilePaths = $Object.single_file_paths $this.CreatedAt = $Object.created_at @@ -63,6 +83,7 @@ $this.SuspendedAt = $Object.suspended_at $this.SuspendedBy = [GitHubUser]::new($Object.suspended_by) $this.Url = $Object.html_url + $this.UpdateStatus() } GitHubAppInstallation([PSCustomObject] $Object, [string] $Target, [string] $Type, [GitHubContext] $Context) { @@ -81,7 +102,28 @@ } $this.Type = $Type $this.RepositorySelection = $Object.repository_selection - $this.Permissions = $Object.permissions + $this.Permissions = [GitHubPermission]::NewPermissionList($Object.permissions, $this.Type) + $this.Events = , ($Object.events) + $this.FilePaths = $Object.single_file_paths + $this.CreatedAt = $Object.created_at + $this.UpdatedAt = $Object.updated_at + $this.SuspendedAt = $Object.suspended_at + $this.SuspendedBy = [GitHubUser]::new($Object.suspended_by) + $this.Url = "https://$($Context.HostName)/$($Type.ToLower())s/$Target/settings/installations/$($Object.id)" + $this.Status = 'Unknown' + } + + GitHubAppInstallation([PSCustomObject] $Object, [string] $Target, [string] $Type, [GitHubContext] $Context, [GitHubApp] $App) { + $this.ID = $Object.id + $this.App = $App + $this.Target = [GitHubOwner]@{ + Name = $Target + Type = $Type + Url = "https://$($Context.HostName)/$Target" + } + $this.Type = $Type + $this.RepositorySelection = $Object.repository_selection + $this.Permissions = [GitHubPermission]::NewPermissionList($Object.permissions, $this.Type) $this.Events = , ($Object.events) $this.FilePaths = $Object.single_file_paths $this.CreatedAt = $Object.created_at @@ -89,5 +131,66 @@ $this.SuspendedAt = $Object.suspended_at $this.SuspendedBy = [GitHubUser]::new($Object.suspended_by) $this.Url = "https://$($Context.HostName)/$($Type.ToLower())s/$Target/settings/installations/$($Object.id)" + $this.UpdateStatus() + } + + # Updates the Status property by comparing installation permissions with app permissions + # filtered by the appropriate scope based on installation type + [void] UpdateStatus() { + if (-not $this.App -or -not $this.App.Permissions) { + $this.Status = 'Unknown' + return + } + + # Get app permissions filtered by installation type scope + $appPermissionsFiltered = switch ($this.Type) { + 'Enterprise' { + $this.App.Permissions | Where-Object { $_.Scope -eq 'Enterprise' } + } + 'Organization' { + $this.App.Permissions | Where-Object { $_.Scope -in @('Organization', 'Repository') } + } + 'User' { + $this.App.Permissions | Where-Object { $_.Scope -in @('Repository') } + } + default { + $this.App.Permissions + } + } + + # Compare permissions by creating lookup dictionaries + $appPermissionLookup = @{} + foreach ($perm in $appPermissionsFiltered) { + $appPermissionLookup[$perm.Name] = $perm.Value + } + + $installationPermissionLookup = @{} + foreach ($perm in $this.Permissions) { + $installationPermissionLookup[$perm.Name] = $perm.Value + } + + # Check if permissions match + $permissionsMatch = $true + + # Check if all app permissions exist in installation with same values + foreach ($name in $appPermissionLookup.Keys) { + if (-not $installationPermissionLookup.ContainsKey($name) -or + $installationPermissionLookup[$name] -ne $appPermissionLookup[$name]) { + $permissionsMatch = $false + break + } + } + + # Check if installation has any extra permissions not in the app + if ($permissionsMatch) { + foreach ($name in $installationPermissionLookup.Keys) { + if (-not $appPermissionLookup.ContainsKey($name)) { + $permissionsMatch = $false + break + } + } + } + + $this.Status = $permissionsMatch ? 'Ok' : 'Outdated' } } diff --git a/src/classes/public/Context/GitHubContext/GitHubAppContext.ps1 b/src/classes/public/Context/GitHubContext/GitHubAppContext.ps1 index 02cc68a67..856433681 100644 --- a/src/classes/public/Context/GitHubContext/GitHubAppContext.ps1 +++ b/src/classes/public/Context/GitHubContext/GitHubAppContext.ps1 @@ -15,7 +15,7 @@ [string] $OwnerType # The permissions that the app is requesting on the target - [pscustomobject] $Permissions + [GitHubPermission[]] $Permissions # The events that the app is subscribing to once installed [string[]] $Events @@ -47,7 +47,7 @@ $this.KeyVaultKeyReference = $Object.KeyVaultKeyReference $this.OwnerName = $Object.OwnerName $this.OwnerType = $Object.OwnerType - $this.Permissions = $Object.Permissions + $this.Permissions = [GitHubPermission]::NewPermissionList($Object.Permissions) $this.Events = $Object.Events } } diff --git a/src/classes/public/Context/GitHubContext/GitHubAppInstallationContext.ps1 b/src/classes/public/Context/GitHubContext/GitHubAppInstallationContext.ps1 index 2b817f3ec..93b5a7934 100644 --- a/src/classes/public/Context/GitHubContext/GitHubAppInstallationContext.ps1 +++ b/src/classes/public/Context/GitHubContext/GitHubAppInstallationContext.ps1 @@ -6,7 +6,7 @@ [System.Nullable[uint64]] $InstallationID # The permissions that the app is requesting on the target - [pscustomobject] $Permissions + [GitHubPermission[]] $Permissions # The events that the app is subscribing to once installed [string[]] $Events @@ -41,7 +41,7 @@ $this.PerPage = $Object.PerPage $this.ClientID = $Object.ClientID $this.InstallationID = $Object.InstallationID - $this.Permissions = $Object.Permissions + $this.Permissions = [GitHubPermission]::NewPermissionList($Object.Permissions, $Object.InstallationType) $this.Events = $Object.Events $this.InstallationType = $Object.InstallationType $this.InstallationName = $Object.InstallationName diff --git a/src/classes/public/GitHubPermission.ps1 b/src/classes/public/GitHubPermission.ps1 new file mode 100644 index 000000000..db355a3f8 --- /dev/null +++ b/src/classes/public/GitHubPermission.ps1 @@ -0,0 +1,1344 @@ +class GitHubPermissionDefinition { + # The programmatic name of the permission as returned by the GitHub API + [string] $Name + + # The human-friendly name of the permission as shown in the GitHub UI + [string] $DisplayName + + # A brief description of what access the permission grants + [string] $Description + + # A link to the relevant documentation or GitHub UI page + [uri] $URL + + # The levels of access that can be granted for this permission + [string[]] $Options + + # The type of permission (Fine-grained, Classic) + [string] $Type + + # The scope at which this permission applies (Repository, Organization, User, Enterprise) + [string] $Scope + + static [GitHubPermissionDefinition[]] $List = @( + # ------------------------------ + # Repository Fine-Grained Permission Definitions + # ------------------------------ + [GitHubPermissionDefinition]::new( + 'actions', + 'Actions', + 'Workflows, workflow runs and artifacts.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#repository-permissions-for-actions', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Repository' + ), + [GitHubPermissionDefinition]::new( + 'administration', + 'Administration', + 'Repository creation, deletion, settings, teams, and collaborators.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#repository-permissions-for-administration', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Repository' + ), + [GitHubPermissionDefinition]::new( + 'attestations', + 'Attestations', + 'Create and retrieve attestations for a repository.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#repository-permissions-for-attestations', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Repository' + ), + [GitHubPermissionDefinition]::new( + 'checks', + 'Checks', + 'Checks on code.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#repository-permissions-for-checks', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Repository' + ), + [GitHubPermissionDefinition]::new( + 'security_events', + 'Code scanning alerts', + 'View and manage code scanning alerts.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#repository-permissions-for-code-scanning-alerts', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Repository' + ), + [GitHubPermissionDefinition]::new( + 'codespaces', + 'Codespaces', + 'Create, edit, delete and list Codespaces.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#repository-permissions-for-codespaces', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Repository' + ), + [GitHubPermissionDefinition]::new( + 'codespaces_lifecycle_admin', + 'Codespaces lifecycle admin', + 'Manage the lifecycle of Codespaces, including starting and stopping.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#repository-permissions-for-codespaces-lifecycle-admin', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Repository' + ), + [GitHubPermissionDefinition]::new( + 'codespaces_metadata', + 'Codespaces metadata', + 'Access Codespaces metadata including the devcontainers and machine type.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#repository-permissions-for-codespaces-metadata', + @( + 'read' + ), + 'Fine-grained', + 'Repository' + ), + [GitHubPermissionDefinition]::new( + 'codespaces_secrets', + 'Codespaces secrets', + 'Restrict Codespaces user secrets modifications to specific repositories.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#repository-permissions-for-codespaces-secrets', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Repository' + ), + [GitHubPermissionDefinition]::new( + 'statuses', + 'Commit statuses', + 'Commit statuses.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#repository-permissions-for-commit-statuses', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Repository' + ), + [GitHubPermissionDefinition]::new( + 'contents', + 'Contents', + 'Repository contents, commits, branches, downloads, releases, and merges.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#repository-permissions-for-contents', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Repository' + ), + [GitHubPermissionDefinition]::new( + 'repository_custom_properties', + 'Custom properties', + 'Read and write repository custom properties values at the repository level, when allowed by the property.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#repository-permissions-for-custom-properties', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Repository' + ), + [GitHubPermissionDefinition]::new( + 'vulnerability_alerts', + 'Dependabot alerts', + 'Retrieve Dependabot alerts.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#repository-permissions-for-dependabot-alerts', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Repository' + ), + [GitHubPermissionDefinition]::new( + 'dependabot_secrets', + 'Dependabot secrets', + 'Manage Dependabot repository secrets.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#repository-permissions-for-dependabot-secrets', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Repository' + ), + [GitHubPermissionDefinition]::new( + 'deployments', + 'Deployments', + 'Deployments and deployment statuses.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#repository-permissions-for-deployments', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Repository' + ), + [GitHubPermissionDefinition]::new( + 'discussions', + 'Discussions', + 'Discussions and related comments and labels.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#repository-permissions-for-discussions', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Repository' + ), + [GitHubPermissionDefinition]::new( + 'environments', + 'Environments', + 'Manage repository environments.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#repository-permissions-for-environments', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Repository' + ), + [GitHubPermissionDefinition]::new( + 'issues', + 'Issues', + 'Issues and related comments, assignees, labels, and milestones.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#repository-permissions-for-issues', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Repository' + ), + [GitHubPermissionDefinition]::new( + 'merge_queues', + 'Merge queues', + "Manage a repository's merge queues", + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#repository-permissions-for-merge-queues', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Repository' + ), + [GitHubPermissionDefinition]::new( #Mandatory + 'metadata', + 'Metadata', + 'Search repositories, list collaborators, and access repository metadata.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#repository-permissions-for-metadata', + @( + 'read' + ), + 'Fine-grained', + 'Repository' + ), + [GitHubPermissionDefinition]::new( + 'packages', + 'Packages', + 'Packages published to the GitHub Package Platform.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#repository-permissions-for-packages', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Repository' + ), + [GitHubPermissionDefinition]::new( + 'pages', + 'Pages', + 'Retrieve Pages statuses, configuration, and builds, as well as create new builds.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#repository-permissions-for-pages', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Repository' + ), + [GitHubPermissionDefinition]::new( + 'repository_projects', + 'Projects', + 'Manage classic projects within a repository.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#repository-permissions-for-projects', + @( + 'read', + 'write', + 'admin' + ), + 'Fine-grained', + 'Repository' + ), + [GitHubPermissionDefinition]::new( + 'pull_requests', + 'Pull requests', + 'Pull requests and related comments, assignees, labels, milestones, and merges.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#repository-permissions-for-pull-requests', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Repository' + ), + [GitHubPermissionDefinition]::new( + 'repository_advisories', + 'Repository security advisories', + 'View and manage repository security advisories.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#repository-permissions-for-repository-security-advisories', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Repository' + ), + [GitHubPermissionDefinition]::new( + 'repo_secret_scanning_dismissal_requests', + 'Secret scanning alert dismissal requests', + 'View and manage secret scanning alert dismissal requests', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#repository-permissions-for-secret-scanning-alert-dismissal-requests', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Repository' + ), + [GitHubPermissionDefinition]::new( + 'secret_scanning_alerts', + 'Secret scanning alerts', + 'View and manage secret scanning alerts.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#repository-permissions-for-secret-scanning-alerts', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Repository' + ), + [GitHubPermissionDefinition]::new( + 'secret_scanning_bypass_requests', + 'Secret scanning push protection bypass requests', + 'Review and manage repository secret scanning push protection bypass requests.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#repository-permissions-for-secret-scanning-push-protection-bypass-requests', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Repository' + ), + [GitHubPermissionDefinition]::new( + 'secrets', + 'Secrets', + 'Manage Actions repository secrets.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#repository-permissions-for-secrets', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Repository' + ), + [GitHubPermissionDefinition]::new( + 'single_file', + 'Single file', + 'Manage just a single file.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#repository-permissions-for-single-file', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Repository' + ), + [GitHubPermissionDefinition]::new( + 'actions_variables', + 'Variables', + 'Manage Actions repository variables.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#repository-permissions-for-variables', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Repository' + ), + [GitHubPermissionDefinition]::new( + 'repository_hooks', + 'Webhooks', + 'Manage the post-receive hooks for a repository.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#repository-permissions-for-webhooks', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Repository' + ), + [GitHubPermissionDefinition]::new( + 'workflows', + 'Workflows', + 'Update GitHub Action workflow files.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#repository-permissions-for-workflows', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Repository' + ), + + # ------------------------------ + # Organization Fine-Grained Permission Definitions + # ------------------------------ + [GitHubPermissionDefinition]::new( + 'organization_api_insights', + 'API Insights', + 'View statistics on how the API is being used for an organization.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#organization-permissions-for-api-insights', + @( + 'read' + ), + 'Fine-grained', + 'Organization' + ), + [GitHubPermissionDefinition]::new( + 'organization_administration', + 'Administration', + 'Manage access to an organization.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#organization-permissions-for-administration', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Organization' + ), + [GitHubPermissionDefinition]::new( + 'organization_user_blocking', + 'Blocking users', + 'View and manage users blocked by the organization.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#organization-permissions-for-blocking-users', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Organization' + ), + [GitHubPermissionDefinition]::new( + 'organization_campaigns', + 'Campaigns', + 'Manage campaigns.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#organization-permissions-for-campaigns', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Organization' + ), + [GitHubPermissionDefinition]::new( + 'organization_custom_org_roles', + 'Custom organization roles', + 'Create, edit, delete and list custom organization roles. View system organization roles.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#organization-permissions-for-custom-organization-roles', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Organization' + ), + [GitHubPermissionDefinition]::new( + 'organization_custom_properties', + 'Custom properties', + 'Read and write repository custom properties values and administer definitions at the organization level.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#organization-permissions-for-custom-properties', + @( + 'read', + 'write', + 'admin' + ), + 'Fine-grained', + 'Organization' + ), + [GitHubPermissionDefinition]::new( + 'organization_custom_roles', + 'Custom repository roles', + 'Create, edit, delete and list custom repository roles.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#organization-permissions-for-custom-repository-roles', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Organization' + ), + [GitHubPermissionDefinition]::new( + 'organization_events', + 'Events', + 'View events triggered by an activity in an organization.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#organization-permissions-for-events', + @( + 'read' + ), + 'Fine-grained', + 'Organization' + ), + [GitHubPermissionDefinition]::new( + 'organization_copilot_seat_management', + 'GitHub Copilot Business', + 'Manage Copilot Business seats and settings', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#organization-permissions-for-github-copilot-business', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Organization' + ), + [GitHubPermissionDefinition]::new( + 'issue_fields', + 'Issue Fields', + 'Manage issue fields for an organization.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#organization-permissions-for-issue-fields', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Organization' + ), + [GitHubPermissionDefinition]::new( + 'issue_types', + 'Issue Types', + 'Manage issue types for an organization.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#organization-permissions-for-issue-types', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Organization' + ), + [GitHubPermissionDefinition]::new( + 'organization_knowledge_bases', + 'Knowledge bases', + 'View and manage knowledge bases for an organization.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#organization-permissions-for-knowledge-bases', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Organization' + ), + [GitHubPermissionDefinition]::new( + 'members', + 'Members', + 'Organization members and teams.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#organization-permissions-for-members', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Organization' + ), + [GitHubPermissionDefinition]::new( + 'organization_models', + 'Models', + 'Manage model access for an organization.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#organization-permissions-for-models', + @( + 'read' + ), + 'Fine-grained', + 'Organization' + ), + [GitHubPermissionDefinition]::new( + 'organization_network_configurations', + 'Network configurations', + 'View and manage hosted compute network configurations available to an organization.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#organization-permissions-for-network-configurations', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Organization' + ), + [GitHubPermissionDefinition]::new( + 'organization_announcement_banners', + 'Organization announcement banners', + 'View and modify announcement banners for an organization.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#organization-permissions-for-organization-announcement-banners', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Organization' + ), + [GitHubPermissionDefinition]::new( + 'organization_secret_scanning_bypass_requests', + 'Organization bypass requests for secret scanning', + 'Review and manage secret scanning push protection bypass requests.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#organization-permissions-for-organization-bypass-requests-for-secret-scanning', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Organization' + ), + [GitHubPermissionDefinition]::new( + 'organization_codespaces', + 'Organization codespaces', + 'Manage Codespaces for an organization.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#organization-permissions-for-organization-codespaces', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Organization' + ), + [GitHubPermissionDefinition]::new( + 'organization_codespaces_secrets', + 'Organization codespaces secrets', + 'Manage Codespaces Secrets for an organization.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#organization-permissions-for-organization-codespaces-secrets', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Organization' + ), + [GitHubPermissionDefinition]::new( + 'organization_codespaces_settings', + 'Organization codespaces settings', + 'Manage Codespaces settings for an organization.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#organization-permissions-for-organization-codespaces-settings', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Organization' + ), + [GitHubPermissionDefinition]::new( + 'organization_dependabot_secrets', + 'Organization dependabot secrets', + 'Manage Dependabot organization secrets.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#organization-permissions-for-organization-dependabot-secrets', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Organization' + ), + [GitHubPermissionDefinition]::new( + 'organization_code_scanning_dismissal_requests', + 'Organization dismissal requests for code scanning', + 'Review and manage code scanning alert dismissal requests.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#organization-permissions-for-organization-dismissal-requests-for-code-scanning', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Organization' + ), + [GitHubPermissionDefinition]::new( + 'organization_private_registries', + 'Organization private registries', + 'Manage private registries for an organization.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#organization-permissions-for-organization-private-registries', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Organization' + ), + [GitHubPermissionDefinition]::new( + 'organization_personal_access_token_requests', + 'Personal access token requests', + 'Manage personal access token requests from organization members.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#organization-permissions-for-personal-access-token-requests', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Organization' + ), + [GitHubPermissionDefinition]::new( + 'organization_personal_access_tokens', + 'Personal access tokens', + 'View and revoke personal access tokens that have been granted access to an organization.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#organization-permissions-for-personal-access-tokens', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Organization' + ), + [GitHubPermissionDefinition]::new( + 'organization_plan', + 'Plan', + "View an organization's plan.", + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#organization-permissions-for-plan', + @( + 'read' + ), + 'Fine-grained', + 'Organization' + ), + [GitHubPermissionDefinition]::new( + 'organization_projects', + 'Projects', + 'Manage projects for an organization.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#organization-permissions-for-projects', + @( + 'read', + 'write', + 'admin' + ), + 'Fine-grained', + 'Organization' + ), + [GitHubPermissionDefinition]::new( + 'secret_scanning_dismissal_requests', + 'Secret scanning alert dismissal requests', + 'Review and manage secret scanning alert dismissal requests', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#organization-permissions-for-secret-scanning-alert-dismissal-requests', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Organization' + ), + [GitHubPermissionDefinition]::new( + 'organization_secrets', + 'Secrets', + 'Manage Actions organization secrets.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#organization-permissions-for-secrets', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Organization' + ), + [GitHubPermissionDefinition]::new( + 'organization_self_hosted_runners', + 'Self-hosted runners', + 'View and manage Actions self-hosted runners available to an organization.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#organization-permissions-for-self-hosted-runners', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Organization' + ), + [GitHubPermissionDefinition]::new( + 'team_discussions', + 'Team discussions', + 'Manage team discussions and related comments.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#organization-permissions-for-team-discussions', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Organization' + ), + [GitHubPermissionDefinition]::new( + 'organization_actions_variables', + 'Variables', + 'Manage Actions organization variables.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#organization-permissions-for-variables', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Organization' + ), + [GitHubPermissionDefinition]::new( + 'organization_hooks', + 'Webhooks', + 'Manage the post-receive hooks for an organization.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#organization-permissions-for-webhooks', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Organization' + ), + + # ------------------------------ + # User (Account) Fine-Grained Permission Definitions + # ------------------------------ + [GitHubPermissionDefinition]::new( + 'blocking', + 'Block another user', + 'View and manage users blocked by the user.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#user-permissions-for-block-another-user', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'User' + ), + [GitHubPermissionDefinition]::new( + 'codespaces_user_secrets', + 'Codespaces user secrets', + 'Manage Codespaces user secrets.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#user-permissions-for-codespaces-user-secrets', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'User' + ), + [GitHubPermissionDefinition]::new( + 'copilot_messages', + 'Copilot Chat', + 'This application will receive your GitHub ID, your GitHub Copilot Chat session messages ' + + '(not including messages sent to another application), and timestamps of provided GitHub Copilot ' + + 'Chat session messages. This permission must be enabled for Copilot Extensions.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#user-permissions-for-copilot-chat', + @( + 'read' + ), + 'Fine-grained', + 'User' + ), + [GitHubPermissionDefinition]::new( + 'copilot_editor_context', + 'Copilot Editor Context', + 'This application will receive bits of Editor Context (e.g. currently opened file) whenever you send it a message through Copilot Chat.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#user-permissions-for-copilot-editor-context', + @( + 'read' + ), + 'Fine-grained', + 'User' + ), + [GitHubPermissionDefinition]::new( + 'emails', + 'Email addresses', + "Manage a user's email addresses.", + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#user-permissions-for-email-addresses', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'User' + ), + [GitHubPermissionDefinition]::new( + 'user_events', + 'Events', + "View events triggered by a user's activity.", + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#user-permissions-for-events', + @( + 'read' + ), + 'Fine-grained', + 'User' + ), + [GitHubPermissionDefinition]::new( + 'followers', + 'Followers', + "A user's followers", + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#user-permissions-for-followers', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'User' + ), + [GitHubPermissionDefinition]::new( + 'gpg_keys', + 'GPG keys', + "View and manage a user's GPG keys.", + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#user-permissions-for-gpg-keys', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'User' + ), + [GitHubPermissionDefinition]::new( + 'gists', + 'Gists', + "Create and modify a user's gists and comments.", + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#user-permissions-for-gists', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'User' + ), + [GitHubPermissionDefinition]::new( + 'keys', + 'Git SSH keys', + 'Git SSH keys', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#user-permissions-for-git-ssh-keys', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'User' + ), + [GitHubPermissionDefinition]::new( + 'interaction_limits', + 'Interaction limits', + 'Interaction limits on repositories', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#user-permissions-for-interaction-limits', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'User' + ), + [GitHubPermissionDefinition]::new( + 'knowledge_bases', + 'Knowledge bases', + 'View knowledge bases for a user.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#user-permissions-for-knowledge-bases', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'User' + ), + [GitHubPermissionDefinition]::new( + 'user_models', + 'Models', + 'Allows access to GitHub Models.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#user-permissions-for-models', + @( + 'read' + ), + 'Fine-grained', + 'User' + ), + [GitHubPermissionDefinition]::new( + 'plan', + 'Plan', + "View a user's plan.", + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#user-permissions-for-plan', + @( + 'read' + ), + 'Fine-grained', + 'User' + ), + [GitHubPermissionDefinition]::new( + 'profile', + 'Profile', + "Manage a user's profile settings.", + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#user-permissions-for-profile', + @( + 'write' + ), + 'Fine-grained', + 'User' + ), + [GitHubPermissionDefinition]::new( + 'git_signing_ssh_public_keys', + 'SSH signing keys', + "View and manage a user's SSH signing keys.", + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#user-permissions-for-ssh-signing-keys', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'User' + ), + [GitHubPermissionDefinition]::new( + 'starring', + 'Starring', + 'List and manage repositories a user is starring.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#user-permissions-for-starring', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'User' + ), + [GitHubPermissionDefinition]::new( + 'watching', + 'Watching', + 'List and change repositories a user is subscribed to.', + 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + + '#user-permissions-for-watching', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'User' + ), + + # ------------------------------ + # Enterprise Fine-Grained Permission Definitions + # ------------------------------ + [GitHubPermissionDefinition]::new( + 'enterprise_custom_properties', + 'Custom properties', + 'View repository custom properties and administer definitions at the enterprise level.', + 'https://docs.github.com/enterprise-cloud@latest/rest/overview/permissions-required-for-github-apps' + + '#enterprise-permissions-for-custom-properties', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Enterprise' + ), + [GitHubPermissionDefinition]::new( + 'enterprise_custom_org_roles', + 'Enterprise custom organization roles', + 'Create, edit, delete and list custom organization roles at the enterprise level. View system organization roles.', + 'https://docs.github.com/enterprise-cloud@latest/rest/overview/permissions-required-for-github-apps' + + '#enterprise-permissions-for-enterprise-custom-organization-roles', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Enterprise' + ), + [GitHubPermissionDefinition]::new( + 'enterprise_organization_installation_repositories', + 'Enterprise organization installation repositories', + 'Manage repository access of GitHub Apps on Enterprise-owned organizations', + 'https://docs.github.com/enterprise-cloud@latest/rest/overview/permissions-required-for-github-apps' + + '#enterprise-permissions-for-enterprise-organization-installation-repositories', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Enterprise' + ), + [GitHubPermissionDefinition]::new( + 'enterprise_organization_installations', + 'Enterprise organization installations', + 'Manage installation of GitHub Apps on Enterprise-owned organizations', + 'https://docs.github.com/enterprise-cloud@latest/rest/overview/permissions-required-for-github-apps' + + '#enterprise-permissions-for-enterprise-organization-installations', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Enterprise' + ), + [GitHubPermissionDefinition]::new( + 'enterprise_organizations', + 'Enterprise organizations', + 'Create and remove enterprise organizations', + 'https://docs.github.com/enterprise-cloud@latest/rest/overview/permissions-required-for-github-apps' + + '#enterprise-permissions-for-enterprise-organizations', + @( + 'write' + ), + 'Fine-grained', + 'Enterprise' + ), + [GitHubPermissionDefinition]::new( + 'enterprise_people', + 'Enterprise people', + 'Manage user access to the enterprise', + 'https://docs.github.com/enterprise-cloud@latest/rest/overview/permissions-required-for-github-apps' + + '#enterprise-permissions-for-enterprise-people', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Enterprise' + ), + [GitHubPermissionDefinition]::new( + 'enterprise_sso', + 'Enterprise single sign-on', + 'View and manage enterprise single sign-on configuration', + 'https://docs.github.com/enterprise-cloud@latest/rest/overview/permissions-required-for-github-apps' + + '#enterprise-permissions-for-enterprise-single-sign-on', + @( + 'read', + 'write' + ), + 'Fine-grained', + 'Enterprise' + ) + ) + + GitHubPermissionDefinition() {} + + GitHubPermissionDefinition( + [string]$Name, + [string]$DisplayName, + [string]$Description, + [string]$URL, + [string[]]$Options, + [string]$Type, + [string]$Scope + ) { + $this.Name = $Name + $this.DisplayName = $DisplayName + $this.Description = $Description + $this.URL = [uri]$URL + $this.Options = $Options + $this.Type = $Type + $this.Scope = $Scope + } + + [string] ToString() { + return $this.Name + } +} + +class GitHubPermission : GitHubPermissionDefinition { + # The value assigned to the permission. Must be one of the options defined in the parent class. + [string] $Value + + GitHubPermission() : base() {} + + GitHubPermission([string] $Permission, [string] $Value) : base() { + $this.Name = $Permission + $this.Value = $Value + $this.DisplayName = $Permission + $this.Description = 'Unknown permission - Open issue to add metadata' + $this.URL = $null + $this.Options = @() + $this.Type = 'Unknown' + $this.Scope = 'Unknown' + } + + # Create a new list of all known permissions with null values + static [GitHubPermission[]] NewPermissionList() { + $tmpList = foreach ($def in [GitHubPermissionDefinition]::List) { + [GitHubPermission]@{ + Name = $def.Name + Value = $null + DisplayName = $def.DisplayName + Description = $def.Description + URL = $def.URL + Options = $def.Options + Type = $def.Type + Scope = $def.Scope + } + } + return $tmpList | Sort-Object Scope, DisplayName + } + + # Create a new list of permissions filtered by installation type with null values + static [GitHubPermission[]] NewPermissionList([string] $InstallationType) { + $all = [GitHubPermission]::NewPermissionList() + $returned = switch ($InstallationType) { + 'Enterprise' { $all | Where-Object { $_.Scope -eq 'Enterprise' } } + 'Organization' { $all | Where-Object { $_.Scope -in @('Organization', 'Repository') } } + 'User' { $all | Where-Object { $_.Scope -in @('Repository') } } + default { $all } + } + return $returned | Sort-Object Scope, DisplayName + } + + # Create a new list of all permissions with values from a PSCustomObject + static [GitHubPermission[]] NewPermissionList([pscustomobject] $Object) { + $all = [GitHubPermission]::NewPermissionList() + foreach ($name in $Object.PSObject.Properties.Name) { + $objectValue = $Object.$name + $knownPermission = $all | Where-Object { $_.Name -eq $name } + if ($knownPermission) { + $knownPermission.Value = $objectValue + } else { + $all += [GitHubPermission]::new($name, $objectValue) + } + } + return $all | Sort-Object Scope, DisplayName + } + + # Create a new list of permissions filtered by installation type with values from a PSCustomObject + static [GitHubPermission[]] NewPermissionList([pscustomobject] $Object, [string] $InstallationType) { + $all = [GitHubPermission]::NewPermissionList($InstallationType) + foreach ($name in $Object.PSObject.Properties.Name) { + $objectValue = $Object.$name + $knownPermission = $all | Where-Object { $_.Name -eq $name } + if ($knownPermission) { + $knownPermission.Value = $objectValue + } else { + $all += [GitHubPermission]::new($name, $objectValue) + } + } + return $all | Sort-Object Scope, DisplayName + } + + # Create a new list of permissions with values from an array of objects (import functionality) + static [GitHubPermission[]] NewPermissionList([object[]] $Objects) { + $all = [GitHubPermission]::NewPermissionList() + foreach ($obj in $Objects) { + $knownPermission = $all | Where-Object { $_.Name -eq $obj.Name } + if ($knownPermission) { + $knownPermission.Value = $obj.Value + } else { + $all += [GitHubPermission]::new($obj.Name, $obj.Value) + } + } + return $all | Sort-Object Scope, DisplayName + } + + # Create a new list of permissions filtered by installation type with values from an array of objects (import functionality) + static [GitHubPermission[]] NewPermissionList([object[]] $Objects, [string] $InstallationType) { + $all = [GitHubPermission]::NewPermissionList($InstallationType) + foreach ($obj in $Objects) { + $knownPermission = $all | Where-Object { $_.Name -eq $obj.Name } + if ($knownPermission) { + $knownPermission.Value = $obj.Value + } else { + $all += [GitHubPermission]::new($obj.Name, $obj.Value) + } + } + return $all | Sort-Object Scope, DisplayName + } +} diff --git a/src/classes/public/GitHubPermissionDefinition.ps1 b/src/classes/public/GitHubPermissionDefinition.ps1 deleted file mode 100644 index 93ff6643f..000000000 --- a/src/classes/public/GitHubPermissionDefinition.ps1 +++ /dev/null @@ -1,46 +0,0 @@ -class GitHubPermissionDefinition { - # The programmatic name of the permission as returned by the GitHub API - [string] $Name - - # The human-friendly name of the permission as shown in the GitHub UI - [string] $DisplayName - - # A brief description of what access the permission grants - [string] $Description - - # A link to the relevant documentation or GitHub UI page - [uri] $URL - - # The levels of access that can be granted for this permission - [string[]] $Options - - # The type of permission (Fine-grained, Classic) - [string] $Type - - # The scope at which this permission applies (Repository, Organization, User, Enterprise) - [string] $Scope - - GitHubPermissionDefinition() {} - - GitHubPermissionDefinition( - [string]$Name, - [string]$DisplayName, - [string]$Description, - [string]$URL, - [string[]]$Options, - [string]$Type, - [string]$Scope - ) { - $this.Name = $Name - $this.DisplayName = $DisplayName - $this.Description = $Description - $this.URL = [uri]$URL - $this.Options = $Options - $this.Type = $Type - $this.Scope = $Scope - } - - [string] ToString() { - return $this.Name - } -} diff --git a/src/formats/GitHubAppInstallation.Format.ps1xml b/src/formats/GitHubAppInstallation.Format.ps1xml index 876cc43b7..d81a69fe6 100644 --- a/src/formats/GitHubAppInstallation.Format.ps1xml +++ b/src/formats/GitHubAppInstallation.Format.ps1xml @@ -20,6 +20,9 @@ + + + @@ -56,6 +59,9 @@ Type + + Status + CreatedAt @@ -88,6 +94,9 @@ Type + + Status + Url diff --git a/src/formats/GitHubPermission.Format.ps1xml b/src/formats/GitHubPermission.Format.ps1xml new file mode 100644 index 000000000..d917b0c3c --- /dev/null +++ b/src/formats/GitHubPermission.Format.ps1xml @@ -0,0 +1,52 @@ + + + + + GitHubPermissionTable + + GitHubPermission + + + + + + + + + + + + + + + + + + + + + Scope + + + + if ($Host.UI.SupportsVirtualTerminal -and + ($env:GITHUB_ACTIONS -ne 'true') -and $_.Url) { + $PSStyle.FormatHyperlink($_.DisplayName, $_.Url) + } else { + $_.DisplayName + } + + + + Value + + + Description + + + + + + + + diff --git a/src/functions/private/Apps/GitHub Apps/Get-GitHubAppInstallationForAuthenticatedApp.ps1 b/src/functions/private/Apps/GitHub Apps/Get-GitHubAppInstallationForAuthenticatedApp.ps1 index afbe9e03c..58f595659 100644 --- a/src/functions/private/Apps/GitHub Apps/Get-GitHubAppInstallationForAuthenticatedApp.ps1 +++ b/src/functions/private/Apps/GitHub Apps/Get-GitHubAppInstallationForAuthenticatedApp.ps1 @@ -46,9 +46,12 @@ Context = $Context } + # Get the authenticated app to compare permissions and events + $authenticatedApp = Get-GitHubAuthenticatedApp -Context $Context + Invoke-GitHubAPI @apiParams | ForEach-Object { foreach ($installation in $_.Response) { - [GitHubAppInstallation]::new($installation) + [GitHubAppInstallation]::new($installation, $authenticatedApp) } } } diff --git a/src/functions/private/Auth/Context/Set-GitHubContext.ps1 b/src/functions/private/Auth/Context/Set-GitHubContext.ps1 index c20ea4281..91ba59e6f 100644 --- a/src/functions/private/Auth/Context/Set-GitHubContext.ps1 +++ b/src/functions/private/Auth/Context/Set-GitHubContext.ps1 @@ -127,7 +127,7 @@ $contextObj['Username'] = [string]$app.Slug $contextObj['NodeID'] = [string]$app.NodeID $contextObj['DatabaseID'] = [string]$app.ID - $contextObj['Permissions'] = [PSCustomObject]$app.Permissions + $contextObj['Permissions'] = [GitHubPermission[]]$app.Permissions $contextObj['Events'] = [string[]]$app.Events $contextObj['OwnerName'] = [string]$app.Owner.Name $contextObj['OwnerType'] = [string]$app.Owner.Type diff --git a/src/functions/public/Auth/Connect-GitHubApp.ps1 b/src/functions/public/Auth/Connect-GitHubApp.ps1 index e69343465..7e208f006 100644 --- a/src/functions/public/Auth/Connect-GitHubApp.ps1 +++ b/src/functions/public/Auth/Connect-GitHubApp.ps1 @@ -134,9 +134,9 @@ HttpVersion = [string]$Context.HttpVersion PerPage = [int]$Context.PerPage ClientID = [string]$Context.ClientID - InstallationID = [string]$installation.id - Permissions = [pscustomobject]$installation.permissions - Events = [string[]]$installation.events + InstallationID = [string]$installation.ID + Permissions = [GitHubPermission[]]$installation.Permissions + Events = [string[]]$installation.Events InstallationType = [string]$installation.Type Token = [securestring]$token.Token TokenExpiresAt = [datetime]$token.ExpiresAt diff --git a/src/functions/public/Auth/Context/Switch-GitHubContext.ps1 b/src/functions/public/Auth/Context/Switch-GitHubContext.ps1 index 0a6149a66..b4f7ed876 100644 --- a/src/functions/public/Auth/Context/Switch-GitHubContext.ps1 +++ b/src/functions/public/Auth/Context/Switch-GitHubContext.ps1 @@ -16,7 +16,7 @@ param( # The context to run the command in. Used to get the details for the API call. # Can be either a string or a GitHubContext object. - [Parameter(ValueFromPipeline)] + [Parameter(Mandatory, ValueFromPipeline, Position = 0)] [object] $Context ) diff --git a/src/functions/public/Config/completers.ps1 b/src/functions/public/Config/completers.ps1 index 1165d3170..c498f0497 100644 --- a/src/functions/public/Config/completers.ps1 +++ b/src/functions/public/Config/completers.ps1 @@ -10,10 +10,37 @@ Register-ArgumentCompleter -CommandName Set-GitHubConfig -ParameterName Value -ScriptBlock { param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters) $null = $commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters - if ($fakeBoundParameters.Name -eq 'CompletionMode') { - $pattern = switch (Get-GitHubConfig -Name CompletionMode) { 'Contains' { "*$wordToComplete*" } default { "$wordToComplete*" } } - @('StartsWith', 'Contains') | Where-Object { $_ -like $pattern } | ForEach-Object { - [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_) + $pattern = switch (Get-GitHubConfig -Name CompletionMode) { 'Contains' { "*$wordToComplete*" } default { "$wordToComplete*" } } + switch ($fakeBoundParameters.Name) { + 'CompletionMode' { + @('StartsWith', 'Contains') | Where-Object { $_ -like $pattern } | ForEach-Object { + [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_) + } + } + 'HttpVersion' { + @('1.0', '1.1', '2.0', '3.0') | Where-Object { $_ -like $pattern } | ForEach-Object { + [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_) + } + } + 'EnvironmentType' { + @('Local', 'GitHubActions', 'FunctionApp', 'Unknown') | Where-Object { $_ -like $pattern } | ForEach-Object { + [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_) + } + } + 'ApiVersion' { + $params = @{ + Context = $fakeBoundParameters.Context + Debug = $false + Verbose = $false + } + Get-GitHubApiVersion @params | Where-Object { $_ -like $pattern } | ForEach-Object { + [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_) + } + } + 'DefaultContext' { + Get-GitHubContext -ListAvailable | Where-Object { $_.Name -like $pattern } | ForEach-Object { + [System.Management.Automation.CompletionResult]::new($_.Name, $_.Name, 'ParameterValue', $_.Name) + } } } } diff --git a/src/functions/public/Meta/Get-GitHubApiVersion.ps1 b/src/functions/public/Meta/Get-GitHubApiVersion.ps1 index 7a618a104..cd9ac2b0a 100644 --- a/src/functions/public/Meta/Get-GitHubApiVersion.ps1 +++ b/src/functions/public/Meta/Get-GitHubApiVersion.ps1 @@ -34,10 +34,13 @@ $stackPath = Get-PSCallStackPath Write-Debug "[$stackPath] - Start" $Context = Resolve-GitHubContext -Context $Context -Anonymous $Anonymous - Assert-GitHubContext -Context $Context -AuthType IAT, PAT, UAT, Anonymous + Assert-GitHubContext -Context $Context -AuthType App, IAT, PAT, UAT, Anonymous } process { + if ($Context.AuthType -eq 'APP') { + $Context = 'Anonymous' + } $apiParams = @{ Method = 'GET' ApiEndpoint = '/versions' diff --git a/src/functions/public/Permission/Get-GitHubPermissionDefinition.ps1 b/src/functions/public/Permission/Get-GitHubPermissionDefinition.ps1 index 0019adf59..f7db75c69 100644 --- a/src/functions/public/Permission/Get-GitHubPermissionDefinition.ps1 +++ b/src/functions/public/Permission/Get-GitHubPermissionDefinition.ps1 @@ -86,7 +86,7 @@ function Get-GitHubPermissionDefinition { return $false } - $script:GitHub.Permissions | Where-Object { + [GitHubPermissionDefinition]::List | Where-Object { (& $test -Value $_.Name -Patterns $Name) -and (& $test -Value $_.DisplayName -Patterns $DisplayName) -and (& $test -Value $_.Type -Patterns $Type) -and diff --git a/src/functions/public/Permission/completers.ps1 b/src/functions/public/Permission/completers.ps1 index 4586354f0..dc0be7453 100644 --- a/src/functions/public/Permission/completers.ps1 +++ b/src/functions/public/Permission/completers.ps1 @@ -2,7 +2,7 @@ param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters) $null = $commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters $pattern = switch (Get-GitHubConfig -Name CompletionMode) { 'Contains' { "*$wordToComplete*" } default { "$wordToComplete*" } } - $script:GitHub.Permissions.Name | Sort-Object -Unique | Where-Object { $_ -like $pattern } | ForEach-Object { + [GitHubPermissionDefinition]::List.Name | Sort-Object -Unique | Where-Object { $_ -like $pattern } | ForEach-Object { [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_) } } @@ -11,7 +11,7 @@ Register-ArgumentCompleter -CommandName Get-GitHubPermissionDefinition -Paramete param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters) $null = $commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters $pattern = switch (Get-GitHubConfig -Name CompletionMode) { 'Contains' { "*$wordToComplete*" } default { "$wordToComplete*" } } - $script:GitHub.Permissions.DisplayName | Sort-Object -Unique | Where-Object { $_ -like $pattern } | ForEach-Object { + [GitHubPermissionDefinition]::List.DisplayName | Sort-Object -Unique | Where-Object { $_ -like $pattern } | ForEach-Object { [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_) } } @@ -20,7 +20,7 @@ Register-ArgumentCompleter -CommandName Get-GitHubPermissionDefinition -Paramete param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters) $null = $commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters $pattern = switch (Get-GitHubConfig -Name CompletionMode) { 'Contains' { "*$wordToComplete*" } default { "$wordToComplete*" } } - $script:GitHub.Permissions.Type | Sort-Object -Unique | Where-Object { $_ -like $pattern } | ForEach-Object { + [GitHubPermissionDefinition]::List.Type | Sort-Object -Unique | Where-Object { $_ -like $pattern } | ForEach-Object { [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_) } } @@ -29,7 +29,7 @@ Register-ArgumentCompleter -CommandName Get-GitHubPermissionDefinition -Paramete param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters) $null = $commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters $pattern = switch (Get-GitHubConfig -Name CompletionMode) { 'Contains' { "*$wordToComplete*" } default { "$wordToComplete*" } } - $script:GitHub.Permissions.Scope | Sort-Object -Unique | Where-Object { $_ -like $pattern } | ForEach-Object { + [GitHubPermissionDefinition]::List.Scope | Sort-Object -Unique | Where-Object { $_ -like $pattern } | ForEach-Object { [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_) } } diff --git a/src/variables/private/GitHub.ps1 b/src/variables/private/GitHub.ps1 index 190de6d2d..67f82414e 100644 --- a/src/variables/private/GitHub.ps1 +++ b/src/variables/private/GitHub.ps1 @@ -4,7 +4,6 @@ $script:IsLocal = -not ($script:IsGitHubActions -or $script:IsFunctionApp) $script:GitHub = [pscustomobject]@{ ContextVault = 'PSModule.GitHub' TokenPrefixPattern = '(?<=^(ghu|gho|ghs|github_pat|ghp)).*' - EnvironmentType = Get-GitHubEnvironmentType DefaultConfig = [GitHubConfig]@{ ID = 'Module' HostName = ($env:GITHUB_SERVER_URL ?? 'github.com') -replace '^https?://' @@ -24,1184 +23,4 @@ $script:GitHub = [pscustomobject]@{ Config = $null Event = $null Runner = $null - Permissions = @( - # ------------------------------ - # Repository Fine-Grained Permission Definitions - # ------------------------------ - [GitHubPermissionDefinition]::new( - 'actions', - 'Actions', - 'Workflows, workflow runs and artifacts.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#repository-permissions-for-actions', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Repository' - ), - [GitHubPermissionDefinition]::new( - 'administration', - 'Administration', - 'Repository creation, deletion, settings, teams, and collaborators.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#repository-permissions-for-administration', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Repository' - ), - [GitHubPermissionDefinition]::new( - 'attestations', - 'Attestations', - 'Create and retrieve attestations for a repository.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#repository-permissions-for-attestations', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Repository' - ), - [GitHubPermissionDefinition]::new( - 'checks', - 'Checks', - 'Checks on code.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#repository-permissions-for-checks', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Repository' - ), - [GitHubPermissionDefinition]::new( - 'security_events', - 'Code scanning alerts', - 'View and manage code scanning alerts.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#repository-permissions-for-code-scanning-alerts', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Repository' - ), - [GitHubPermissionDefinition]::new( - 'codespaces', - 'Codespaces', - 'Create, edit, delete and list Codespaces.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#repository-permissions-for-codespaces', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Repository' - ), - [GitHubPermissionDefinition]::new( - 'codespaces_lifecycle_admin', - 'Codespaces lifecycle admin', - 'Manage the lifecycle of Codespaces, including starting and stopping.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#repository-permissions-for-codespaces-lifecycle-admin', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Repository' - ), - [GitHubPermissionDefinition]::new( - 'codespaces_metadata', - 'Codespaces metadata', - 'Access Codespaces metadata including the devcontainers and machine type.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#repository-permissions-for-codespaces-metadata', - @( - 'read' - ), - 'Fine-grained', - 'Repository' - ), - [GitHubPermissionDefinition]::new( - 'codespaces_secrets', - 'Codespaces secrets', - 'Restrict Codespaces user secrets modifications to specific repositories.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#repository-permissions-for-codespaces-secrets', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Repository' - ), - [GitHubPermissionDefinition]::new( - 'statuses', - 'Commit statuses', - 'Commit statuses.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#repository-permissions-for-commit-statuses', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Repository' - ), - [GitHubPermissionDefinition]::new( - 'contents', - 'Contents', - 'Repository contents, commits, branches, downloads, releases, and merges.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#repository-permissions-for-contents', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Repository' - ), - [GitHubPermissionDefinition]::new( - 'repository_custom_properties', - 'Custom properties', - 'Read and write repository custom properties values at the repository level, when allowed by the property.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#repository-permissions-for-custom-properties', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Repository' - ), - [GitHubPermissionDefinition]::new( - 'vulnerability_alerts', - 'Dependabot alerts', - 'Retrieve Dependabot alerts.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#repository-permissions-for-dependabot-alerts', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Repository' - ), - [GitHubPermissionDefinition]::new( - 'dependabot_secrets', - 'Dependabot secrets', - 'Manage Dependabot repository secrets.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#repository-permissions-for-dependabot-secrets', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Repository' - ), - [GitHubPermissionDefinition]::new( - 'deployments', - 'Deployments', - 'Deployments and deployment statuses.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#repository-permissions-for-deployments', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Repository' - ), - [GitHubPermissionDefinition]::new( - 'discussions', - 'Discussions', - 'Discussions and related comments and labels.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#repository-permissions-for-discussions', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Repository' - ), - [GitHubPermissionDefinition]::new( - 'environments', - 'Environments', - 'Manage repository environments.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#repository-permissions-for-environments', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Repository' - ), - [GitHubPermissionDefinition]::new( - 'issues', - 'Issues', - 'Issues and related comments, assignees, labels, and milestones.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#repository-permissions-for-issues', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Repository' - ), - [GitHubPermissionDefinition]::new( - 'merge_queues', - 'Merge queues', - "Manage a repository's merge queues", - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#repository-permissions-for-merge-queues', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Repository' - ), - [GitHubPermissionDefinition]::new( #Mandatory - 'metadata', - 'Metadata', - 'Search repositories, list collaborators, and access repository metadata.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#repository-permissions-for-metadata', - @( - 'read' - ), - 'Fine-grained', - 'Repository' - ), - [GitHubPermissionDefinition]::new( - 'packages', - 'Packages', - 'Packages published to the GitHub Package Platform.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#repository-permissions-for-packages', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Repository' - ), - [GitHubPermissionDefinition]::new( - 'pages', - 'Pages', - 'Retrieve Pages statuses, configuration, and builds, as well as create new builds.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#repository-permissions-for-pages', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Repository' - ), - [GitHubPermissionDefinition]::new( - 'repository_projects', - 'Projects', - 'Manage classic projects within a repository.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#repository-permissions-for-projects', - @( - 'read', - 'write', - 'admin' - ), - 'Fine-grained', - 'Repository' - ), - [GitHubPermissionDefinition]::new( - 'pull_requests', - 'Pull requests', - 'Pull requests and related comments, assignees, labels, milestones, and merges.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#repository-permissions-for-pull-requests', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Repository' - ), - [GitHubPermissionDefinition]::new( - 'repository_advisories', - 'Repository security advisories', - 'View and manage repository security advisories.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#repository-permissions-for-repository-security-advisories', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Repository' - ), - [GitHubPermissionDefinition]::new( - 'repo_secret_scanning_dismissal_requests', - 'Secret scanning alert dismissal requests', - 'View and manage secret scanning alert dismissal requests', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#repository-permissions-for-secret-scanning-alert-dismissal-requests', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Repository' - ), - [GitHubPermissionDefinition]::new( - 'secret_scanning_alerts', - 'Secret scanning alerts', - 'View and manage secret scanning alerts.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#repository-permissions-for-secret-scanning-alerts', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Repository' - ), - [GitHubPermissionDefinition]::new( - 'secret_scanning_bypass_requests', - 'Secret scanning push protection bypass requests', - 'Review and manage repository secret scanning push protection bypass requests.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#repository-permissions-for-secret-scanning-push-protection-bypass-requests', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Repository' - ), - [GitHubPermissionDefinition]::new( - 'secrets', - 'Secrets', - 'Manage Actions repository secrets.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#repository-permissions-for-secrets', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Repository' - ), - [GitHubPermissionDefinition]::new( - 'single_file', - 'Single file', - 'Manage just a single file.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#repository-permissions-for-single-file', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Repository' - ), - [GitHubPermissionDefinition]::new( - 'actions_variables', - 'Variables', - 'Manage Actions repository variables.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#repository-permissions-for-variables', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Repository' - ), - [GitHubPermissionDefinition]::new( - 'repository_hooks', - 'Webhooks', - 'Manage the post-receive hooks for a repository.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#repository-permissions-for-webhooks', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Repository' - ), - [GitHubPermissionDefinition]::new( - 'workflows', - 'Workflows', - 'Update GitHub Action workflow files.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#repository-permissions-for-workflows', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Repository' - ), - - # ------------------------------ - # Organization Fine-Grained Permission Definitions - # ------------------------------ - [GitHubPermissionDefinition]::new( - 'organization_api_insights', - 'API Insights', - 'View statistics on how the API is being used for an organization.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#organization-permissions-for-api-insights', - @( - 'read' - ), - 'Fine-grained', - 'Organization' - ), - [GitHubPermissionDefinition]::new( - 'organization_administration', - 'Administration', - 'Manage access to an organization.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#organization-permissions-for-administration', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Organization' - ), - [GitHubPermissionDefinition]::new( - 'organization_user_blocking', - 'Blocking users', - 'View and manage users blocked by the organization.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#organization-permissions-for-blocking-users', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Organization' - ), - [GitHubPermissionDefinition]::new( - 'organization_campaigns', - 'Campaigns', - 'Manage campaigns.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#organization-permissions-for-campaigns', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Organization' - ), - [GitHubPermissionDefinition]::new( - 'organization_custom_org_roles', - 'Custom organization roles', - 'Create, edit, delete and list custom organization roles. View system organization roles.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#organization-permissions-for-custom-organization-roles', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Organization' - ), - [GitHubPermissionDefinition]::new( - 'organization_custom_properties', - 'Custom properties', - 'Read and write repository custom properties values and administer definitions at the organization level.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#organization-permissions-for-custom-properties', - @( - 'read', - 'write', - 'admin' - ), - 'Fine-grained', - 'Organization' - ), - [GitHubPermissionDefinition]::new( - 'organization_custom_roles', - 'Custom repository roles', - 'Create, edit, delete and list custom repository roles.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#organization-permissions-for-custom-repository-roles', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Organization' - ), - [GitHubPermissionDefinition]::new( - 'organization_events', - 'Events', - 'View events triggered by an activity in an organization.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#organization-permissions-for-events', - @( - 'read' - ), - 'Fine-grained', - 'Organization' - ), - [GitHubPermissionDefinition]::new( - 'organization_copilot_seat_management', - 'GitHub Copilot Business', - 'Manage Copilot Business seats and settings', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#organization-permissions-for-github-copilot-business', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Organization' - ), - [GitHubPermissionDefinition]::new( - 'issue_fields', - 'Issue Fields', - 'Manage issue fields for an organization.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#organization-permissions-for-issue-fields', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Organization' - ), - [GitHubPermissionDefinition]::new( - 'issue_types', - 'Issue Types', - 'Manage issue types for an organization.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#organization-permissions-for-issue-types', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Organization' - ), - [GitHubPermissionDefinition]::new( - 'organization_knowledge_bases', - 'Knowledge bases', - 'View and manage knowledge bases for an organization.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#organization-permissions-for-knowledge-bases', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Organization' - ), - [GitHubPermissionDefinition]::new( - 'members', - 'Members', - 'Organization members and teams.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#organization-permissions-for-members', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Organization' - ), - [GitHubPermissionDefinition]::new( - 'organization_models', - 'Models', - 'Manage model access for an organization.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#organization-permissions-for-models', - @( - 'read' - ), - 'Fine-grained', - 'Organization' - ), - [GitHubPermissionDefinition]::new( - 'organization_network_configurations', - 'Network configurations', - 'View and manage hosted compute network configurations available to an organization.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#organization-permissions-for-network-configurations', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Organization' - ), - [GitHubPermissionDefinition]::new( - 'organization_announcement_banners', - 'Organization announcement banners', - 'View and modify announcement banners for an organization.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#organization-permissions-for-organization-announcement-banners', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Organization' - ), - [GitHubPermissionDefinition]::new( - 'organization_secret_scanning_bypass_requests', - 'Organization bypass requests for secret scanning', - 'Review and manage secret scanning push protection bypass requests.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#organization-permissions-for-organization-bypass-requests-for-secret-scanning', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Organization' - ), - [GitHubPermissionDefinition]::new( - 'organization_codespaces', - 'Organization codespaces', - 'Manage Codespaces for an organization.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#organization-permissions-for-organization-codespaces', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Organization' - ), - [GitHubPermissionDefinition]::new( - 'organization_codespaces_secrets', - 'Organization codespaces secrets', - 'Manage Codespaces Secrets for an organization.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#organization-permissions-for-organization-codespaces-secrets', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Organization' - ), - [GitHubPermissionDefinition]::new( - 'organization_codespaces_settings', - 'Organization codespaces settings', - 'Manage Codespaces settings for an organization.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#organization-permissions-for-organization-codespaces-settings', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Organization' - ), - [GitHubPermissionDefinition]::new( - 'organization_dependabot_secrets', - 'Organization dependabot secrets', - 'Manage Dependabot organization secrets.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#organization-permissions-for-organization-dependabot-secrets', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Organization' - ), - [GitHubPermissionDefinition]::new( - 'organization_code_scanning_dismissal_requests', - 'Organization dismissal requests for code scanning', - 'Review and manage code scanning alert dismissal requests.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#organization-permissions-for-organization-dismissal-requests-for-code-scanning', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Organization' - ), - [GitHubPermissionDefinition]::new( - 'organization_private_registries', - 'Organization private registries', - 'Manage private registries for an organization.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#organization-permissions-for-organization-private-registries', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Organization' - ), - [GitHubPermissionDefinition]::new( - 'organization_personal_access_token_requests', - 'Personal access token requests', - 'Manage personal access token requests from organization members.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#organization-permissions-for-personal-access-token-requests', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Organization' - ), - [GitHubPermissionDefinition]::new( - 'organization_personal_access_tokens', - 'Personal access tokens', - 'View and revoke personal access tokens that have been granted access to an organization.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#organization-permissions-for-personal-access-tokens', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Organization' - ), - [GitHubPermissionDefinition]::new( - 'organization_plan', - 'Plan', - "View an organization's plan.", - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#organization-permissions-for-plan', - @( - 'read' - ), - 'Fine-grained', - 'Organization' - ), - [GitHubPermissionDefinition]::new( - 'organization_projects', - 'Projects', - 'Manage projects for an organization.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#organization-permissions-for-projects', - @( - 'read', - 'write', - 'admin' - ), - 'Fine-grained', - 'Organization' - ), - [GitHubPermissionDefinition]::new( - 'secret_scanning_dismissal_requests', - 'Secret scanning alert dismissal requests', - 'Review and manage secret scanning alert dismissal requests', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#organization-permissions-for-secret-scanning-alert-dismissal-requests', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Organization' - ), - [GitHubPermissionDefinition]::new( - 'organization_secrets', - 'Secrets', - 'Manage Actions organization secrets.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#organization-permissions-for-secrets', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Organization' - ), - [GitHubPermissionDefinition]::new( - 'organization_self_hosted_runners', - 'Self-hosted runners', - 'View and manage Actions self-hosted runners available to an organization.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#organization-permissions-for-self-hosted-runners', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Organization' - ), - [GitHubPermissionDefinition]::new( - 'team_discussions', - 'Team discussions', - 'Manage team discussions and related comments.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#organization-permissions-for-team-discussions', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Organization' - ), - [GitHubPermissionDefinition]::new( - 'organization_actions_variables', - 'Variables', - 'Manage Actions organization variables.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#organization-permissions-for-variables', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Organization' - ), - [GitHubPermissionDefinition]::new( - 'organization_hooks', - 'Webhooks', - 'Manage the post-receive hooks for an organization.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#organization-permissions-for-webhooks', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Organization' - ), - - # ------------------------------ - # User (Account) Fine-Grained Permission Definitions - # ------------------------------ - [GitHubPermissionDefinition]::new( - 'blocking', - 'Block another user', - 'View and manage users blocked by the user.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#user-permissions-for-block-another-user', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'User' - ), - [GitHubPermissionDefinition]::new( - 'codespaces_user_secrets', - 'Codespaces user secrets', - 'Manage Codespaces user secrets.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#user-permissions-for-codespaces-user-secrets', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'User' - ), - [GitHubPermissionDefinition]::new( - 'copilot_messages', - 'Copilot Chat', - 'This application will receive your GitHub ID, your GitHub Copilot Chat session messages ' + - '(not including messages sent to another application), and timestamps of provided GitHub Copilot ' + - 'Chat session messages. This permission must be enabled for Copilot Extensions.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#user-permissions-for-copilot-chat', - @( - 'read' - ), - 'Fine-grained', - 'User' - ), - [GitHubPermissionDefinition]::new( - 'copilot_editor_context', - 'Copilot Editor Context', - 'This application will receive bits of Editor Context (e.g. currently opened file) whenever you send it a message through Copilot Chat.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#user-permissions-for-copilot-editor-context', - @( - 'read' - ), - 'Fine-grained', - 'User' - ), - [GitHubPermissionDefinition]::new( - 'emails', - 'Email addresses', - "Manage a user's email addresses.", - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#user-permissions-for-email-addresses', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'User' - ), - [GitHubPermissionDefinition]::new( - 'user_events', - 'Events', - "View events triggered by a user's activity.", - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#user-permissions-for-events', - @( - 'read' - ), - 'Fine-grained', - 'User' - ), - [GitHubPermissionDefinition]::new( - 'followers', - 'Followers', - "A user's followers", - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#user-permissions-for-followers', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'User' - ), - [GitHubPermissionDefinition]::new( - 'gpg_keys', - 'GPG keys', - "View and manage a user's GPG keys.", - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#user-permissions-for-gpg-keys', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'User' - ), - [GitHubPermissionDefinition]::new( - 'gists', - 'Gists', - "Create and modify a user's gists and comments.", - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#user-permissions-for-gists', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'User' - ), - [GitHubPermissionDefinition]::new( - 'keys', - 'Git SSH keys', - 'Git SSH keys', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#user-permissions-for-git-ssh-keys', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'User' - ), - [GitHubPermissionDefinition]::new( - 'interaction_limits', - 'Interaction limits', - 'Interaction limits on repositories', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#user-permissions-for-interaction-limits', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'User' - ), - [GitHubPermissionDefinition]::new( - 'knowledge_bases', - 'Knowledge bases', - 'View knowledge bases for a user.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#user-permissions-for-knowledge-bases', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'User' - ), - [GitHubPermissionDefinition]::new( - 'user_models', - 'Models', - 'Allows access to GitHub Models.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#user-permissions-for-models', - @( - 'read' - ), - 'Fine-grained', - 'User' - ), - [GitHubPermissionDefinition]::new( - 'plan', - 'Plan', - "View a user's plan.", - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#user-permissions-for-plan', - @( - 'read' - ), - 'Fine-grained', - 'User' - ), - [GitHubPermissionDefinition]::new( - 'profile', - 'Profile', - "Manage a user's profile settings.", - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#user-permissions-for-profile', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'User' - ), - [GitHubPermissionDefinition]::new( - 'git_signing_ssh_public_keys', - 'SSH signing keys', - "View and manage a user's SSH signing keys.", - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#user-permissions-for-ssh-signing-keys', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'User' - ), - [GitHubPermissionDefinition]::new( - 'starring', - 'Starring', - 'List and manage repositories a user is starring.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#user-permissions-for-starring', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'User' - ), - [GitHubPermissionDefinition]::new( - 'watching', - 'Watching', - 'List and change repositories a user is subscribed to.', - 'https://docs.github.com/rest/overview/permissions-required-for-github-apps' + - '#user-permissions-for-watching', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'User' - ), - - # ------------------------------ - # Enterprise Fine-Grained Permission Definitions - # ------------------------------ - [GitHubPermissionDefinition]::new( - 'enterprise_custom_properties', - 'Custom properties', - 'View repository custom properties and administer definitions at the enterprise level.', - 'https://docs.github.com/enterprise-cloud@latest/rest/overview/permissions-required-for-github-apps' + - '#enterprise-permissions-for-custom-properties', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Enterprise' - ), - [GitHubPermissionDefinition]::new( - 'enterprise_organization_installation_repositories', - 'Enterprise organization installation repositories', - 'Manage repository access of GitHub Apps on Enterprise-owned organizations', - 'https://docs.github.com/enterprise-cloud@latest/rest/overview/permissions-required-for-github-apps' + - '#enterprise-permissions-for-enterprise-organization-installation-repositories', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Enterprise' - ), - [GitHubPermissionDefinition]::new( - 'enterprise_organization_installations', - 'Enterprise organization installations', - 'Manage installation of GitHub Apps on Enterprise-owned organizations', - 'https://docs.github.com/enterprise-cloud@latest/rest/overview/permissions-required-for-github-apps' + - '#enterprise-permissions-for-enterprise-organization-installations', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Enterprise' - ), - [GitHubPermissionDefinition]::new( - 'enterprise_organizations', - 'Enterprise organizations', - 'Create and remove enterprise organizations', - 'https://docs.github.com/enterprise-cloud@latest/rest/overview/permissions-required-for-github-apps' + - '#enterprise-permissions-for-enterprise-organizations', - @( - 'write' - ), - 'Fine-grained', - 'Enterprise' - ), - [GitHubPermissionDefinition]::new( - 'enterprise_people', - 'Enterprise people', - 'Manage user access to the enterprise', - 'https://docs.github.com/enterprise-cloud@latest/rest/overview/permissions-required-for-github-apps' + - '#enterprise-permissions-for-enterprise-people', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Enterprise' - ), - [GitHubPermissionDefinition]::new( - 'enterprise_sso', - 'Enterprise single sign-on', - 'View and manage enterprise single sign-on configuration', - 'https://docs.github.com/enterprise-cloud@latest/rest/overview/permissions-required-for-github-apps' + - '#enterprise-permissions-for-enterprise-single-sign-on', - @( - 'read', - 'write' - ), - 'Fine-grained', - 'Enterprise' - ) - ) } diff --git a/tests/Apps.Tests.ps1 b/tests/Apps.Tests.ps1 index 44803729b..fc36ba570 100644 --- a/tests/Apps.Tests.ps1 +++ b/tests/Apps.Tests.ps1 @@ -24,9 +24,13 @@ Describe 'Apps' { Context 'As using on ' -ForEach $authCases { BeforeAll { + $permissionsDefinitions = [GitHubPermissionDefinition]::List LogGroup 'Context' { $context = Connect-GitHubAccount @connectParams -PassThru -Silent - Write-Host ($context | Format-List | Out-String) + Write-Host "$($context | Format-List | Out-String)" + } + LogGroup 'Permissions' { + Write-Host "$($context.Permissions | Format-Table | Out-String)" } } @@ -35,198 +39,210 @@ Describe 'Apps' { Write-Host ('-' * 60) } - # Tests for APP goes here - if ($AuthType -eq 'APP') { - Context 'GitHub Apps' { - It 'Get-GitHubApp - Can get app details' { - $app = Get-GitHubApp - LogGroup 'App' { - Write-Host ($app | Format-List | Out-String) - } - $app | Should -Not -BeNullOrEmpty - $app | Should -BeOfType 'GitHubApp' - $app.ID | Should -Not -BeNullOrEmpty - $app.ClientID | Should -Not -BeNullOrEmpty - $app.Slug | Should -Not -BeNullOrEmpty - $app.NodeID | Should -Not -BeNullOrEmpty - $app.Owner | Should -BeOfType 'GitHubOwner' - $app.Name | Should -Not -BeNullOrEmpty - $app.Description | Should -Not -BeNullOrEmpty - $app.ExternalUrl | Should -Not -BeNullOrEmpty - $app.Url | Should -Not -BeNullOrEmpty - $app.CreatedAt | Should -Not -BeNullOrEmpty - $app.UpdatedAt | Should -Not -BeNullOrEmpty - $app.Permissions | Should -BeOfType 'PSCustomObject' - $app.Events | Should -BeOfType 'string' - $app.Installations | Should -Not -BeNullOrEmpty - } + It 'Get-GitHubApp - Get an app by slug' -Skip:($AuthType -eq 'APP') { + $app = Get-GitHubApp -Slug 'github-actions' + LogGroup 'App by slug' { + Write-Host ($app | Format-List | Out-String) + } + $app | Should -Not -BeNullOrEmpty + } - It 'Get-GitHubAppInstallationRequest - Can get installation requests' { - $installationRequests = Get-GitHubAppInstallationRequest - LogGroup 'Installation requests' { - Write-Host ($installationRequests | Format-List | Out-String) - } + Context 'GitHub Apps' -Skip:($AuthType -ne 'APP') { + It 'Get-GitHubApp - Can get app details' { + $app = Get-GitHubApp + LogGroup 'App' { + Write-Host ($app | Format-List | Out-String) } + $app | Should -Not -BeNullOrEmpty + $app | Should -BeOfType 'GitHubApp' + $app.ID | Should -Not -BeNullOrEmpty + $app.ClientID | Should -Not -BeNullOrEmpty + $app.Slug | Should -Not -BeNullOrEmpty + $app.NodeID | Should -Not -BeNullOrEmpty + $app.Owner | Should -BeOfType 'GitHubOwner' + $app.Name | Should -Not -BeNullOrEmpty + $app.Description | Should -Not -BeNullOrEmpty + $app.ExternalUrl | Should -Not -BeNullOrEmpty + $app.Url | Should -Not -BeNullOrEmpty + $app.CreatedAt | Should -Not -BeNullOrEmpty + $app.UpdatedAt | Should -Not -BeNullOrEmpty + $app.Permissions.Count | Should -BeGreaterThan 0 + $app.Permissions | Should -BeOfType 'GitHubPermission' + $app.Permissions.Name | Should -BeIn $permissionsDefinitions.Name + $app.Events | Should -BeOfType 'string' + $app.Installations | Should -Not -BeNullOrEmpty + } - It 'Get-GitHubAppInstallation - Can get app installations' { - $githubApp = Get-GitHubApp - $installations = Get-GitHubAppInstallation - LogGroup 'Installations' { - Write-Host ($installations | Format-List | Out-String) - } - $installations | Should -Not -BeNullOrEmpty - foreach ($installation in $installations) { - $installation | Should -BeOfType 'GitHubAppInstallation' - $installation.ID | Should -Not -BeNullOrEmpty - $installation.App | Should -BeOfType 'GitHubApp' - $installation.App.ClientID | Should -Be $githubApp.ClientID - $installation.App.AppID | Should -Not -BeNullOrEmpty - $installation.App.Slug | Should -Not -BeNullOrEmpty - $installation.Target | Should -BeOfType 'GitHubOwner' - $installation.Target | Should -Not -BeNullOrEmpty - $installation.Type | Should -BeIn @('Enterprise', 'Organization', 'User') - $installation.RepositorySelection | Should -Not -BeNullOrEmpty - $installation.Permissions | Should -BeOfType 'PSCustomObject' - $installation.Events | Should -BeOfType 'string' - $installation.CreatedAt | Should -Not -BeNullOrEmpty - $installation.UpdatedAt | Should -Not -BeNullOrEmpty - $installation.SuspendedAt | Should -BeNullOrEmpty - $installation.SuspendedBy | Should -BeOfType 'GitHubUser' - $installation.SuspendedBy | Should -BeNullOrEmpty - } + It 'Get-GitHubAppInstallationRequest - Can get installation requests' { + $installationRequests = Get-GitHubAppInstallationRequest + LogGroup 'Installation requests' { + Write-Host ($installationRequests | Format-List | Out-String) } + } - It 'Get-GitHubAppInstallation - ' { - $githubApp = Get-GitHubApp - $installation = Get-GitHubAppInstallation | Where-Object { ($_.Target.Name -eq $owner) -and ($_.Type -eq $ownerType) } - LogGroup "Installation - $ownerType" { - Write-Host ($installation | Format-List | Out-String) + It 'Get-GitHubAppInstallation - Can get app installations' { + $githubApp = Get-GitHubApp + $installations = Get-GitHubAppInstallation + $installations | Should -Not -BeNullOrEmpty + foreach ($installation in $installations) { + LogGroup "Installation - $($installation.Target.Name)" { + Write-Host "$($installation | Format-List | Out-String)" } - $installation | Should -Not -BeNullOrEmpty $installation | Should -BeOfType 'GitHubAppInstallation' $installation.ID | Should -Not -BeNullOrEmpty $installation.App | Should -BeOfType 'GitHubApp' $installation.App.ClientID | Should -Be $githubApp.ClientID - $installation.App.AppID | Should -Not -BeNullOrEmpty $installation.App.Slug | Should -Not -BeNullOrEmpty $installation.Target | Should -BeOfType 'GitHubOwner' - $installation.Target | Should -Be $owner - $installation.Type | Should -Be $ownerType + $installation.Target | Should -Not -BeNullOrEmpty + $installation.Type | Should -BeIn @('Enterprise', 'Organization', 'User') $installation.RepositorySelection | Should -Not -BeNullOrEmpty - $installation.Permissions | Should -BeOfType 'PSCustomObject' + $installation.Permissions.Count | Should -BeGreaterThan 0 + $installation.Permissions | Should -BeOfType [GitHubPermission] + $installation.Permissions.Name | Should -BeIn $permissionsDefinitions.Name $installation.Events | Should -BeOfType 'string' $installation.CreatedAt | Should -Not -BeNullOrEmpty $installation.UpdatedAt | Should -Not -BeNullOrEmpty $installation.SuspendedAt | Should -BeNullOrEmpty $installation.SuspendedBy | Should -BeOfType 'GitHubUser' $installation.SuspendedBy | Should -BeNullOrEmpty + $installation.Status | Should -Not -BeNullOrEmpty + $installation.Status | Should -BeIn @('Ok', 'Outdated') } } - Context 'Webhooks' { - It 'Get-GitHubAppWebhookConfiguration - Can get the webhook configuration' { - $webhookConfig = Get-GitHubAppWebhookConfiguration - LogGroup 'Webhook config' { - Write-Host ($webhookConfig | Format-Table | Out-String) - } - $webhookConfig | Should -Not -BeNullOrEmpty + It 'Get-GitHubAppInstallation - ' { + $githubApp = Get-GitHubApp + $installation = Get-GitHubAppInstallation | Where-Object { ($_.Target.Name -eq $owner) -and ($_.Type -eq $ownerType) } + LogGroup "Installation - $ownerType" { + Write-Host ($installation | Format-List | Out-String) } + $installation | Should -Not -BeNullOrEmpty + $installation | Should -BeOfType 'GitHubAppInstallation' + $installation.ID | Should -Not -BeNullOrEmpty + $installation.App | Should -BeOfType 'GitHubApp' + $installation.App.ClientID | Should -Be $githubApp.ClientID + $installation.App.Slug | Should -Not -BeNullOrEmpty + $installation.Target | Should -BeOfType 'GitHubOwner' + $installation.Target | Should -Be $owner + $installation.Type | Should -Be $ownerType + $installation.RepositorySelection | Should -Not -BeNullOrEmpty + $installation.Permissions.Count | Should -BeGreaterThan 0 + $installation.Permissions | Should -BeOfType [GitHubPermission] + $installation.Permissions.Name | Should -BeIn $permissionsDefinitions.Name + $installation.Events | Should -BeOfType 'string' + $installation.CreatedAt | Should -Not -BeNullOrEmpty + $installation.UpdatedAt | Should -Not -BeNullOrEmpty + $installation.SuspendedAt | Should -BeNullOrEmpty + $installation.SuspendedBy | Should -BeOfType 'GitHubUser' + $installation.SuspendedBy | Should -BeNullOrEmpty + $installation.Status | Should -Not -BeNullOrEmpty + $installation.Status | Should -BeIn @('Ok', 'Outdated') + } + } - It 'Update-GitHubAppWebhookConfiguration - Can update the webhook configuration' { - { Update-GitHubAppWebhookConfiguration -ContentType 'form' } | Should -Not -Throw - $webhookConfig = Get-GitHubAppWebhookConfiguration - LogGroup 'Webhook config - form' { - Write-Host ($webhookConfig | Format-Table | Out-String) - } - { Update-GitHubAppWebhookConfiguration -ContentType 'json' } | Should -Not -Throw - $webhookConfig = Get-GitHubAppWebhookConfiguration - LogGroup 'Webhook config - json' { - Write-Host ($webhookConfig | Format-Table | Out-String) - } + Context 'Webhooks' -Skip:($AuthType -ne 'APP') { + It 'Get-GitHubAppWebhookConfiguration - Can get the webhook configuration' { + $webhookConfig = Get-GitHubAppWebhookConfiguration + LogGroup 'Webhook config' { + Write-Host ($webhookConfig | Format-Table | Out-String) } + $webhookConfig | Should -Not -BeNullOrEmpty + } - It 'Get-GitHubAppWebhookDelivery - Can get webhook deliveries' { - $deliveries = Get-GitHubAppWebhookDelivery - LogGroup 'Deliveries' { - Write-Host ($deliveries | Format-Table | Out-String) - } - $deliveries | Should -Not -BeNullOrEmpty + It 'Update-GitHubAppWebhookConfiguration - Can update the webhook configuration' { + { Update-GitHubAppWebhookConfiguration -ContentType 'form' } | Should -Not -Throw + $webhookConfig = Get-GitHubAppWebhookConfiguration + LogGroup 'Webhook config - form' { + Write-Host ($webhookConfig | Format-Table | Out-String) } - - It 'Get-GitHubAppWebhookDelivery - Can redeliver a webhook delivery' { - $deliveries = Get-GitHubAppWebhookDelivery | Select-Object -First 1 - LogGroup 'Delivery - redeliver' { - Write-Host ($deliveries | Format-Table | Out-String) - } - { Invoke-GitHubAppWebhookReDelivery -ID $deliveries.id } | Should -Not -Throw - LogGroup 'Delivery - redeliver' { - Write-Host ($deliveries | Format-Table | Out-String) - } + { Update-GitHubAppWebhookConfiguration -ContentType 'json' } | Should -Not -Throw + $webhookConfig = Get-GitHubAppWebhookConfiguration + LogGroup 'Webhook config - json' { + Write-Host ($webhookConfig | Format-Table | Out-String) } } - Context 'Installation' { - BeforeAll { - $githubApp = Get-GitHubApp - $config = Get-GitHubConfig - $context = Connect-GitHubApp @connectAppParams -PassThru -Silent - LogGroup 'Context' { - Write-Host ($context | Format-List | Out-String) - } + It 'Get-GitHubAppWebhookDelivery - Can get webhook deliveries' { + $deliveries = Get-GitHubAppWebhookDelivery + LogGroup 'Deliveries' { + Write-Host ($deliveries | Format-Table | Out-String) } + $deliveries | Should -Not -BeNullOrEmpty + } - It 'Connect-GitHubApp - Connects as a GitHub App to ' { - $context | Should -BeOfType 'GitHubAppInstallationContext' - $context.ClientID | Should -Be $githubApp.ClientID - $context.TokenExpiresAt | Should -BeOfType [datetime] - $context.InstallationID | Should -BeOfType [uint64] - $context.InstallationID | Should -BeGreaterThan 0 - $context.Permissions | Should -BeOfType [PSCustomObject] - $context.Events | Should -BeOfType 'string' - $context.InstallationType | Should -Be $ownertype - $context.InstallationName | Should -Be $owner - $context.ID | Should -Be "$($config.HostName)/$($githubApp.Slug)/$ownertype/$owner" - $context.Name | Should -Be "$($config.HostName)/$($githubApp.Slug)/$ownertype/$owner" - $context.DisplayName | Should -Be $githubApp.Name - $context.Type | Should -Be 'Installation' - $context.HostName | Should -Be $config.HostName - $context.ApiBaseUri | Should -Be $config.ApiBaseUri - $context.ApiVersion | Should -Be $config.ApiVersion - $context.AuthType | Should -Be 'IAT' - $context.NodeID | Should -Not -BeNullOrEmpty - $context.DatabaseID | Should -Not -BeNullOrEmpty - $context.UserName | Should -Be $githubApp.Slug - $context.Token | Should -BeOfType [System.Security.SecureString] - $context.TokenType | Should -Be 'ghs' - $context.HttpVersion | Should -Be $config.HttpVersion - $context.PerPage | Should -Be $config.PerPage + It 'Get-GitHubAppWebhookDelivery - Can redeliver a webhook delivery' { + $deliveries = Get-GitHubAppWebhookDelivery | Select-Object -First 1 + LogGroup 'Delivery - redeliver' { + Write-Host ($deliveries | Format-Table | Out-String) } + { Invoke-GitHubAppWebhookReDelivery -ID $deliveries.id } | Should -Not -Throw + LogGroup 'Delivery - redeliver' { + Write-Host ($deliveries | Format-Table | Out-String) + } + } + } - It 'Connect-GitHubApp - TokenExpiresIn property should be calculated correctly' { - $context.TokenExpiresIn | Should -BeOfType [TimeSpan] - $context.TokenExpiresIn.TotalMinutes | Should -BeGreaterThan 0 - $context.TokenExpiresIn.TotalMinutes | Should -BeLessOrEqual 60 + Context 'Installation' -Skip:($AuthType -ne 'APP') { + BeforeAll { + $githubApp = Get-GitHubApp + $config = Get-GitHubConfig + $permissionsDefinitions = [GitHubPermissionDefinition]::List + $context = Connect-GitHubApp @connectAppParams -PassThru -Silent + LogGroup 'Context' { + Write-Host "$($context | Format-List | Out-String)" } + LogGroup 'Permissions' { + Write-Host "$($context.Permissions | Format-Table | Out-String)" + } + } - It 'Revoked GitHub App token should fail on API call' -Skip:($TokenType -eq 'GITHUB_TOKEN') { - $org = Get-GitHubOrganization -Name PSModule -Context $context - $org | Should -Not -BeNullOrEmpty - $context | Disconnect-GitHub + It 'Connect-GitHubApp - Connects as a GitHub App to ' { + $context | Should -BeOfType 'GitHubAppInstallationContext' + $context.ClientID | Should -Be $githubApp.ClientID + $context.TokenExpiresAt | Should -BeOfType [datetime] + $context.InstallationID | Should -BeOfType [uint64] + $context.InstallationID | Should -BeGreaterThan 0 + $context.Events | Should -BeOfType 'string' + $context.InstallationType | Should -Be $ownertype + $context.InstallationName | Should -Be $owner + $context.ID | Should -Be "$($config.HostName)/$($githubApp.Slug)/$ownertype/$owner" + $context.Name | Should -Be "$($config.HostName)/$($githubApp.Slug)/$ownertype/$owner" + $context.DisplayName | Should -Be $githubApp.Name + $context.Type | Should -Be 'Installation' + $context.HostName | Should -Be $config.HostName + $context.ApiBaseUri | Should -Be $config.ApiBaseUri + $context.ApiVersion | Should -Be $config.ApiVersion + $context.AuthType | Should -Be 'IAT' + $context.NodeID | Should -Not -BeNullOrEmpty + $context.DatabaseID | Should -Not -BeNullOrEmpty + $context.UserName | Should -Be $githubApp.Slug + $context.Token | Should -BeOfType [System.Security.SecureString] + $context.TokenType | Should -Be 'ghs' + $context.HttpVersion | Should -Be $config.HttpVersion + $context.PerPage | Should -Be $config.PerPage + $context.Permissions.Count | Should -BeGreaterThan 0 + $context.Permissions | Should -BeOfType [GitHubPermission] + $context.Permissions.Name | Should -BeIn $permissionsDefinitions.Name + $context.Events | Should -BeOfType 'string' - { - Invoke-RestMethod -Method Get -Uri "$($context.ApiBaseUri)/orgs/PSModule" -Authentication Bearer -Token $context.token - } | Should -Throw - } } - } - # Tests for IAT UAT and PAT goes here - It 'Get-GitHubApp - Get an app by slug' -Skip:($AuthType -eq 'APP') { - $app = Get-GitHubApp -Slug 'github-actions' - LogGroup 'App by slug' { - Write-Host ($app | Format-List | Out-String) + It 'Connect-GitHubApp - TokenExpiresIn property should be calculated correctly' { + $context.TokenExpiresIn | Should -BeOfType [TimeSpan] + $context.TokenExpiresIn.TotalMinutes | Should -BeGreaterThan 0 + $context.TokenExpiresIn.TotalMinutes | Should -BeLessOrEqual 60 + } + + It 'Revoked GitHub App token should fail on API call' -Skip:($TokenType -eq 'GITHUB_TOKEN') { + $org = Get-GitHubOrganization -Name PSModule -Context $context + $org | Should -Not -BeNullOrEmpty + $context | Disconnect-GitHub + + { + Invoke-RestMethod -Method Get -Uri "$($context.ApiBaseUri)/orgs/PSModule" -Authentication Bearer -Token $context.token + } | Should -Throw } - $app | Should -Not -BeNullOrEmpty } } } diff --git a/tests/GitHub.Tests.ps1 b/tests/GitHub.Tests.ps1 index 53b9e347e..9a7c69a17 100644 --- a/tests/GitHub.Tests.ps1 +++ b/tests/GitHub.Tests.ps1 @@ -124,6 +124,7 @@ Describe 'Auth' { It 'Connect-GitHubAccount - Connects to GitHub CLI on runners' { [string]::IsNullOrEmpty($(gh auth token)) | Should -Be $false } + It 'Get-GitHubViewer - Gets the logged in context' { $viewer = Get-GitHubViewer LogGroup 'Viewer' { diff --git a/tests/Permissions.Tests.ps1 b/tests/Permissions.Tests.ps1 index 043358796..99662a3d3 100644 --- a/tests/Permissions.Tests.ps1 +++ b/tests/Permissions.Tests.ps1 @@ -37,53 +37,30 @@ Describe 'Permissions' { Context 'For Apps' -Skip:($AuthType -ne 'APP') { BeforeAll { + $permissionsDefinitions = [GitHubPermissionDefinition]::List $installationContext = Connect-GitHubApp @connectAppParams -PassThru -Default -Silent LogGroup 'Context - Installation' { - Write-Host ($installationContext | Format-List | Out-String) + Write-Host "$($installationContext | Format-List | Out-String)" } - } - - It 'App context should have Permissions property populated' -Skip:($AuthType -ne 'APP') { - $installationContext.Permissions | Should -Not -BeNullOrEmpty - $installationContext.Permissions | Should -BeOfType [pscustomobject] - } - - It 'All app installation permissions should exist in permission catalog and be valid options' -Skip:($AuthType -ne 'APP') { - $catalog = Get-GitHubPermissionDefinition - $catalogNames = $catalog.Name - - # Flatten context permission hashtable/object into name/value pairs (value is access level like read/write/admin) - $granted = @() - $installationContext.Permissions.PSObject.Properties | ForEach-Object { - if ($_.Name -eq 'metadata') { return } # metadata is mandatory; still in catalog but just proceed normally - $granted += [pscustomobject]@{ Name = $_.Name; Level = [string]$_.Value } - } - - # Unknown permissions (present in context but not in catalog) - $unknown = $granted | Where-Object { $_.Name -notin $catalogNames } - if ($unknown) { - throw "Unknown permission(s) detected in app installation: $($unknown.Name -join ', ')" + LogGroup 'Permissions' { + Write-Host "$($installationContext.Permissions | Format-Table | Out-String)" } + } - # For each granted permission ensure level is one of the catalog options - foreach ($g in $granted) { - $def = $catalog | Where-Object Name -EQ $g.Name - $def | Should -Not -BeNullOrEmpty - $def.Options | Should -Contain $g.Level - } + It 'App context should have Permissions property populated' { + $installationContext.Permissions.Count | Should -BeGreaterThan 0 + $installationContext.Permissions | Should -BeOfType [GitHubPermission] + $installationContext.Permissions.Name | Should -BeIn $permissionsDefinitions.Name } - It 'Permission catalog should contain all permissions granted to the app installation' -Skip:($AuthType -ne 'APP') { - $catalog = Get-GitHubPermissionDefinition + It 'Permission catalog should contain all permissions granted to the app installation' { $missing = @() - $installationContext.Permissions.PSObject.Properties | ForEach-Object { - if ($_.Name -notin $catalog.Name) { + $installationContext.Permissions | ForEach-Object { + if ($_.Name -notin $permissionsDefinitions.Name) { $missing += $_.Name } } - if ($missing.Count -gt 0) { - throw "Missing permission definitions for: $($missing -join ', ')" - } + $missing.Count | Should -Be 0 -Because "The following permissions are missing from the catalog: $($missing -join ', ')" } } } @@ -93,7 +70,7 @@ Describe 'Permissions' { $result = Get-GitHubPermissionDefinition $result | Should -Not -BeNullOrEmpty $result | Should -BeOfType [GitHubPermissionDefinition] - ($result | Measure-Object).Count | Should -BeGreaterThan 0 + $result.Count | Should -BeGreaterThan 0 } It 'Should return only Fine-grained permissions when filtered by Type' { From 002492b7f2a644c9b16f792c0fdf7da0e38a8818 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Tue, 16 Sep 2025 11:38:56 +0200 Subject: [PATCH 2/3] =?UTF-8?q?=F0=9F=A9=B9=20[Patch]:=20Prevent=20Argumen?= =?UTF-8?q?tCompleters=20from=20falling=20back=20to=20the=20default=20file?= =?UTF-8?q?=20path=20completion=20(#515)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR addresses an issue where PowerShell argument completers would fall back to file path completion when no valid argument matches were found, leading to irrelevant file and folder suggestions. ## Problem When using argument completion with GitHub PowerShell module commands, if no matches were found for the typed input, PowerShell would default to showing file and folder completions from the current directory. This created a confusing user experience where typing something like: ```powershell Get-GitHubLicense -Name invalidlicense ``` Would show local files and folders instead of no completions, suggesting invalid options to the user. ## Solution Updated all 34 argument completers across 14 files to explicitly return `$null` when no matches are found. The fix follows this pattern: **Before:** ```powershell Get-GitHubLicense @params | Where-Object { $_.Name -like $pattern } | ForEach-Object { [System.Management.Automation.CompletionResult]::new($_.Name, $_.Name, 'ParameterValue', $_.Name) } ``` **After:** ```powershell $filteredOptions = Get-GitHubLicense @params | Where-Object { $_.Name -like $pattern } if (-not $filteredOptions) { return $null } $filteredOptions | ForEach-Object { [System.Management.Automation.CompletionResult]::new($_.Name, $_.Name, 'ParameterValue', $_.Name) } ``` ## Result - ✅ When valid matches exist: Shows appropriate completions as before - ✅ When no matches exist: No completions shown (cursor stays in place) - ❌ Previously: Would show irrelevant file/folder completions when no matches found This provides a cleaner, more intuitive completion experience that doesn't suggest invalid options to users. ## Files Modified All completer files throughout the module structure: - Core completers in `/src/completers.ps1` - Function-specific completers in `/src/functions/public/*/completers.ps1` Fixes #514. --- 💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more [Copilot coding agent tips](https://gh.io/copilot-coding-agent-tips) in the docs. --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: MariusStorhaug <17722253+MariusStorhaug@users.noreply.github.com> --- src/completers.ps1 | 18 ++++++++-- .../public/Auth/Context/completers.ps1 | 6 +++- src/functions/public/Config/completers.ps1 | 36 +++++++++++++++---- .../public/Environments/completers.ps1 | 12 +++++-- src/functions/public/Gitignore/completers.ps1 | 6 +++- src/functions/public/License/completers.ps1 | 6 +++- .../public/Organization/completers.ps1 | 18 ++++++++-- .../public/Permission/completers.ps1 | 24 ++++++++++--- .../Repositories/Permissions/completers.ps1 | 6 +++- .../public/Repositories/completers.ps1 | 36 +++++++++++++++---- src/functions/public/Secrets/completers.ps1 | 6 +++- src/functions/public/Teams/completers.ps1 | 12 +++++-- src/functions/public/Variables/completers.ps1 | 6 +++- src/functions/public/Workflows/completers.ps1 | 12 +++++-- 14 files changed, 170 insertions(+), 34 deletions(-) diff --git a/src/completers.ps1 b/src/completers.ps1 index 901d1de8e..9cc61195a 100644 --- a/src/completers.ps1 +++ b/src/completers.ps1 @@ -7,7 +7,11 @@ Verbose = $false Debug = $false } - Get-GitHubAppInstallation @params | Where-Object { $_.Type -eq 'User' -and $_.Target.Name -like $pattern } | ForEach-Object { + $filteredOptions = Get-GitHubAppInstallation @params | Where-Object { $_.Type -eq 'User' -and $_.Target.Name -like $pattern } + if (-not $filteredOptions) { + return $null + } + $filteredOptions | ForEach-Object { [System.Management.Automation.CompletionResult]::new($_.Target.Name, $_.Target.Name, 'ParameterValue', $_.Target.Name) } } @@ -20,7 +24,11 @@ Register-ArgumentCompleter -CommandName Connect-GitHubApp -ParameterName Organiz Verbose = $false Debug = $false } - Get-GitHubAppInstallation @params | Where-Object { $_.Type -eq 'Organization' -and $_.Target.Name -like $pattern } | ForEach-Object { + $filteredOptions = Get-GitHubAppInstallation @params | Where-Object { $_.Type -eq 'Organization' -and $_.Target.Name -like $pattern } + if (-not $filteredOptions) { + return $null + } + $filteredOptions | ForEach-Object { [System.Management.Automation.CompletionResult]::new($_.Target.Name, $_.Target.Name, 'ParameterValue', $_.Target.Name) } } @@ -33,7 +41,11 @@ Register-ArgumentCompleter -CommandName Connect-GitHubApp -ParameterName Enterpr Verbose = $false Debug = $false } - Get-GitHubAppInstallation @params | Where-Object { $_.Type -eq 'Enterprise' -and $_.Target.Name -like $pattern } | ForEach-Object { + $filteredOptions = Get-GitHubAppInstallation @params | Where-Object { $_.Type -eq 'Enterprise' -and $_.Target.Name -like $pattern } + if (-not $filteredOptions) { + return $null + } + $filteredOptions | ForEach-Object { [System.Management.Automation.CompletionResult]::new($_.Target.Name, $_.Target.Name, 'ParameterValue', $_.Target.Name) } } diff --git a/src/functions/public/Auth/Context/completers.ps1 b/src/functions/public/Auth/Context/completers.ps1 index 9f4c2752d..c2eb1dee3 100644 --- a/src/functions/public/Auth/Context/completers.ps1 +++ b/src/functions/public/Auth/Context/completers.ps1 @@ -14,7 +14,11 @@ $contexts += Get-GitHubContext -ListAvailable -Verbose:$false -Debug:$false $contexts = $contexts | Sort-Object -Property Name - $contexts | Where-Object { $_.Name -like $pattern } | ForEach-Object { + $filteredOptions = $contexts | Where-Object { $_.Name -like $pattern } + if (-not $filteredOptions) { + return $null + } + $filteredOptions | ForEach-Object { [System.Management.Automation.CompletionResult]::new($_.Name, $_.Name, 'ParameterValue', $_.Name) } } diff --git a/src/functions/public/Config/completers.ps1 b/src/functions/public/Config/completers.ps1 index c498f0497..6127bf086 100644 --- a/src/functions/public/Config/completers.ps1 +++ b/src/functions/public/Config/completers.ps1 @@ -2,7 +2,11 @@ param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters) $null = $commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters $pattern = switch (Get-GitHubConfig -Name CompletionMode) { 'Contains' { "*$wordToComplete*" } default { "$wordToComplete*" } } - ([GitHubConfig]).GetProperties().Name | Where-Object { $_ -like $pattern } | ForEach-Object { + $filteredOptions = ([GitHubConfig]).GetProperties().Name | Where-Object { $_ -like $pattern } + if (-not $filteredOptions) { + return $null + } + $filteredOptions | ForEach-Object { [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_ ) } } @@ -13,17 +17,29 @@ Register-ArgumentCompleter -CommandName Set-GitHubConfig -ParameterName Value -S $pattern = switch (Get-GitHubConfig -Name CompletionMode) { 'Contains' { "*$wordToComplete*" } default { "$wordToComplete*" } } switch ($fakeBoundParameters.Name) { 'CompletionMode' { - @('StartsWith', 'Contains') | Where-Object { $_ -like $pattern } | ForEach-Object { + $filteredOptions = @('StartsWith', 'Contains') | Where-Object { $_ -like $pattern } + if (-not $filteredOptions) { + return $null + } + $filteredOptions | ForEach-Object { [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_) } } 'HttpVersion' { - @('1.0', '1.1', '2.0', '3.0') | Where-Object { $_ -like $pattern } | ForEach-Object { + $filteredOptions = @('1.0', '1.1', '2.0', '3.0') | Where-Object { $_ -like $pattern } + if (-not $filteredOptions) { + return $null + } + $filteredOptions | ForEach-Object { [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_) } } 'EnvironmentType' { - @('Local', 'GitHubActions', 'FunctionApp', 'Unknown') | Where-Object { $_ -like $pattern } | ForEach-Object { + $filteredOptions = @('Local', 'GitHubActions', 'FunctionApp', 'Unknown') | Where-Object { $_ -like $pattern } + if (-not $filteredOptions) { + return $null + } + $filteredOptions | ForEach-Object { [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_) } } @@ -33,12 +49,20 @@ Register-ArgumentCompleter -CommandName Set-GitHubConfig -ParameterName Value -S Debug = $false Verbose = $false } - Get-GitHubApiVersion @params | Where-Object { $_ -like $pattern } | ForEach-Object { + $filteredOptions = Get-GitHubApiVersion @params | Where-Object { $_ -like $pattern } + if (-not $filteredOptions) { + return $null + } + $filteredOptions | ForEach-Object { [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_) } } 'DefaultContext' { - Get-GitHubContext -ListAvailable | Where-Object { $_.Name -like $pattern } | ForEach-Object { + $filteredOptions = Get-GitHubContext -ListAvailable | Where-Object { $_.Name -like $pattern } + if (-not $filteredOptions) { + return $null + } + $filteredOptions | ForEach-Object { [System.Management.Automation.CompletionResult]::new($_.Name, $_.Name, 'ParameterValue', $_.Name) } } diff --git a/src/functions/public/Environments/completers.ps1 b/src/functions/public/Environments/completers.ps1 index e2f9422f4..4e121eacb 100644 --- a/src/functions/public/Environments/completers.ps1 +++ b/src/functions/public/Environments/completers.ps1 @@ -11,7 +11,11 @@ Debug = $false } $params | Remove-HashtableEntry -NullOrEmptyValues - Get-GitHubEnvironment @params | Where-Object { $_.Name -like $pattern } | ForEach-Object { + $filteredOptions = Get-GitHubEnvironment @params | Where-Object { $_.Name -like $pattern } + if (-not $filteredOptions) { + return $null + } + $filteredOptions | ForEach-Object { [System.Management.Automation.CompletionResult]::new($_.Name, $_.Name, 'ParameterValue', $_.Name) } } @@ -28,7 +32,11 @@ Register-ArgumentCompleter -CommandName ($script:PSModuleInfo.FunctionsToExport) Debug = $false } $params | Remove-HashtableEntry -NullOrEmptyValues - Get-GitHubEnvironment @params | Where-Object { $_.Name -like $pattern } | ForEach-Object { + $filteredOptions = Get-GitHubEnvironment @params | Where-Object { $_.Name -like $pattern } + if (-not $filteredOptions) { + return $null + } + $filteredOptions | ForEach-Object { [System.Management.Automation.CompletionResult]::new($_.Name, $_.Name, 'ParameterValue', $_.Name) } } diff --git a/src/functions/public/Gitignore/completers.ps1 b/src/functions/public/Gitignore/completers.ps1 index 86d7df84e..e6a60acdd 100644 --- a/src/functions/public/Gitignore/completers.ps1 +++ b/src/functions/public/Gitignore/completers.ps1 @@ -7,7 +7,11 @@ Verbose = $false Debug = $false } - Get-GitHubGitignore @params | Where-Object { $_ -like $pattern } | ForEach-Object { + $filteredOptions = Get-GitHubGitignore @params | Where-Object { $_ -like $pattern } + if (-not $filteredOptions) { + return $null + } + $filteredOptions | ForEach-Object { [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_) } } diff --git a/src/functions/public/License/completers.ps1 b/src/functions/public/License/completers.ps1 index a4c5a4f10..677059f67 100644 --- a/src/functions/public/License/completers.ps1 +++ b/src/functions/public/License/completers.ps1 @@ -7,7 +7,11 @@ Verbose = $false Debug = $false } - Get-GitHubLicense @params | Where-Object { $_.Name -like $pattern } | ForEach-Object { + $filteredOptions = Get-GitHubLicense @params | Where-Object { $_.Name -like $pattern } + if (-not $filteredOptions) { + return $null + } + $filteredOptions | ForEach-Object { [System.Management.Automation.CompletionResult]::new($_.Name, $_.Name, 'ParameterValue', $_.Name) } } diff --git a/src/functions/public/Organization/completers.ps1 b/src/functions/public/Organization/completers.ps1 index 2994f0240..59cf3b2c1 100644 --- a/src/functions/public/Organization/completers.ps1 +++ b/src/functions/public/Organization/completers.ps1 @@ -8,7 +8,11 @@ Verbose = $false Debug = $false } - Get-GitHubOrganization @params | Where-Object { $_.Name -like $pattern } | ForEach-Object { + $filteredOptions = Get-GitHubOrganization @params | Where-Object { $_.Name -like $pattern } + if (-not $filteredOptions) { + return $null + } + $filteredOptions | ForEach-Object { [System.Management.Automation.CompletionResult]::new($_.Name, $_.Name, 'ParameterValue', $_.Name) } } @@ -22,7 +26,11 @@ Register-ArgumentCompleter -CommandName ($script:PSModuleInfo.FunctionsToExport) Verbose = $false Debug = $false } - Get-GitHubOrganization @params | Where-Object { $_.Name -like $pattern } | ForEach-Object { + $filteredOptions = Get-GitHubOrganization @params | Where-Object { $_.Name -like $pattern } + if (-not $filteredOptions) { + return $null + } + $filteredOptions | ForEach-Object { [System.Management.Automation.CompletionResult]::new($_.Name, $_.Name, 'ParameterValue', $_.Name) } } @@ -36,7 +44,11 @@ Register-ArgumentCompleter -CommandName ($script:PSModuleInfo.FunctionsToExport) Verbose = $false Debug = $false } - Get-GitHubOrganization @params | Where-Object { $_.Name -like $pattern } | ForEach-Object { + $filteredOptions = Get-GitHubOrganization @params | Where-Object { $_.Name -like $pattern } + if (-not $filteredOptions) { + return $null + } + $filteredOptions | ForEach-Object { [System.Management.Automation.CompletionResult]::new($_.Name, $_.Name, 'ParameterValue', $_.Name) } } diff --git a/src/functions/public/Permission/completers.ps1 b/src/functions/public/Permission/completers.ps1 index dc0be7453..d49392053 100644 --- a/src/functions/public/Permission/completers.ps1 +++ b/src/functions/public/Permission/completers.ps1 @@ -2,7 +2,11 @@ param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters) $null = $commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters $pattern = switch (Get-GitHubConfig -Name CompletionMode) { 'Contains' { "*$wordToComplete*" } default { "$wordToComplete*" } } - [GitHubPermissionDefinition]::List.Name | Sort-Object -Unique | Where-Object { $_ -like $pattern } | ForEach-Object { + $filteredOptions = [GitHubPermissionDefinition]::List.Name | Sort-Object -Unique | Where-Object { $_ -like $pattern } + if (-not $filteredOptions) { + return $null + } + $filteredOptions | ForEach-Object { [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_) } } @@ -11,7 +15,11 @@ Register-ArgumentCompleter -CommandName Get-GitHubPermissionDefinition -Paramete param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters) $null = $commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters $pattern = switch (Get-GitHubConfig -Name CompletionMode) { 'Contains' { "*$wordToComplete*" } default { "$wordToComplete*" } } - [GitHubPermissionDefinition]::List.DisplayName | Sort-Object -Unique | Where-Object { $_ -like $pattern } | ForEach-Object { + $filteredOptions = [GitHubPermissionDefinition]::List.DisplayName | Sort-Object -Unique | Where-Object { $_ -like $pattern } + if (-not $filteredOptions) { + return $null + } + $filteredOptions | ForEach-Object { [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_) } } @@ -20,7 +28,11 @@ Register-ArgumentCompleter -CommandName Get-GitHubPermissionDefinition -Paramete param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters) $null = $commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters $pattern = switch (Get-GitHubConfig -Name CompletionMode) { 'Contains' { "*$wordToComplete*" } default { "$wordToComplete*" } } - [GitHubPermissionDefinition]::List.Type | Sort-Object -Unique | Where-Object { $_ -like $pattern } | ForEach-Object { + $filteredOptions = [GitHubPermissionDefinition]::List.Type | Sort-Object -Unique | Where-Object { $_ -like $pattern } + if (-not $filteredOptions) { + return $null + } + $filteredOptions | ForEach-Object { [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_) } } @@ -29,7 +41,11 @@ Register-ArgumentCompleter -CommandName Get-GitHubPermissionDefinition -Paramete param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters) $null = $commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters $pattern = switch (Get-GitHubConfig -Name CompletionMode) { 'Contains' { "*$wordToComplete*" } default { "$wordToComplete*" } } - [GitHubPermissionDefinition]::List.Scope | Sort-Object -Unique | Where-Object { $_ -like $pattern } | ForEach-Object { + $filteredOptions = [GitHubPermissionDefinition]::List.Scope | Sort-Object -Unique | Where-Object { $_ -like $pattern } + if (-not $filteredOptions) { + return $null + } + $filteredOptions | ForEach-Object { [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_) } } diff --git a/src/functions/public/Repositories/Permissions/completers.ps1 b/src/functions/public/Repositories/Permissions/completers.ps1 index cdb1e0c2e..e266724bf 100644 --- a/src/functions/public/Repositories/Permissions/completers.ps1 +++ b/src/functions/public/Repositories/Permissions/completers.ps1 @@ -2,7 +2,11 @@ param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters) $null = $commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters $pattern = switch (Get-GitHubConfig -Name CompletionMode) { 'Contains' { "*$wordToComplete*" } default { "$wordToComplete*" } } - @('None', 'Pull', 'Triage', 'Push', 'Maintain', 'Admin', 'Read', 'Write') | Where-Object { $_ -like $pattern } | ForEach-Object { + $filteredOptions = @('None', 'Pull', 'Triage', 'Push', 'Maintain', 'Admin', 'Read', 'Write') | Where-Object { $_ -like $pattern } + if (-not $filteredOptions) { + return $null + } + $filteredOptions | ForEach-Object { [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_) } } diff --git a/src/functions/public/Repositories/completers.ps1 b/src/functions/public/Repositories/completers.ps1 index 43ac6a7c7..124d98ae2 100644 --- a/src/functions/public/Repositories/completers.ps1 +++ b/src/functions/public/Repositories/completers.ps1 @@ -7,7 +7,11 @@ Verbose = $false Debug = $false } - Get-GitHubGitignore @params | Where-Object { $_ -like $pattern } | ForEach-Object { + $filteredOptions = Get-GitHubGitignore @params | Where-Object { $_ -like $pattern } + if (-not $filteredOptions) { + return $null + } + $filteredOptions | ForEach-Object { [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_) } } @@ -21,7 +25,11 @@ Register-ArgumentCompleter -CommandName New-GitHubRepository -ParameterName Lice Verbose = $false Debug = $false } - Get-GitHubLicense @params | Where-Object { $_.Name -like $pattern } | ForEach-Object { + $filteredOptions = Get-GitHubLicense @params | Where-Object { $_.Name -like $pattern } + if (-not $filteredOptions) { + return $null + } + $filteredOptions | ForEach-Object { [System.Management.Automation.CompletionResult]::new($_.Name, $_.Name, 'ParameterValue', $_.Name) } } @@ -30,7 +38,11 @@ Register-ArgumentCompleter -CommandName Get-GitHubRepository -ParameterName Addi param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters) $null = $commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters $pattern = switch (Get-GitHubConfig -Name CompletionMode) { 'Contains' { "*$wordToComplete*" } default { "$wordToComplete*" } } - [GitHubRepository].GetProperties().Name | Where-Object { $_ -like $pattern } | ForEach-Object { + $filteredOptions = [GitHubRepository].GetProperties().Name | Where-Object { $_ -like $pattern } + if (-not $filteredOptions) { + return $null + } + $filteredOptions | ForEach-Object { [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_) } } @@ -39,7 +51,11 @@ Register-ArgumentCompleter -CommandName Get-GitHubRepository -ParameterName Prop param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters) $null = $commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters $pattern = switch (Get-GitHubConfig -Name CompletionMode) { 'Contains' { "*$wordToComplete*" } default { "$wordToComplete*" } } - [GitHubRepository].GetProperties().Name | Where-Object { $_ -like $pattern } | ForEach-Object { + $filteredOptions = [GitHubRepository].GetProperties().Name | Where-Object { $_ -like $pattern } + if (-not $filteredOptions) { + return $null + } + $filteredOptions | ForEach-Object { [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_) } } @@ -54,7 +70,11 @@ Register-ArgumentCompleter -CommandName ($script:PSModuleInfo.FunctionsToExport) Verbose = $false Debug = $false } - Get-GitHubRepository @params | Where-Object { $_.Name -like $pattern } | ForEach-Object { + $filteredOptions = Get-GitHubRepository @params | Where-Object { $_.Name -like $pattern } + if (-not $filteredOptions) { + return $null + } + $filteredOptions | ForEach-Object { [System.Management.Automation.CompletionResult]::new($_.Name, $_.Name, 'ParameterValue', $_.Name) } } @@ -70,7 +90,11 @@ Register-ArgumentCompleter -CommandName ($script:PSModuleInfo.FunctionsToExport Verbose = $false Debug = $false } - Get-GitHubRepository @params | Where-Object { $_.Name -like $pattern } | ForEach-Object { + $filteredOptions = Get-GitHubRepository @params | Where-Object { $_.Name -like $pattern } + if (-not $filteredOptions) { + return $null + } + $filteredOptions | ForEach-Object { [System.Management.Automation.CompletionResult]::new($_.Name, $_.Name, 'ParameterValue', $_.Name) } } diff --git a/src/functions/public/Secrets/completers.ps1 b/src/functions/public/Secrets/completers.ps1 index 411ed8ace..ec923b24e 100644 --- a/src/functions/public/Secrets/completers.ps1 +++ b/src/functions/public/Secrets/completers.ps1 @@ -12,7 +12,11 @@ Debug = $false } $params | Remove-HashtableEntry -NullOrEmptyValues - Get-GitHubSecret @params | Where-Object { $_.Name -like $pattern } | ForEach-Object { + $filteredOptions = Get-GitHubSecret @params | Where-Object { $_.Name -like $pattern } + if (-not $filteredOptions) { + return $null + } + $filteredOptions | ForEach-Object { [System.Management.Automation.CompletionResult]::new($_.Name, $_.Name, 'ParameterValue', $_.Name) } } diff --git a/src/functions/public/Teams/completers.ps1 b/src/functions/public/Teams/completers.ps1 index 64d643909..d0c65e2a8 100644 --- a/src/functions/public/Teams/completers.ps1 +++ b/src/functions/public/Teams/completers.ps1 @@ -9,7 +9,11 @@ Verbose = $false Debug = $false } - Get-GitHubTeam @params | Where-Object { $_.Slug -like $pattern } | ForEach-Object { + $filteredOptions = Get-GitHubTeam @params | Where-Object { $_.Slug -like $pattern } + if (-not $filteredOptions) { + return $null + } + $filteredOptions | ForEach-Object { [System.Management.Automation.CompletionResult]::new($_.Slug, $_.Slug, 'ParameterValue', $_.Slug) } } @@ -24,7 +28,11 @@ Register-ArgumentCompleter -CommandName ($script:PSModuleInfo.FunctionsToExport) Verbose = $false Debug = $false } - Get-GitHubTeam @params | Where-Object { $_.Slug -like $pattern } | ForEach-Object { + $filteredOptions = Get-GitHubTeam @params | Where-Object { $_.Slug -like $pattern } + if (-not $filteredOptions) { + return $null + } + $filteredOptions | ForEach-Object { [System.Management.Automation.CompletionResult]::new($_.Slug, $_.Slug, 'ParameterValue', $_.Slug) } } diff --git a/src/functions/public/Variables/completers.ps1 b/src/functions/public/Variables/completers.ps1 index c897efa47..856e09ef4 100644 --- a/src/functions/public/Variables/completers.ps1 +++ b/src/functions/public/Variables/completers.ps1 @@ -12,7 +12,11 @@ Debug = $false } $params | Remove-HashtableEntry -NullOrEmptyValues - Get-GitHubVariable @params | Where-Object { $_.Name -like $pattern } | ForEach-Object { + $filteredOptions = Get-GitHubVariable @params | Where-Object { $_.Name -like $pattern } + if (-not $filteredOptions) { + return $null + } + $filteredOptions | ForEach-Object { [System.Management.Automation.CompletionResult]::new($_.Name, $_.Name, 'ParameterValue', $_.Name) } } diff --git a/src/functions/public/Workflows/completers.ps1 b/src/functions/public/Workflows/completers.ps1 index 29a8fb0e4..ca7c10323 100644 --- a/src/functions/public/Workflows/completers.ps1 +++ b/src/functions/public/Workflows/completers.ps1 @@ -11,7 +11,11 @@ Debug = $false } $params | Remove-HashtableEntry -NullOrEmptyValues - Get-GitHubWorkflow @params | Where-Object { $_.Name -like $pattern } | ForEach-Object { + $filteredOptions = Get-GitHubWorkflow @params | Where-Object { $_.Name -like $pattern } + if (-not $filteredOptions) { + return $null + } + $filteredOptions | ForEach-Object { [System.Management.Automation.CompletionResult]::new($_.Name, $_.Name, 'ParameterValue', $_.Name) } } @@ -28,7 +32,11 @@ Register-ArgumentCompleter -CommandName ($script:PSModuleInfo.FunctionsToExport Debug = $false } $params | Remove-HashtableEntry -NullOrEmptyValues - Get-GitHubWorkflow @params | Where-Object { $_.ID -like $pattern } | ForEach-Object { + $filteredOptions = Get-GitHubWorkflow @params | Where-Object { $_.ID -like $pattern } + if (-not $filteredOptions) { + return $null + } + $filteredOptions | ForEach-Object { [System.Management.Automation.CompletionResult]::new($_.ID, "$($_.Name) ($($_.ID))", 'ParameterValue', "$($_.Name) ($($_.ID))" ) } } From e66d934795d2c4fdbb20340834ae27c4c60f38a9 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Wed, 17 Sep 2025 09:23:32 +0200 Subject: [PATCH 3/3] =?UTF-8?q?=F0=9F=AA=B2=20[Fix]:=20Fixing=20the=20`Sec?= =?UTF-8?q?rets`=20test=20for=20`PublicKey`=20for=20`codespaces`=20(#518)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Description This pull request updates the `Secrets.Tests.ps1` test suite to improve how the `Get-GitHubPublicKey` command is tested for different organization plans and makes a minor formatting change to a log message. Test logic improvements for organization plan handling: * Updated the `Get-GitHubPublicKey -Type codespaces` test to conditionally expect a throw for organizations on the 'free' plan, and to run the usual public key retrieval for other plans. This ensures the test behaves correctly based on the organization's plan type. ## Type of change - [ ] 📖 [Docs] - [x] 🪲 [Fix] - [ ] 🩹 [Patch] - [ ] ⚠️ [Security fix] - [ ] 🚀 [Feature] - [ ] 🌟 [Breaking change] ## Checklist - [x] I have performed a self-review of my own code - [x] I have commented my code, particularly in hard-to-understand areas --- tests/Secrets.Tests.ps1 | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/tests/Secrets.Tests.ps1 b/tests/Secrets.Tests.ps1 index 48e06423d..ead8632ca 100644 --- a/tests/Secrets.Tests.ps1 +++ b/tests/Secrets.Tests.ps1 @@ -111,7 +111,7 @@ Describe 'Secrets' { It 'Get-GitHubPublicKey - Codespaces' { $result = Get-GitHubPublicKey -Type codespaces - LogGroup 'PublicKey - Codespaces' { + LogGroup 'PublicKey' { Write-Host "$($result | Select-Object * | Format-Table -AutoSize | Out-String)" } $result | Should -Not -BeNullOrEmpty @@ -140,13 +140,21 @@ Describe 'Secrets' { } It 'Get-GitHubPublicKey - Codespaces' { - $plan = $org.plan.name - Write-Host "Running with plan [$plan]" - $result = Get-GitHubPublicKey @scope -Type codespaces - LogGroup 'PublicKey' { - Write-Host "$($result | Select-Object * | Format-Table -AutoSize | Out-String)" + LogGroup 'Plan' { + Write-Host "$($org.plan | Select-Object * | Out-String)" + } + switch ($org.plan.name) { + 'free' { + { Get-GitHubPublicKey @scope -Type codespaces } | Should -Throw + } + default { + $result = Get-GitHubPublicKey @scope -Type codespaces + LogGroup 'PublicKey - Codespaces' { + Write-Host "$($result | Select-Object * | Format-Table -AutoSize | Out-String)" + } + $result | Should -Not -BeNullOrEmpty + } } - $result | Should -Not -BeNullOrEmpty } } @@ -586,7 +594,7 @@ Describe 'Secrets' { LogGroup 'Get secret and test whitespace handling' { $secretToRemove = Get-GitHubSecret @scope -Name $pipelineTestSecretName Write-Host "$($secretToRemove | Format-List | Out-String)" - Write-Host "Testing that environment secrets with valid Environment property work correctly" + Write-Host 'Testing that environment secrets with valid Environment property work correctly' } LogGroup 'Remove via pipeline' { { $secretToRemove | Remove-GitHubSecret } | Should -Not -Throw