diff --git a/Formatters/GitHubCodespaces.Format.ps1xml b/Formatters/GitHubCodespaces.Format.ps1xml index 256de388..95bde172 100644 --- a/Formatters/GitHubCodespaces.Format.ps1xml +++ b/Formatters/GitHubCodespaces.Format.ps1xml @@ -34,5 +34,41 @@ + + + GitHub.CodespaceMachine + + GitHub.CodespaceMachine + + + + + + + name + + + display_name + + + operating_system + + + storage_in_bytes + + + memory_in_bytes + + + cpus + + + prebuild_availability + + + + + + diff --git a/GitHubCodespaces.ps1 b/GitHubCodespaces.ps1 index ff322485..6495bc40 100644 --- a/GitHubCodespaces.ps1 +++ b/GitHubCodespaces.ps1 @@ -3,6 +3,7 @@ @{ GitHubCodespaceTypeName = 'GitHub.Codespace' + GitHubCodespaceMachineTypeName = 'GitHub.CodespaceMachine' }.GetEnumerator() | ForEach-Object { Set-Variable -Scope Script -Option ReadOnly -Name $_.Key -Value $_.Value } @@ -28,7 +29,7 @@ filter Get-GitHubCodespace .PARAMETER Uri Uri for the Codespace. - The OwnerName and CodespaceName will be extracted from here instead of needing to provide + The OwnerName and RepositoryName will be extracted from here instead of needing to provide them individually. .PARAMETER OrganizationName @@ -134,9 +135,8 @@ filter Get-GitHubCodespace [String] $UserName, [Parameter( - Mandatory, ValueFromPipelineByPropertyName, - ParameterSetName = 'CodespaceName')] + ParameterSetName = 'AuthenticatedUser')] [string] $CodespaceName, [string] $AccessToken @@ -154,18 +154,20 @@ filter Get-GitHubCodespace { 'AuthenticatedUser' { - $uriFragment = 'user/codespaces' - $description = 'Getting codespaces for current authenticated user' - - break - } - - 'CodespaceName' - { - $telemetryProperties['CodespaceName'] = Get-PiiSafeString -PlainText $CodespaceName + if ([string]::IsNullOrWhiteSpace($CodespaceName)) + { + # list-codespaces-for-the-authenticated-user + $uriFragment = 'user/codespaces' + $description = 'Getting codespaces for current authenticated user' + } + else + { + # get-a-codespace-for-the-authenticated-user + $telemetryProperties['CodespaceName'] = Get-PiiSafeString -PlainText $CodespaceName - $uriFragment = "user/codespaces/$CodespaceName" - $description = "Getting user/codespaces/$CodespaceName" + $uriFragment = "user/codespaces/$CodespaceName" + $description = "Getting user/codespaces/$CodespaceName" + } break } @@ -221,6 +223,158 @@ filter Get-GitHubCodespace return ($result | Add-GitHubCodespaceAdditionalProperties) } +filter Get-GitHubCodespaceMachine +{ + <# + .SYNOPSIS + Retrieves the machine types available for a given repository or that a codespace can transition to use. + + .DESCRIPTION + Retrieves information about codespace machine types. + + The Git repo for this module can be found here: http://aka.ms/PowerShellForGitHub + + .PARAMETER OwnerName + Owner of the Codespace. + If not supplied here, the DefaultOwnerName configuration property value will be used. + + .PARAMETER RepositoryName + Name of the repository. + If not supplied here, the DefaultRepositoryName configuration property value will be used. + + .PARAMETER Uri + Uri for the Codespace. + The OwnerName and RepositoryName will be extracted from here instead of needing to provide + them individually. + + .PARAMETER CodespaceName + Name of the Codespace. + + .PARAMETER AccessToken + If provided, this will be used as the AccessToken for authentication with the + REST Api. Otherwise, will attempt to use the configured value or will run unauthenticated. + + .INPUTS + GitHub.Codespace + GitHub.Project + GitHub.Repository + + .OUTPUTS + GitHub.CodespaceMachine + + .EXAMPLE + Get-GitHubCodespaceMachine -OwnerName microsoft -RepositoryName PowerShellForGitHub + + Gets all codespace machines for the microsoft/PowerShellForGitHub repository. + + name display_name operating_system storage_in_bytes memory_in_bytes cpus prebuild_availability + ---- ------------ ---------------- ---------------- --------------- ---- --------------------- + basicLinux32gb 2 cores, 8 GB RAM, 32 GB storage linux 34359738368 8589934592 2 + standardLinux32gb 4 cores, 16 GB RAM, 32 GB storage linux 34359738368 17179869184 4 + premiumLinux 8 cores, 32 GB RAM, 64 GB storage linux 68719476736 34359738368 8 + largePremiumLinux 16 cores, 64 GB RAM, 128 GB storage linux 137438953472 68719476736 16 + + .EXAMPLE + Get-GitHubCodespaceMachine -CodespaceName laughing-chainsaw-8v6qq79wvg6f7x7x + + Gets all codespace machines available for use by an existing codespace. + + name display_name operating_system storage_in_bytes memory_in_bytes cpus prebuild_availability + ---- ------------ ---------------- ---------------- --------------- ---- --------------------- + basicLinux32gb 2 cores, 8 GB RAM, 32 GB storage linux 34359738368 8589934592 2 ready + standardLinux32gb 4 cores, 16 GB RAM, 32 GB storage linux 34359738368 17179869184 4 ready + + .LINK + https://docs.github.com/en/rest/codespaces/machines?apiVersion=2022-11-28#list-available-machine-types-for-a-repository + + .LINK + https://docs.github.com/en/rest/codespaces/machines?apiVersion=2022-11-28#list-machine-types-for-a-codespace + #> + [CmdletBinding(DefaultParameterSetName = 'CodespaceName')] + [OutputType({ $Script:GitHubCodespaceMachineTypeName })] + [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', '', Justification = 'The Uri parameter is only referenced by Resolve-RepositoryElements which get access to it from the stack via Get-Variable -Scope 1.')] + param( + [Parameter( + Mandatory, + ValueFromPipelineByPropertyName, + ParameterSetName = 'Elements')] + [string] $OwnerName, + + [Parameter( + Mandatory, + ValueFromPipelineByPropertyName, + ParameterSetName = 'Elements')] + [string] $RepositoryName, + + [Parameter( + Mandatory, + ValueFromPipelineByPropertyName, + ParameterSetName = 'Uri')] + [Alias('RepositoryUrl')] + [string] $Uri, + + [Parameter( + Mandatory, + ValueFromPipelineByPropertyName, + ParameterSetName = 'CodespaceName')] + [string] $CodespaceName, + + [string] $AccessToken + ) + + Write-InvocationLog + + $telemetryProperties = @{ + UsageType = $PSCmdlet.ParameterSetName + } + + $uriFragment = [String]::Empty + $description = [String]::Empty + switch ($PSCmdlet.ParameterSetName) + { + 'CodespaceName' + { + $telemetryProperties['CodespaceName'] = Get-PiiSafeString -PlainText $CodespaceName + + $uriFragment = "user/codespaces/$CodespaceName/machines" + $description = "Getting user/codespaces/$CodespaceName/machines" + + break + } + + { $_ -in ('Elements', 'Uri') } + { + $elements = Resolve-RepositoryElements + $OwnerName = $elements.ownerName + $RepositoryName = $elements.repositoryName + + $telemetryProperties['OwnerName'] = Get-PiiSafeString -PlainText $OwnerName + $telemetryProperties['RepositoryName'] = Get-PiiSafeString -PlainText $RepositoryName + + $uriFragment = "repos/$OwnerName/$RepositoryName/codespaces/machines" + $description = "Getting repos/$OwnerName/$RepositoryName/codespaces/machines" + + break + } + } + + $params = @{ + UriFragment = $uriFragment + Description = $description + AccessToken = $AccessToken + TelemetryEventName = $MyInvocation.MyCommand.Name + TelemetryProperties = $telemetryProperties + } + + $result = Invoke-GHRestMethodMultipleResult @params + if ($null -ne $result.machines) + { + $result = $result.machines + } + + return ($result | Add-GitHubCodespaceAdditionalProperties -TypeName $Script:GitHubCodespaceMachineTypeName) +} + function New-GitHubCodespace { <# @@ -242,7 +396,7 @@ function New-GitHubCodespace .PARAMETER Uri Uri for the Codespace. - The OwnerName and CodespaceName will be extracted from here instead of needing to provide + The OwnerName and RepositoryName will be extracted from here instead of needing to provide them individually. .PARAMETER PullRequest @@ -309,7 +463,7 @@ function New-GitHubCodespace Creates a new codespace for the current authenticated user in the specified repository from a pull request. .EXAMPLE - New-GitHubCodespace -OwnerName marykay -RepositoryName one + New-GitHubCodespace -OwnerName microsoft -RepositoryName PowerShellForGitHub Creates a codespace owned by the authenticated user in the specified repository. @@ -737,6 +891,12 @@ filter Stop-GitHubCodespace The Git repo for this module can be found here: http://aka.ms/PowerShellForGitHub + .PARAMETER OrganizationName + Name of the Organization. + + .PARAMETER UserName + The handle for the GitHub user account(s). + .PARAMETER CodespaceName Name of the Codespace. @@ -744,7 +904,7 @@ filter Stop-GitHubCodespace If present will wait for the codespace to stop. .PARAMETER PassThru - Returns the updated GitHub Issue. By default, this cmdlet does not generate any output. + Returns the stop action result. By default, this cmdlet does not generate any output. You can use "Set-GitHubConfiguration -DefaultPassThru" to control the default behavior of this switch. @@ -769,11 +929,22 @@ filter Stop-GitHubCodespace GitHub Apps must have write access to the codespaces_lifecycle_admin repository permission to use this endpoint. #> [CmdletBinding( + DefaultParameterSetName = 'AuthenticatedUser', SupportsShouldProcess, ConfirmImpact = 'Low')] [OutputType({ $script:GitHubCodespaceTypeName })] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', '', Justification = 'PassThru is accessed indirectly via Resolve-ParameterWithDefaultConfigurationValue')] param( + [Parameter( + Mandatory, + ParameterSetName = 'Organization')] + [string] $OrganizationName, + + [Parameter( + Mandatory, + ParameterSetName = 'Organization')] + [string] $UserName, + [Parameter( Mandatory, ValueFromPipeline, @@ -791,11 +962,25 @@ filter Stop-GitHubCodespace $telemetryProperties = @{ CodespaceName = Get-PiiSafeString -PlainText $CodespaceName + UsageType = $PSCmdlet.ParameterSetName Wait = $Wait.IsPresent } + $uriFragment = [String]::Empty + + if ($PSCmdlet.ParameterSetName -eq 'Organization') + { + $telemetryProperties['OrganizationName'] = Get-PiiSafeString -PlainText $OrganizationName + $telemetryProperties['UserName'] = Get-PiiSafeString -PlainText $UserName + $uriFragment = "orgs/$OrganizationName/members/$UserName/codespaces/$CodespaceName/stop" + } + else + { + $uriFragment = "user/codespaces/$CodespaceName/stop" + } + $params = @{ - UriFragment = "user/codespaces/$CodespaceName/stop" + UriFragment = $uriFragment Method = 'Post' Description = "Stop Codespace $CodespaceName" AccessToken = $AccessToken @@ -898,6 +1083,324 @@ function Wait-GitHubCodespaceAction } } +function Add-GitHubCodespaceUser +{ + <# + .SYNOPSIS + Add user(s) to Codespaces billing for an organization. + + .DESCRIPTION + Add user(s) to Codespaces billing for an organization. + + The Git repo for this module can be found here: http://aka.ms/PowerShellForGitHub + + .PARAMETER OrganizationName + Name of the Organization. + + .PARAMETER UserName + The handle for the GitHub user account(s). + + .PARAMETER AccessToken + If provided, this will be used as the AccessToken for authentication with the + REST Api. Otherwise, will attempt to use the configured value or will run unauthenticated. + + .EXAMPLE + Add-GitHubCodespaceUser -OrganizationName microsoft -UserName octocat + + .INPUTS + GitHub.User + + .OUTPUTS + None + + .NOTES + To use this endpoint, the billing settings for the organization must be set to SelectedMembers, + which can done using Set-GitHubCodespaceVisibility -Visibility SelectedMembers. + + .NOTES + You must authenticate using an access token with the admin:org scope to use this endpoint. + + .LINK + https://docs.github.com/en/rest/codespaces/organizations?apiVersion=2022-11-28#add-users-to-codespaces-billing-for-an-organization + #> + [CmdletBinding(SupportsShouldProcess)] + param ( + [Parameter(Mandatory)] + [string] $OrganizationName, + + [Parameter( + Mandatory, + ValueFromPipeline, + ValueFromPipelineByPropertyName)] + [string[]] $UserName, + + [string] $AccessToken + ) + + begin + { + $userNames = @() + } + + process + { + foreach ($name in $UserName) + { + $userNames += $name + } + } + + end + { + Write-InvocationLog + + $hashBody = @{ + selected_usernames = @($userNames) + } + + $params = @{ + UriFragment = "orgs/$OrganizationName/codespaces/access/selected_users" + Body = ConvertTo-Json -InputObject $hashBody + Method = 'Post' + Description = 'Add users to GitHub Codespace billing' + AccessToken = $AccessToken + TelemetryEventName = $MyInvocation.MyCommand.Name + } + + if (-not $PSCmdlet.ShouldProcess(($userNames -join ','), 'Add Codespace Users')) + { + return + } + + try + { + $null = Invoke-GHRestMethod @params + } + catch + { + if ($_.Exception.Message -like '*(304)*') # Not Modified + { + Write-Log -Message "Codespace selected_users not modified. Requested users already included." -Level Verbose + } + else + { + throw + } + } + } +} + +function Remove-GitHubCodespaceUser +{ + <# + .SYNOPSIS + Remove user(s) from Codespaces billing for an organization. + + .DESCRIPTION + Remove user(s) from Codespaces billing for an organization. + + The Git repo for this module can be found here: http://aka.ms/PowerShellForGitHub + + .PARAMETER OrganizationName + Name of the Organization. + + .PARAMETER UserName + The handle for the GitHub user account(s). + + .PARAMETER AccessToken + If provided, this will be used as the AccessToken for authentication with the + REST Api. Otherwise, will attempt to use the configured value or will run unauthenticated. + + .EXAMPLE + Remove-GitHubCodespaceUser -OrganizationName microsoft -UserName octocat + + .INPUTS + GitHub.User + + .OUTPUTS + None + + .NOTES + To use this endpoint, the billing settings for the organization must be set to SelectedMembers, + which can done using Set-GitHubCodespaceVisibility -Visibility SelectedMembers. + + .NOTES + You must authenticate using an access token with the admin:org scope to use this endpoint. + + .LINK + https://docs.github.com/en/rest/codespaces/organizations?apiVersion=2022-11-28#removes-users-from-codespaces-billing-for-an-organization + #> + [CmdletBinding(SupportsShouldProcess)] + [Alias('Delete-GitHubCodespaceUser')] + param ( + [Parameter(Mandatory)] + [string] $OrganizationName, + + [Parameter( + Mandatory, + ValueFromPipeline, + ValueFromPipelineByPropertyName)] + [string[]] $UserName, + + [string] $AccessToken + ) + + begin + { + $userNames = @() + } + + process + { + foreach ($name in $UserName) + { + $userNames += $name + } + } + + end + { + Write-InvocationLog + + $hashBody = @{ + selected_usernames = $userNames + } + + $params = @{ + UriFragment = "orgs/$OrganizationName/codespaces/access/selected_users" + Body = ConvertTo-Json -InputObject $hashBody + Method = 'Delete' + Description = 'Remove users from GitHub codespace billing' + AccessToken = $AccessToken + TelemetryEventName = $MyInvocation.MyCommand.Name + } + + if (-not $PSCmdlet.ShouldProcess(($userNames -join ','), 'Remove Codespace Users')) + { + return + } + + try + { + $null = Invoke-GHRestMethod @params + } + catch + { + if ($_.Exception.Message -like '*(304)*') # Not Modified + { + Write-Log -Message "Codespace selected_users not modified. Requested users already excluded." -Level Verbose + } + else + { + throw + } + } + } +} + +filter Set-GitHubCodespaceVisibility +{ + <# + .SYNOPSIS + Manage access control for organization codespaces. + + .DESCRIPTION + Manage access control for organization codespaces. + Sets which users can access codespaces in an organization. This is synonymous with granting + or revoking Codespaces access permissions for users according to the visibility. + + The Git repo for this module can be found here: http://aka.ms/PowerShellForGitHub + + .PARAMETER OrganizationName + Name of the Organization. + + .PARAMETER Visibility + Which users can access codespaces in the organization. + Disabled means that no users can access codespaces in the organization. + + .PARAMETER UserName + The usernames of the organization member(s) who should have access + to Codespaces in the organization. Required when visibility is SelectedMembers. + The provided list of usernames will replace any existing value. + + .PARAMETER AccessToken + If provided, this will be used as the AccessToken for authentication with the + REST Api. Otherwise, will attempt to use the configured value or will run unauthenticated. + + .EXAMPLE + Set-GitHubCodespaceVisibility -Visibility SelectedMembers -User octocat -Force + + .INPUTS + GitHub.User + + .NOTES + You must authenticate using an access token with the admin:org scope to use this endpoint. + + .LINK + https://docs.github.com/en/rest/codespaces/organizations?apiVersion=2022-11-28#manage-access-control-for-organization-codespaces + #> + [CmdletBinding( + SupportsShouldProcess, + ConfirmImpact = 'High')] + param ( + [Parameter(Mandatory)] + [string] $OrganizationName, + + [ValidateSet('Disabled', 'SelectedMembers', 'AllMembers', 'AllMembersAndOutsideCollaborators')] + [string] $Visibility, + + [Parameter( + ValueFromPipeline, + ValueFromPipelineByPropertyName)] + [string[]] $UserName, + + [switch] $Force, + + [string] $AccessToken + ) + + Write-InvocationLog + + if (($UserName.Count -gt 0) -and ($Visibility -ne 'SelectedMembers')) + { + $message = 'You can only specify the UserName parameter when the Visibility is set to ''SelectedMembers''' + Write-Log -Message $message -Level Error + throw $message + } + + $visibilityMap = @{ + Disabled = 'disabled' + SelectedMembers = 'selected_members' + AllMembers = 'all_members' + AllMembersAndOutsideCollaborators = 'all_members_and_outside_collaborators' + } + $hashBody = @{ visibility = $visibilityMap[$Visibility] } + + if ($Visibility -eq 'SelectedMembers') + { + $hashBody.Add('selected_usernames', @($UserName)) + } + + $params = @{ + UriFragment = "orgs/$OrganizationName/codespaces/access" + Body = ConvertTo-Json -InputObject $hashBody + Method = 'Put' + Description = 'Set Codespace Visiblity' + AccessToken = $AccessToken + TelemetryEventName = $MyInvocation.MyCommand.Name + } + + if ($Force -and (-not $Confirm)) + { + $ConfirmPreference = 'None' + } + + if ($PSCmdLet.ShouldProcess($Visibility, 'Set Codespace Visibility')) + { + $null = Invoke-GHRestMethod @params + } +} + filter Add-GitHubCodespaceAdditionalProperties { <# @@ -936,7 +1439,7 @@ filter Add-GitHubCodespaceAdditionalProperties if (-not (Get-GitHubConfiguration -Name DisablePipelineSupport)) { - if ($item.name) + if ($item.name -and ($TypeName -eq $script:GitHubCodespaceTypeName)) { Add-Member -InputObject $item -Name 'CodespaceUrl' -Value "user/codespaces/$($item.name)" -MemberType NoteProperty -Force Add-Member -InputObject $item -Name 'CodespaceName' -Value $item.name -MemberType NoteProperty -Force diff --git a/PowerShellForGitHub.psd1 b/PowerShellForGitHub.psd1 index f53a71ff..1ed55b2b 100644 --- a/PowerShellForGitHub.psd1 +++ b/PowerShellForGitHub.psd1 @@ -69,6 +69,7 @@ # Functions to export from this module FunctionsToExport = @( 'Add-GitHubAssignee', + 'Add-GitHubCodespaceUser', 'Add-GitHubIssueLabel', 'Add-GitHubGistStar', 'Backup-GitHubConfiguration', @@ -83,6 +84,7 @@ 'Get-GitHubCloneTraffic', 'Get-GitHubCodeOfConduct', 'Get-GitHubCodespace', + 'Get-GitHubCodespaceMachine' 'Get-GitHubConfiguration', 'Get-GitHubContent', 'Get-GitHubDeploymentEnvironment', @@ -160,6 +162,7 @@ 'New-GitHubTeam', 'Remove-GitHubAssignee', 'Remove-GitHubCodespace', + 'Remove-GitHubCodespaceUser', 'Remove-GitHubComment', 'Remove-GitHubDeploymentEnvironment' 'Remove-GitHubGist', @@ -188,6 +191,7 @@ 'Reset-GitHubConfiguration', 'Restore-GitHubConfiguration', 'Set-GitHubAuthentication', + 'Set-GitHubCodespaceVisibility', 'Set-GitHubConfiguration', 'Set-GitHubContent', 'Set-GitHubGist', @@ -226,6 +230,7 @@ 'Add-GitHubGistFile', 'Delete-GitHubAsset', 'Delete-GitHubBranch', + 'Delete-GitHubCodespaceUser' 'Delete-GitHubComment', 'Delete-GitHubDeploymentEnvironment', 'Delete-GitHubGist', diff --git a/Tests/GitHubCodespaces.tests.ps1 b/Tests/GitHubCodespaces.tests.ps1 index 57f6a663..e97f01ee 100644 --- a/Tests/GitHubCodespaces.tests.ps1 +++ b/Tests/GitHubCodespaces.tests.ps1 @@ -34,6 +34,83 @@ BeforeAll { $mainBranchName = $repo | Get-GitHubRepositoryBranch | Select-Object -ExpandProperty name -First 1 } +Describe 'GitHubCodespaces\Set-GitHubCodespaceVisibility' { + Context 'When setting the visibility of a codespace' { + + It 'sets the visibility successfully too ''Disabled'' ' { + $setVisibilityParams = @{ + Force = $true + OrganizationName = $script:organizationName + Visibility = 'Disabled' + } + # At this time, the API does not offer any way to retrieve the current Visibility for a Codespace. The best we can do is validate that this function call doesn't fail. + Set-GitHubCodespaceVisibility @setVisibilityParams + } + + It 'sets the visibility successfully too ''AllMembers'' ' { + $setVisibilityParams = @{ + Force = $true + OrganizationName = $script:organizationName + Visibility = 'AllMembers' + } + # At this time, the API does not offer any way to retrieve the current Visibility for a Codespace. The best we can do is validate that this function call doesn't fail. + Set-GitHubCodespaceVisibility @setVisibilityParams + } + + It 'sets the visibility successfully to ''SelectedMembers'' ' { + $setVisibilityParams = @{ + Force = $true + OrganizationName = $script:organizationName + UserName = $script:ownerName + Visibility = 'SelectedMembers' + } + # At this time, the API does not offer any way to retrieve the current Visibility for a Codespace. The best we can do is validate that this function call doesn't fail. + Set-GitHubCodespaceVisibility @setVisibilityParams + } + + It 'accepts users via the pipeline for visibility ''SelectedMembers'' ' { + $setVisibilityParams = @{ + Force = $true + OrganizationName = $script:organizationName + Visibility = 'SelectedMembers' + } + # At this time, the API does not offer any way to retrieve the current Visibility for a Codespace. The best we can do is validate that this function call doesn't fail. + $script:ownerName | Set-GitHubCodespaceVisibility @setVisibilityParams + } + + } +} + +Describe 'GitHubCodespaces\Remove-GitHubCodespaceUser' { + Context 'When revoking a user''s access to codespaces for an organization' { + + It 'removes a user successfully' { + { Remove-GitHubCodespaceUser -OrganizationName $script:organizationName -UserName $script:ownerName } | Should -Not -Throw + } + + It 'accepts user names from pipeline' { + # By passing in the same user, we are also proving 304 Not Modified is handled + { $script:OwnerName | Remove-GitHubCodespaceUser -OrganizationName $script:organizationName } | Should -Not -Throw + } + + } +} + +Describe 'GitHubCodespaces\Add-GitHubCodespaceUser' { + Context 'When granting a user access to codespaces for an organization' { + + It 'adds a user successfully' { + { Add-GitHubCodespaceUser -OrganizationName $script:organizationName -UserName $script:ownerName } | Should -Not -Throw + } + + It 'accepts user names from pipeline' { + # By passing in the same user, we are also proving 304 Not Modified is handled + { $script:OwnerName | Add-GitHubCodespaceUser -OrganizationName $script:organizationName } | Should -Not -Throw + } + + } +} + Describe 'GitHubCodespaces\Delete-GitHubCodespace' { Context 'When deleting a codespace for the authenticated user' { BeforeEach { @@ -119,6 +196,20 @@ Describe 'GitHubCodespaces\Get-GitHubCodespace' { } } + Context 'When getting all codespaces for a specified organization' { + BeforeAll { + $codespaces = Get-GitHubCodespace -OrganizationName $script:organizationName + } + + It 'Should return objects of the correct type' { + $codespaces[0].PSObject.TypeNames[0] | Should -Be 'GitHub.Codespace' + } + + It 'Should return one or more results' { + @($codespaces | Where-Object { $_ }).Count | Should -BeGreaterOrEqual 1 + } + } + Context 'When getting a codespace for a specified organization user' { BeforeAll { $codespaces = Get-GitHubCodespace -OrganizationName $script:organizationName @@ -186,6 +277,33 @@ Describe 'GitHubCodespaces\Get-GitHubCodespace' { } } +Describe 'GitHubCodespaces\Get-GitHubCodespaceMachine' { + BeforeAll { + # Suppress HTTP 202 warning for codespace creation + $WarningPreference = 'SilentlyContinue' + + $newGitHubCodespaceParms = @{ + OwnerName = $repo.owner.login + RepositoryName = $defaultRepositoryName + } + $null = New-GitHubCodespace @newGitHubCodespaceParms -Wait + } + + Context 'When listing machine types a codespace can transition to' { + BeforeAll { + $codespaceMachine = Get-GitHubCodespace @newGitHubCodespaceParms | Select-Object -First 1 | Get-GitHubCodespaceMachine + } + + It 'Should return an object of the correct type' { + $codespaceMachine.PSObject.TypeNames[0] | Should -Be 'GitHub.CodespaceMachine' + } + } + + AfterAll { + Get-GitHubCodespace @newGitHubCodespaceParms | Remove-GitHubCodespace -Confirm:$false -Force + } +} + Describe 'GitHubCodespaces\New-GitHubCodespace' { Context -Name 'When creating a repository for the authenticated user' { @@ -198,7 +316,7 @@ Describe 'GitHubCodespaces\New-GitHubCodespace' { } It 'Should return an object of the correct type' { - $codespace | Should -BeOfType PSCustomObject + $codespace.PSObject.TypeNames[0] | Should -Be 'GitHub.Codespace' } It 'Should return the correct properties' { @@ -237,7 +355,7 @@ Describe 'GitHubCodespaces\New-GitHubCodespace' { } It 'Should return an object of the correct type' { - $codespace | Should -BeOfType PSCustomObject + $codespace.PSObject.TypeNames[0] | Should -Be 'GitHub.Codespace' } It 'Should return the correct properties' { @@ -277,7 +395,7 @@ Describe 'GitHubCodespaces\New-GitHubCodespace' { } It 'Should return an object of the correct type' { - $codespace | Should -BeOfType PSCustomObject + $codespace.PSObject.TypeNames[0] | Should -Be 'GitHub.Codespace' } It 'Should return the correct properties' { @@ -313,7 +431,7 @@ Describe 'GitHubCodespaces\New-GitHubCodespace' { } It 'Should return an object of the correct type' { - $codespace | Should -BeOfType PSCustomObject + $codespace.PSObject.TypeNames[0] | Should -Be 'GitHub.Codespace' } It 'Should return the correct properties' { @@ -346,7 +464,7 @@ Describe 'GitHubCodespaces\New-GitHubCodespace' { } It 'Should return an object of the correct type' { - $codespace | Should -BeOfType PSCustomObject + $codespace.PSObject.TypeNames[0] | Should -Be 'GitHub.Codespace' } It 'Should return the correct properties' { @@ -429,6 +547,23 @@ Describe 'GitHubCodespaces\Stop-GitHubCodespace' { } } + Context 'When stopping a codespace for an organization user' { + BeforeAll { + $userCodespace = Get-GitHubCodespace -OrganizationName $script:organizationName -UserName $script:OwnerName | Select-Object -First 1 + } + + It 'Should not throw' { + # Also asserts pipeline input + { Stop-GitHubCodespace -OrganizationName $script:organizationName -UserName $script:OwnerName -CodespaceName $userCodespace.name } | Should -Not -Throw + } + + It 'Should become Shutdown' { + # Also asserts Wait and PassThru + $result = Stop-GitHubCodespace -OrganizationName $script:organizationName -UserName $script:OwnerName -CodespaceName $userCodespace.name -Wait -PassThru + $result.State | Should -Be 'Shutdown' + } + } + AfterAll { Get-GitHubCodespace @newGitHubCodespaceParms | Remove-GitHubCodespace -Confirm:$false -Force } diff --git a/USAGE.md b/USAGE.md index 1fb5f916..9c2f5b17 100644 --- a/USAGE.md +++ b/USAGE.md @@ -148,11 +148,16 @@ * [Removing a codespace](#removing-a-codespace) * [Starting a codespace](#starting-a-codespace) * [Stopping a codespace](#stopping-a-codespace) - * [Codespaces](#codespaces-organizations) - * [Getting codespaces](#getting-organization-codespaces) - * [Removing a codespace](#removing-an-organization-codespace) - * [Stopping a codespace](#stopping-an-organization-codespace) - + * [Codespaces/organizations](#codespaces-organizations) + * [Manage access control for Codespaces](#manage-organization-codespaces-billing) + * [Add users to Codespaces billing](#adding-users-to-codespaces-billing) + * [Removes users from Codespaces billing](#removing-users-from-codespaces-billing) + * [Getting Codespaces](#getting-organization-codespaces) + * [Removing a Codespace](#removing-an-organization-codespace) + * [Stopping a Codespace](#stopping-an-organization-codespace) + * [Codespaces/machines](#codespaces-machines) + * [Getting Codespaces machines](#getting-codespaces-machines) + ---------- ## Full Module Documentation @@ -1380,6 +1385,30 @@ Stop-GithubCodespace -CodespaceName $codespaceName -Wait ### Codespaces organizations +#### Manage organization Codespaces billing +```powershell + +# Disable Codespace access entirely in the organization. +Set-GitHubCodespaceVisibility -Visibility Disabled + +# Allow all organization members to access Codespaces. +Set-GitHubCodespaceVisibility -Visibility AllMembers + +# Limit Codespace access to a selected list of organization members. +# Care should be taken with this option, as the users specified will overwrite any active list. +Set-GitHubCodespaceVisibility -Visibility SelectedMembers -UserName octocat,heptacat +``` + +#### Adding users to Codespaces billing +```powershell +Add-GitHubCodespaceUser -OrganizationName microsoft -UserName octocat,heptacat +``` + +#### Removing users from Codespaces billing +```powershell +Remove-GitHubCodespaceUser -OrganizationName microsoft -UserName octocat,heptacat +``` + #### Getting organization Codespaces ```powershell @@ -1402,6 +1431,20 @@ Remove-GitHubCodespace -OrganizationName microsoft -UserName octocat -CodespaceN ```powershell $codespaceName = 'microsoft-symmetrical-chainsaw-7q4vp6v7q3pwqq' -# Stopping a codespace (wait for Shutdown) +# Stopping a Codespace (wait for Shutdown) Stop-GithubCodespace -OrganizationName microsoft -UserName octocat -CodespaceName $codespaceName -Wait +``` +---------- + +### Codespaces machines + +#### Getting Codespaces machines +```powershell + +# Get machine types a Codespace can transition to use +Get-GitHubCodespace | Select-Object -First 1 | Get-GitHubCodespaceMachine + +# Get all machine types available for a given repository based on its configuration +Get-GitHubCodespaceMachine -Owner microsoft -RepositoryName PowerShellForGitHub + ``` \ No newline at end of file