Skip to content

Commit 47a0b35

Browse files
🪲 [Fix]: GitHub App Installations load failure (#200)
## Description This pull request includes several changes to improve the handling of GitHub contexts and enhance the functionality of the `Connect-GitHubAccount` and `Connect-GitHubApp` functions. The most important changes include updating the type of the `Permissions` property, adding parameterless constructors, and modifying the logic for loading GitHub App contexts. Enhancements to GitHub context handling: * [`src/classes/public/Context/GitHubContext/AppGitHubContext.ps1`](diffhunk://#diff-59869473eaaec5f3a6e02c2c8cca88b9cc96f5e9fd840a87ead0d079c2c61372L12-R19): Changed the type of the `Permissions` property from `string[]` to `pscustomobject` and added a parameterless constructor. * [`src/classes/public/Context/GitHubContext/InstallationGitHubContext.ps1`](diffhunk://#diff-c9fc34da1bb954dfdae3f8d44c516bf8605d70c07941cd7b2c708ddd739292c0L13-R13): Changed the type of the `Permissions` property from `string[]` to `pscustomobject` and added a parameterless constructor. [[1]](diffhunk://#diff-c9fc34da1bb954dfdae3f8d44c516bf8605d70c07941cd7b2c708ddd739292c0L13-R13) [[2]](diffhunk://#diff-c9fc34da1bb954dfdae3f8d44c516bf8605d70c07941cd7b2c708ddd739292c0R24-R26) * [`src/classes/public/Context/GitHubContext/UserGitHubContext.ps1`](diffhunk://#diff-362543d00b76fa5b7f197c241363be74af60997ed87ea9209907944a89e5f069R25-R27): Added a parameterless constructor. Improvements to `Connect-GitHubAccount` function: * [`src/functions/public/Auth/Connect-GitHubAccount.ps1`](diffhunk://#diff-12918e90451cdedb78571b9a67ac0313331a25175cebb606b7108b7bf06af092L106-R110): Renamed parameters (`SkipAppAutoload` to `AutoloadInstallations`), removed unused parameters (`Shallow`), and updated the logic for loading GitHub App contexts. [[1]](diffhunk://#diff-12918e90451cdedb78571b9a67ac0313331a25175cebb606b7108b7bf06af092L106-R110) [[2]](diffhunk://#diff-12918e90451cdedb78571b9a67ac0313331a25175cebb606b7108b7bf06af092L184-L196) [[3]](diffhunk://#diff-12918e90451cdedb78571b9a67ac0313331a25175cebb606b7108b7bf06af092L304-L305) [[4]](diffhunk://#diff-12918e90451cdedb78571b9a67ac0313331a25175cebb606b7108b7bf06af092L325-R306) Enhancements to `Connect-GitHubApp` function: * [`src/functions/public/Auth/Connect-GitHubApp.ps1`](diffhunk://#diff-7d1951ca779d73ee11b11db4ade6f33f8ea5fcf052324a72d2c8e83304eaa045L67-R80): Removed the `Shallow` parameter, added context resolution and validation in the `process` block, and updated the logic for processing installations. [[1]](diffhunk://#diff-7d1951ca779d73ee11b11db4ade6f33f8ea5fcf052324a72d2c8e83304eaa045L67-R80) [[2]](diffhunk://#diff-7d1951ca779d73ee11b11db4ade6f33f8ea5fcf052324a72d2c8e83304eaa045L107-R149) Refinements to `Set-GitHubContext` function: * [`src/functions/public/Auth/Context/Set-GitHubContext.ps1`](diffhunk://#diff-64c988967c6049cce19ca80e8ddaa220f26d32f8ed6f2626a18bda0b1c28ab30R45-R162): Refactored to use a local variable for context manipulation and added detailed logging for context information. Coverage documentation updates: * [`Coverage.md`](diffhunk://#diff-dbfc2288d75ec9b5432d4012b7f852336ed6550ea8964dd6227b64b4a38d7777L8-R20): Updated the number of available and missing functions, and adjusted the coverage percentage. [[1]](diffhunk://#diff-dbfc2288d75ec9b5432d4012b7f852336ed6550ea8964dd6227b64b4a38d7777L8-R20) [[2]](diffhunk://#diff-dbfc2288d75ec9b5432d4012b7f852336ed6550ea8964dd6227b64b4a38d7777L55-L61) ## Type of change <!-- Use the check-boxes [x] on the options that are relevant. --> - [ ] 📖 [Docs] - [ ] 🪲 [Fix] - [x] 🩹 [Patch] - [ ] ⚠️ [Security fix] - [ ] 🚀 [Feature] - [ ] 🌟 [Breaking change] ## Checklist <!-- Use the check-boxes [x] on the options that are relevant. --> - [ ] I have performed a self-review of my own code - [ ] I have commented my code, particularly in hard-to-understand areas --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
1 parent 793923f commit 47a0b35

File tree

8 files changed

+200
-170
lines changed

8 files changed

+200
-170
lines changed

Coverage.md

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,19 @@
55
<table>
66
<tr>
77
<td>Available functions</td>
8-
<td>985</td>
8+
<td>980</td>
99
</tr>
1010
<tr>
1111
<td>Covered functions</td>
1212
<td>155</td>
1313
</tr>
1414
<tr>
1515
<td>Missing functions</td>
16-
<td>830</td>
16+
<td>825</td>
1717
</tr>
1818
<tr>
1919
<td>Coverage</td>
20-
<td>15.74%</td>
20+
<td>15.82%</td>
2121
</tr>
2222
</table>
2323

@@ -52,13 +52,8 @@
5252
| `/codes_of_conduct` | | :x: | | | |
5353
| `/codes_of_conduct/{key}` | | :x: | | | |
5454
| `/emojis` | | :white_check_mark: | | | |
55-
| `/enterprises/{enterprise}/copilot/billing/seats` | | :x: | | | |
56-
| `/enterprises/{enterprise}/copilot/metrics` | | :x: | | | |
57-
| `/enterprises/{enterprise}/copilot/usage` | | :x: | | | |
5855
| `/enterprises/{enterprise}/dependabot/alerts` | | :x: | | | |
5956
| `/enterprises/{enterprise}/secret-scanning/alerts` | | :x: | | | |
60-
| `/enterprises/{enterprise}/team/{team_slug}/copilot/metrics` | | :x: | | | |
61-
| `/enterprises/{enterprise}/team/{team_slug}/copilot/usage` | | :x: | | | |
6257
| `/events` | | :x: | | | |
6358
| `/feeds` | | :x: | | | |
6459
| `/gists` | | :x: | | :x: | |

src/classes/public/Context/GitHubContext/AppGitHubContext.ps1

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,14 @@
99
[string] $OwnerType
1010

1111
# The permissions that the app is requesting on the target
12-
[string[]] $Permissions
12+
[pscustomobject] $Permissions
1313

1414
# The events that the app is subscribing to once installed
1515
[string[]] $Events
1616

17+
# Simple parameterless constructor
18+
AppGitHubContext() {}
19+
1720
# Creates a context object from a hashtable of key-vaule pairs.
1821
AppGitHubContext([hashtable]$Properties) {
1922
foreach ($Property in $Properties.Keys) {

src/classes/public/Context/GitHubContext/InstallationGitHubContext.ps1

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
[int] $InstallationID
1111

1212
# The permissions that the app is requesting on the target
13-
[string[]] $Permissions
13+
[pscustomobject] $Permissions
1414

1515
# The events that the app is subscribing to once installed
1616
[string[]] $Events
@@ -21,6 +21,9 @@
2121
# The target login of the installation.
2222
[string] $TargetName
2323

24+
# Simple parameterless constructor
25+
InstallationGitHubContext() {}
26+
2427
# Creates a context object from a hashtable of key-vaule pairs.
2528
InstallationGitHubContext([hashtable]$Properties) {
2629
foreach ($Property in $Properties.Keys) {

src/classes/public/Context/GitHubContext/UserGitHubContext.ps1

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@
2222
# 2024-01-01-00:00:00
2323
[datetime] $RefreshTokenExpirationDate
2424

25+
# Simple parameterless constructor
26+
UserGitHubContext() {}
27+
2528
# Creates a context object from a hashtable of key-vaule pairs.
2629
UserGitHubContext([hashtable]$Properties) {
2730
foreach ($Property in $Properties.Keys) {

src/functions/public/Auth/Connect-GitHubAccount.ps1

Lines changed: 7 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -103,17 +103,11 @@
103103
)]
104104
[string] $PrivateKey,
105105

106-
# Skip loading GitHub App contexts.
106+
# Automatically load installations for the GitHub App.
107107
[Parameter(
108108
ParameterSetName = 'App'
109109
)]
110-
[switch] $SkipAppAutoload,
111-
112-
# Do not load credentials for the GitHub App Installations, just metadata.
113-
[Parameter(
114-
ParameterSetName = 'App'
115-
)]
116-
[switch] $Shallow,
110+
[switch] $AutoloadInstallations,
117111

118112
# The default enterprise to use in commands.
119113
[Parameter()]
@@ -181,19 +175,6 @@
181175
if (-not $customTokenProvided -and $gitHubTokenPresent) {
182176
$authType = 'Token'
183177
$Token = $gitHubToken
184-
$gitHubEvent = Get-Content -Path $env:GITHUB_EVENT_PATH -Raw | ConvertFrom-Json
185-
'Enterprise: ' + $gitHubEvent.enterprise.slug
186-
'Organization: ' + $gitHubEvent.organization.login
187-
'Repository: ' + $gitHubEvent.repository.name
188-
'Repository Owner: ' + $gitHubEvent.repository.owner.login
189-
'Repository Owner Type: ' + $gitHubEvent.repository.owner.type
190-
'Sender: ' + $gitHubEvent.sender.login
191-
192-
$Enterprise = [string]$gitHubEvent.enterprise.slug
193-
$TargetType = [string]$gitHubEvent.repository.owner.type
194-
$TargetName = [string]$gitHubEvent.repository.owner.login
195-
$Owner = [string]$gitHubEvent.repository.owner.login
196-
$Repo = [string]$gitHubEvent.repository.name
197178
}
198179
}
199180

@@ -299,10 +280,8 @@
299280
'ghs' {
300281
Write-Verbose 'Logging in using an installation access token...'
301282
$context += @{
302-
Token = ConvertTo-SecureString -AsPlainText $Token
303-
TokenType = $tokenType
304-
TargetType = $TargetType
305-
TargetName = $TargetName
283+
Token = ConvertTo-SecureString -AsPlainText $Token
284+
TokenType = $tokenType
306285
}
307286
$context['AuthType'] = 'IAT'
308287
}
@@ -322,9 +301,9 @@
322301
Write-Host "Logged in as $name!"
323302
}
324303

325-
if ($authType -eq 'App' -and -not $SkipAppAutoload) {
326-
Write-Verbose 'Loading GitHub App contexts...'
327-
Connect-GitHubApp -Shallow:$Shallow
304+
if ($authType -eq 'App' -and $AutoloadInstallations) {
305+
Write-Verbose 'Loading GitHub App Installation contexts...'
306+
Connect-GitHubApp
328307
}
329308

330309
} catch {

src/functions/public/Auth/Connect-GitHubApp.ps1

Lines changed: 68 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -64,96 +64,86 @@
6464
# The context to run the command in. Used to get the details for the API call.
6565
# Can be either a string or a GitHubContext object.
6666
[Parameter()]
67-
[object] $Context = (Get-GitHubContext),
68-
69-
# Do not load credentials for the GitHub App Installations, just metadata.
70-
[Parameter()]
71-
[switch] $Shallow
67+
[object] $Context = (Get-GitHubContext)
7268
)
7369

74-
$commandName = $MyInvocation.MyCommand.Name
75-
Write-Verbose "[$commandName] - Start"
70+
begin {
71+
$commandName = $MyInvocation.MyCommand.Name
72+
Write-Verbose "[$commandName] - Start"
73+
}
7674

77-
$Context = $Context | Resolve-GitHubContext
78-
$Context | Assert-GitHubContext -AuthType 'App'
75+
process {
76+
try {
77+
$Context = $Context | Resolve-GitHubContext
78+
$Context | Assert-GitHubContext -AuthType 'App'
7979

80-
try {
81-
$defaultContextData = @{
82-
ApiBaseUri = $Context.ApiBaseUri
83-
ApiVersion = $Context.ApiVersion
84-
HostName = $Context.HostName
85-
ClientID = $Context.ClientID
86-
AuthType = 'IAT'
87-
TokenType = 'ghs'
88-
}
89-
90-
$installations = Get-GitHubAppInstallation
91-
Write-Verbose "Found [$($installations.Count)] installations."
92-
switch ($PSCmdlet.ParameterSetName) {
93-
'User' {
94-
Write-Verbose "Filtering installations for user [$User]."
95-
$installations = $installations | Where-Object { $_.target_type -eq 'User' -and $_.account.login -in $User }
96-
}
97-
'Organization' {
98-
Write-Verbose "Filtering installations for organization [$Organization]."
99-
$installations = $installations | Where-Object { $_.target_type -eq 'Organization' -and $_.account.login -in $Organization }
100-
}
101-
'Enterprise' {
102-
Write-Verbose "Filtering installations for enterprise [$Enterprise]."
103-
$installations = $installations | Where-Object { $_.target_type -eq 'Enterprise' -and $_.account.slug -in $Enterprise }
104-
}
105-
}
106-
107-
Write-Verbose "Found [$($installations.Count)] installations for the target type."
108-
$installations | ForEach-Object {
109-
$installation = $_
110-
$contextParams = @{} + $defaultContextData.Clone()
111-
if ($Shallow) {
112-
$token = [PSCustomObject]@{
113-
Token = [securestring]::new()
114-
ExpiresAt = [datetime]::MinValue
115-
}
116-
} else {
117-
$token = New-GitHubAppInstallationAccessToken -InstallationID $installation.id
118-
}
119-
$contextParams += @{
120-
InstallationID = $installation.id
121-
Token = $token.Token
122-
TokenExpirationDate = $token.ExpiresAt
123-
Permissions = $installation.permissions
124-
Events = $installation.events
125-
TargetType = $installation.target_type
126-
}
127-
switch ($installation.target_type) {
80+
$installations = Get-GitHubAppInstallation -Context $Context
81+
Write-Verbose "Found [$($installations.Count)] installations."
82+
switch ($PSCmdlet.ParameterSetName) {
12883
'User' {
129-
$contextParams += @{
130-
TargetName = $installation.account.login
131-
}
84+
Write-Verbose "Filtering installations for user [$User]."
85+
$installations = $installations | Where-Object { $_.target_type -eq 'User' -and $_.account.login -in $User }
13286
}
13387
'Organization' {
134-
$contextParams += @{
135-
TargetName = $installation.account.login
136-
}
88+
Write-Verbose "Filtering installations for organization [$Organization]."
89+
$installations = $installations | Where-Object { $_.target_type -eq 'Organization' -and $_.account.login -in $Organization }
13790
}
13891
'Enterprise' {
139-
$contextParams += @{
140-
TargetName = $installation.account.slug
141-
}
92+
Write-Verbose "Filtering installations for enterprise [$Enterprise]."
93+
$installations = $installations | Where-Object { $_.target_type -eq 'Enterprise' -and $_.account.slug -in $Enterprise }
14294
}
14395
}
144-
Write-Verbose 'Logging in using an installation access token...'
145-
Write-Verbose ($contextParams | Format-Table | Out-String)
146-
$tmpContext = [InstallationGitHubContext]::new((Set-GitHubContext -Context $contextParams -PassThru))
147-
Write-Verbose ($tmpContext | Format-List | Out-String)
148-
if (-not $Silent) {
149-
$name = $tmpContext.name
150-
Write-Host "Connected $name"
96+
97+
Write-Verbose "Found [$($installations.Count)] installations for the target."
98+
$installations | ForEach-Object {
99+
$installation = $_
100+
Write-Verbose "Processing installation [$($installation.account.login)] [$($installation.id)]"
101+
$token = New-GitHubAppInstallationAccessToken -Context $Context -InstallationID $installation.id
102+
103+
$contextParams = @{
104+
AuthType = [string]'IAT'
105+
TokenType = [string]'ghs'
106+
DisplayName = [string]$Context.DisplayName
107+
ApiBaseUri = [string]$Context.ApiBaseUri
108+
ApiVersion = [string]$Context.ApiVersion
109+
HostName = [string]$Context.HostName
110+
ClientID = [string]$Context.ClientID
111+
InstallationID = [string]$installation.id
112+
Permissions = [pscustomobject]$installation.permissions
113+
Events = [string[]]$installation.events
114+
TargetType = [string]$installation.target_type
115+
Token = [securestring]$token.Token
116+
TokenExpirationDate = [string]$token.ExpiresAt
117+
}
118+
119+
switch ($installation.target_type) {
120+
'User' {
121+
$contextParams['TargetName'] = $installation.account.login
122+
}
123+
'Organization' {
124+
$contextParams['TargetName'] = $installation.account.login
125+
}
126+
'Enterprise' {
127+
$contextParams['TargetName'] = $installation.account.slug
128+
}
129+
}
130+
Write-Verbose 'Logging in using a managed installation access token...'
131+
Write-Verbose ($contextParams | Format-Table | Out-String)
132+
$tmpContext = [InstallationGitHubContext]::new((Set-GitHubContext -Context $contextParams.Clone() -PassThru))
133+
Write-Verbose ($tmpContext | Format-List | Out-String)
134+
if (-not $Silent) {
135+
$name = $tmpContext.name
136+
Write-Host "Connected $name"
137+
}
138+
$contextParams.Clear()
151139
}
140+
} catch {
141+
Write-Error $_
142+
Write-Error (Get-PSCallStack | Format-Table | Out-String)
143+
throw 'Failed to connect to GitHub using a GitHub App.'
152144
}
153-
} catch {
154-
Write-Error $_
155-
Write-Error (Get-PSCallStack | Format-Table | Out-String)
156-
throw 'Failed to connect to GitHub using a GitHub App.'
157145
}
158-
Write-Verbose "[$commandName] - End"
146+
end {
147+
Write-Verbose "[$commandName] - End"
148+
}
159149
}

0 commit comments

Comments
 (0)