From 58890019078decf6965b6560dc99f2ae39a91402 Mon Sep 17 00:00:00 2001 From: Jason Vercellone Date: Tue, 4 Feb 2025 04:43:15 -0600 Subject: [PATCH 1/2] =?UTF-8?q?=F0=9F=9A=80GitHub=20codespaces=20(#291)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Description - Add-GitHubCodespaceUser - Export-GitHubCodespace - Get-GitHubCodespace - Get-GitHubCodespaceDefault - Get-GitHubCodespaceDevContainer - Get-GitHubCodespaceExport - New-GitHubCodespace - Remove-GitHubCodespace - Remove-GitHubCodespaceUser - Set-GitHubCodespace - Set-GitHubCodespaceVisibility - Start-GitHubCodespace - Stop-GitHubCodespace - Test-GitHubCodespace - Test-IsCodespace - Wait-GitHubCodespaceAction - Wait-GitHubCodespaceExport Includes a GitHubCodespace.ps1 class, a GitHubCodespace.Format.ps1xml and a ConvertTo-GitHubCodespace private function. Can do more in this area depending on feedback. I was not able to test all the Organization endpoints, but I tested the others locally. These few certainly require more scrutiny: - Add-GitHubCodespaceUser - Remove-GitHubCodespaceUser - Set-GitHubCodespaceVisibility ## Type of change - [x] 🚀 [Feature] ## Checklist - [x] I have performed a self-review of my own code - [x] I have commented my code, particularly in hard-to-understand areas --------- Co-authored-by: Vercellone, Jason Co-authored-by: Marius Storhaug --- .../public/Codespaces/GitHubCodespace.ps1 | 54 ++++++ src/formats/GitHubCodespace.Format.ps1xml | 173 ++++++++++++++++++ .../Codespaces/ConvertTo-GitHubCodespace.ps1 | 11 ++ .../Codespaces/Add-GitHubCodespaceUser.ps1 | 54 ++++++ .../Codespaces/Export-GitHubCodespace.ps1 | 59 ++++++ .../public/Codespaces/Get-GitHubCodespace.ps1 | 104 +++++++++++ .../Codespaces/Get-GitHubCodespaceDefault.ps1 | 51 ++++++ .../Get-GitHubCodespaceDevContainer.ps1 | 57 ++++++ .../Codespaces/Get-GitHubCodespaceExport.ps1 | 47 +++++ .../public/Codespaces/New-GitHubCodespace.ps1 | 159 ++++++++++++++++ .../Codespaces/Remove-GitHubCodespace.ps1 | 72 ++++++++ .../Codespaces/Remove-GitHubCodespaceUser.ps1 | 54 ++++++ .../public/Codespaces/Set-GitHubCodespace.ps1 | 94 ++++++++++ .../Set-GitHubCodespaceVisibility.ps1 | 73 ++++++++ .../Codespaces/Start-GitHubCodespace.ps1 | 55 ++++++ .../Codespaces/Stop-GitHubCodespace.ps1 | 74 ++++++++ .../Codespaces/Test-GitHubCodespace.ps1 | 73 ++++++++ .../public/Codespaces/Test-IsCodespace.ps1 | 6 + .../Codespaces/Wait-GitHubCodespaceAction.ps1 | 39 ++++ .../Codespaces/Wait-GitHubCodespaceExport.ps1 | 47 +++++ 20 files changed, 1356 insertions(+) create mode 100644 src/classes/public/Codespaces/GitHubCodespace.ps1 create mode 100644 src/formats/GitHubCodespace.Format.ps1xml create mode 100644 src/functions/private/Codespaces/ConvertTo-GitHubCodespace.ps1 create mode 100644 src/functions/public/Codespaces/Add-GitHubCodespaceUser.ps1 create mode 100644 src/functions/public/Codespaces/Export-GitHubCodespace.ps1 create mode 100644 src/functions/public/Codespaces/Get-GitHubCodespace.ps1 create mode 100644 src/functions/public/Codespaces/Get-GitHubCodespaceDefault.ps1 create mode 100644 src/functions/public/Codespaces/Get-GitHubCodespaceDevContainer.ps1 create mode 100644 src/functions/public/Codespaces/Get-GitHubCodespaceExport.ps1 create mode 100644 src/functions/public/Codespaces/New-GitHubCodespace.ps1 create mode 100644 src/functions/public/Codespaces/Remove-GitHubCodespace.ps1 create mode 100644 src/functions/public/Codespaces/Remove-GitHubCodespaceUser.ps1 create mode 100644 src/functions/public/Codespaces/Set-GitHubCodespace.ps1 create mode 100644 src/functions/public/Codespaces/Set-GitHubCodespaceVisibility.ps1 create mode 100644 src/functions/public/Codespaces/Start-GitHubCodespace.ps1 create mode 100644 src/functions/public/Codespaces/Stop-GitHubCodespace.ps1 create mode 100644 src/functions/public/Codespaces/Test-GitHubCodespace.ps1 create mode 100644 src/functions/public/Codespaces/Test-IsCodespace.ps1 create mode 100644 src/functions/public/Codespaces/Wait-GitHubCodespaceAction.ps1 create mode 100644 src/functions/public/Codespaces/Wait-GitHubCodespaceExport.ps1 diff --git a/src/classes/public/Codespaces/GitHubCodespace.ps1 b/src/classes/public/Codespaces/GitHubCodespace.ps1 new file mode 100644 index 000000000..7b90e62b5 --- /dev/null +++ b/src/classes/public/Codespaces/GitHubCodespace.ps1 @@ -0,0 +1,54 @@ +class GitHubCodespace { + # Unique identifier of the delivery. + [uint64] $ID + + [string] $Name + + [Guid] $environment_id + + # Time when the delivery was delivered. + [object] $Owner + [object] $Billable_Owner + [object] $Repository + [object] $Machine + [bool] $Prebuild + [datetime] $created_at + [Nullable[datetime]] $updated_at + [Nullable[datetime]] $last_used_at + [string]$State + [string] $url + [object] $git_status + [string] $location + [uint16] $idle_timeout_minutes + [string] $web_url + [string] $machines_url + [string] $start_url + [string] $stop_url + [string] $pulls_url + [string[]] $recent_folders + [object] $runtime_constraints + [string] $display_name + [string] $devcontainer_path + [bool] $pending_operation + [UInt32] $retention_period_minutes + [Nullable[datetime]] $retention_expires_at + [string] $template + [string] $publish_url + + # Simple parameterless constructor + GitHubCodespace() {} + + # Creates a context object from a hashtable of key-vaule pairs. + GitHubCodespace([hashtable]$Properties) { + foreach ($Property in $Properties.Keys) { + $this.$Property = $Properties.$Property + } + } + + # Creates a context object from a PSCustomObject. + GitHubCodespace([PSCustomObject]$Object) { + $Object.PSObject.Properties | ForEach-Object { + $this.($_.Name) = $_.Value + } + } +} diff --git a/src/formats/GitHubCodespace.Format.ps1xml b/src/formats/GitHubCodespace.Format.ps1xml new file mode 100644 index 000000000..929310fc3 --- /dev/null +++ b/src/formats/GitHubCodespace.Format.ps1xml @@ -0,0 +1,173 @@ + + + + + + GitHubCodespaceTableView + + GitHubCodespace + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Name + + + display_name + + + location + + + state + + + created_at + + + updated_at + + + last_used_at + + + + + + + + + + GitHubCodespaceListView + + GitHubCodespace + + + + + + + ID + + + Name + + + environment_id + + + Owner + + + Billable_Owner + + + Repository + + + Machine + + + Prebuild + + + + created_at + + + + updated_at + + + + last_used_at + + + State + + + url + + + git_status + + + location + + + idle_timeout_minutes + + + web_url + + + machines_url + + + start_url + + + stop_url + + + pulls_url + + + recent_folders + + + runtime_constraints + + + display_name + + + devcontainer_path + + + pending_operation + + + retention_period_minutes + + + retention_expires_at + + + template + + + publish_url + + + + + + + + diff --git a/src/functions/private/Codespaces/ConvertTo-GitHubCodespace.ps1 b/src/functions/private/Codespaces/ConvertTo-GitHubCodespace.ps1 new file mode 100644 index 000000000..860686de1 --- /dev/null +++ b/src/functions/private/Codespaces/ConvertTo-GitHubCodespace.ps1 @@ -0,0 +1,11 @@ +function ConvertTo-GitHubCodespace { + [OutputType([GitHubCodespace])] + [CmdletBinding()] + param( + [Parameter(ValueFromPipeline)] + $InputObject + ) + process { + [GitHubCodespace]$InputObject + } +} diff --git a/src/functions/public/Codespaces/Add-GitHubCodespaceUser.ps1 b/src/functions/public/Codespaces/Add-GitHubCodespaceUser.ps1 new file mode 100644 index 000000000..1d1a00fe2 --- /dev/null +++ b/src/functions/public/Codespaces/Add-GitHubCodespaceUser.ps1 @@ -0,0 +1,54 @@ +function Add-GitHubCodespaceUser { + <# + .SYNOPSIS + Adds users to Codespaces access for an organization. + + .DESCRIPTION + Codespaces for the specified users will be billed to the organization. + To use this endpoint, the access settings for the organization must be set to selected_members. + For information on how to change this setting please see [these docs](https://docs.github.com/rest/codespaces/organizations#manage-access-control-for-organization-codespaces) + You must authenticate using an access token with the admin:org scope to use this endpoint. + + .PARAMETER Organization + The organization name. The name is not case sensitive. + + .PARAMETER User + Handle for the GitHub user account(s). + + .EXAMPLE + > Add-GitHubCodespaceUser -Organization PSModule -user fake_user_name + + .OUTPUTS + [PSObject[]] + + .LINK + https://docs.github.com/en/rest/codespaces/organizations?apiVersion=2022-11-28#add-users-to-codespaces-access-for-an-organization + #> + [CmdletBinding(SupportsShouldProcess)] + param ( + [Parameter(Mandatory)] + [string]$Organization, + [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)] + [string[]]$User, + + # 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()] + [object] $Context = (Get-GitHubContext) + ) + process { + if ($PSCmdLet.ShouldProcess( + "Adding users [$($User -join ',')] to GitHub codespace access", + "Are you sure you want to add $($User -join ',')?", + 'Add codespace users' + )) { + $postParams = @{ + APIEndpoint = "/orgs/$Organization/codespaces/access/selected_users" + Body = [PSCustomObject]@{ selected_usernames = @($User) } | ConvertTo-Json + Context = $Context + Method = 'POST' + } + Invoke-GitHubAPI @postParams | Select-Object -ExpandProperty Response + } + } +} diff --git a/src/functions/public/Codespaces/Export-GitHubCodespace.ps1 b/src/functions/public/Codespaces/Export-GitHubCodespace.ps1 new file mode 100644 index 000000000..be2d99aac --- /dev/null +++ b/src/functions/public/Codespaces/Export-GitHubCodespace.ps1 @@ -0,0 +1,59 @@ +function Export-GitHubCodespace { + <# + .SYNOPSIS + Exports a codespace. + + .DESCRIPTION + Triggers an export of the specified codespace and returns a URL and ID where the status of the export can be monitored. + If changes cannot be pushed to the codespace's repository, they will be pushed to a new or previously-existing fork instead. + You must authenticate using a personal access token with the codespace scope to use this endpoint. + GitHub Apps must have write access to the codespaces_lifecycle_admin repository permission to use this endpoint. + + .PARAMETER Name + The name of the codespace. + + .PARAMETER Wait + If present will wait for the export to complete. + + .EXAMPLE + > Export-GitHubCodespace -Name fluffy-disco-v7xgv7j4j52pvw9 + + .EXAMPLE + > Export-GitHubCodespace -Name $ominousSpace.name -Wait + + .OUTPUTS + [PSObject[]] + + .LINK + https://docs.github.com/en/rest/codespaces/codespaces?apiVersion=2022-11-28#export-a-codespace-for-the-authenticated-user + #> + [CmdletBinding()] + param ( + [Parameter(Mandatory)] + [string]$Name, + + [switch]$Wait, + + # 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()] + [object] $Context = (Get-GitHubContext) + ) + process { + $postParams = @{ + APIEndpoint = "/user/codespaces/$Name/exports" + Context = $Context + Method = 'POST' + } + $export = Invoke-GitHubAPI @postParams | Select-Object -ExpandProperty Response + if ($Wait.IsPresent) { + $waitParams = @{ + Context = $Context + Id = $export.id + Name = $Name + } + $export = Wait-GitHubCodespaceExport @waitParams + } + $export + } +} diff --git a/src/functions/public/Codespaces/Get-GitHubCodespace.ps1 b/src/functions/public/Codespaces/Get-GitHubCodespace.ps1 new file mode 100644 index 000000000..44cbf89f9 --- /dev/null +++ b/src/functions/public/Codespaces/Get-GitHubCodespace.ps1 @@ -0,0 +1,104 @@ +function Get-GitHubCodespace { + <# + .SYNOPSIS + Retrieve GitHub codespace(s). + + .PARAMETER Organization + The organization name. The name is not case sensitive. + + .PARAMETER User + The handle for the GitHub user account. + + .PARAMETER Owner + The account owner of the repository. The name is not case sensitive. + + .PARAMETER Repository + The name of the repository. The name is not case sensitive. + + .PARAMETER Name + The name of the codespace. + + .EXAMPLE + > Get-GitHubCodespace -Name urban-dollop-pqxgrq55v4c97g4 + + .OUTPUTS + [PSObject[]] + + .LINK + https://docs.github.com/en/rest/codespaces/codespaces?apiVersion=2022-11-28#list-codespaces-in-a-repository-for-the-authenticated-user + + .LINK + https://docs.github.com/en/rest/codespaces/codespaces?apiVersion=2022-11-28#list-codespaces-for-the-authenticated-user + + .LINK + https://docs.github.com/en/rest/codespaces/organizations?apiVersion=2022-11-28#list-codespaces-for-the-organization + + .LINK + https://docs.github.com/en/rest/codespaces/codespaces?apiVersion=2022-11-28#get-a-codespace-for-the-authenticated-user + + .LINK + https://docs.github.com/en/rest/codespaces/organizations?apiVersion=2022-11-28#list-codespaces-for-a-user-in-organization + #> + # [CmdletBinding(DefaultParameterSetName = 'Scope', SupportsPaging)] + [CmdletBinding(DefaultParameterSetName = 'Scope')] + param ( + [Parameter(ParameterSetName = 'Organization', Mandatory)] + [string]$Organization, + [Parameter(ParameterSetName = 'Organization')] + [string]$User, + + [Parameter(ParameterSetName = 'Repository', Mandatory)] + [string]$Owner, + + [Parameter(ParameterSetName = 'Repository', Mandatory)] + [string]$Repository, + + [Parameter(ParameterSetName = 'Name', Mandatory)] + [string]$Name, + + [Parameter(ParameterSetName = 'Scope')] + [ValidateSet('Organization', 'User')] + [string]$Scope = 'User', + + # 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()] + [object] $Context = (Get-GitHubContext) + ) + process { + $getParams = @{ + APIEndpoint = switch ($PSCmdlet.ParameterSetName) { + 'Organization' { + [string]::IsNullOrWhiteSpace($User) ? + "/orgs/$Organization/codespaces" : + "/orgs/$Organization/members/$User/codespaces" + break + } + 'Repository' { + "/repos/$Owner/$Repository/codespaces" + break + } + 'Name' { + "/user/codespaces/$Name" + break + } + 'Scope' { + $Scope -eq 'Organization' ? + "/orgs/$Organization/codespaces" : + '/user/codespaces' + break + } + } + Context = $Context + Method = 'GET' + } + # foreach($_name in 'First','Skip') { + # if ($PSBoundParameters.ContainsKey($_name)) { + # $getParams[$_name] = $PSBoundParameters[$_name] + # } + # } + $response = Invoke-GitHubAPI @getParams | Select-Object -ExpandProperty Response + [bool]$response.PSObject.Properties['codespaces'] ? $response.codespaces : $response | ConvertTo-GitHubCodespace + #| Add-ObjectDetail -TypeName GitHub.Codespace -DefaultProperties name, display_name, location, state, created_at, updated_at, last_used_at + } +} \ No newline at end of file diff --git a/src/functions/public/Codespaces/Get-GitHubCodespaceDefault.ps1 b/src/functions/public/Codespaces/Get-GitHubCodespaceDefault.ps1 new file mode 100644 index 000000000..4895f5c7d --- /dev/null +++ b/src/functions/public/Codespaces/Get-GitHubCodespaceDefault.ps1 @@ -0,0 +1,51 @@ +function Get-GitHubCodespaceDefault { + <# + .SYNOPSIS + Get default attributes for creating a new codespace. + + .DESCRIPTION + Gets the default attributes for codespaces created by the user with the repository. + You must authenticate using an access token with the codespace scope to use this endpoint. + GitHub Apps must have write access to the codespaces repository permission to use this endpoint. + + .PARAMETER Owner + The account owner of the repository. The name is not case sensitive. + + .PARAMETER Repository + The name of the repository. The name is not case sensitive. + + .EXAMPLE + > Get-GitHubCodespaceDefault -Owner PSModule -Repository Sodium + + .OUTPUTS + [PSObject[]] + + .LINK + https://docs.github.com/en/rest/codespaces/codespaces?apiVersion=2022-11-28#get-default-attributes-for-a-codespace + #> + # [CmdletBinding(SupportsPaging)] + [CmdletBinding()] + param ( + [Parameter(ParameterSetName = 'Repository', Mandatory)] + [string]$Owner, + + [Parameter(ParameterSetName = 'Repository', Mandatory)] + [string]$Repository, + + # 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()] + [object] $Context = (Get-GitHubContext) + ) + $getParams = @{ + ApiEndpoint = "/repos/$Owner/$Repository/codespaces/new" + } + # foreach($_name in 'First','Skip') { + # if ($PSBoundParameters.ContainsKey($_name)) { + # $getParams[$_name] = $PSBoundParameters[$_name] + # } + # } + $response = Invoke-GitHubAPI @getParams | Select-Object -ExpandProperty Response + [bool]$response.PSObject.Properties['billable_owner'] ? $response.billable_owner : $response + # | Add-ObjectDetail -DefaultProperties login,type,site_admin +} diff --git a/src/functions/public/Codespaces/Get-GitHubCodespaceDevContainer.ps1 b/src/functions/public/Codespaces/Get-GitHubCodespaceDevContainer.ps1 new file mode 100644 index 000000000..448cce4f2 --- /dev/null +++ b/src/functions/public/Codespaces/Get-GitHubCodespaceDevContainer.ps1 @@ -0,0 +1,57 @@ +function Get-GitHubCodespaceDevContainer { + <# + .SYNOPSIS + List devcontainer configurations in a repository for the authenticated user. + + .DESCRIPTION + Lists the devcontainer.json files associated with a specified repository and the authenticated user. + These files specify launchpoint configurations for codespaces created within the repository. + You must authenticate using an access token with the codespace scope to use this endpoint. + GitHub Apps must have read access to the codespaces_metadata repository permission to use this endpoint. + + .PARAMETER Owner + The account owner of the repository. The name is not case sensitive. + + .PARAMETER Repository + The name of the repository. The name is not case sensitive. + + .EXAMPLE + > Get-GitHubCodespaceDevContainer -Owner PSModule -Repository Sodium + + path name display_name + ---- ---- ------------ + .devcontainer/devcontainer.json Debian Debian + + .OUTPUTS + [PSObject[]] + + .LINK + https://docs.github.com/en/rest/codespaces/codespaces?apiVersion=2022-11-28#list-devcontainer-configurations-in-a-repository-for-the-authenticated-user + #> + # [CmdletBinding(SupportsPaging)] + [CmdletBinding()] + param ( + [Parameter(ParameterSetName = 'Repository', Mandatory)] + [string]$Owner, + + [Parameter(ParameterSetName = 'Repository', Mandatory)] + [string]$Repository, + + # 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()] + [object] $Context = (Get-GitHubContext) + ) + $getParams = @{ + APIEndpoint = "/repos/$Owner/$Repository/codespaces/devcontainers" + ContentType = 'application/vnd.github+json' + Method = 'GET' + } + # foreach($_name in 'First','Skip') { + # if ($PSBoundParameters.ContainsKey($_name)) { + # $getParams[$_name] = $PSBoundParameters[$_name] + # } + # } + $response = Invoke-GitHubAPI @getParams | Select-Object -ExpandProperty Response + [bool]$response.PSObject.Properties['devcontainers'] ? $response.devcontainers : $response +} diff --git a/src/functions/public/Codespaces/Get-GitHubCodespaceExport.ps1 b/src/functions/public/Codespaces/Get-GitHubCodespaceExport.ps1 new file mode 100644 index 000000000..f4a68ab55 --- /dev/null +++ b/src/functions/public/Codespaces/Get-GitHubCodespaceExport.ps1 @@ -0,0 +1,47 @@ +function Get-GitHubCodespaceExport { + <# + .SYNOPSIS + Get details about a codespace export. + + .DESCRIPTION + Gets information about an export of a codespace. + You must authenticate using a personal access token with the codespace scope to use this endpoint. + GitHub Apps must have read access to the codespaces_lifecycle_admin repository permission to use this endpoint. + + .PARAMETER Id + The ID of the export operation, or latest. For future use. Only latest is supported now. + + .PARAMETER Name + The name of the codespace. + + .EXAMPLE + > Get-GitHubCodespaceExport -Name urban-dollop-pqxgrq55v4c97g4 + + .OUTPUTS + [PSObject[]] + + .LINK + https://docs.github.com/en/rest/codespaces/codespaces?apiVersion=2022-11-28#get-details-about-a-codespace-export + #> + [CmdletBinding()] + param ( + [Parameter()] + [string]$Id = 'latest', + + [Parameter(Mandatory)] + [string]$Name, + + # 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()] + [object] $Context = (Get-GitHubContext) + ) + process { + $getParams = @{ + APIEndpoint = "/user/codespaces/$Name/exports/$Id" + Context = $Context + Method = 'GET' + } + Invoke-GitHubAPI @getParams | Select-Object -ExpandProperty Response + } +} diff --git a/src/functions/public/Codespaces/New-GitHubCodespace.ps1 b/src/functions/public/Codespaces/New-GitHubCodespace.ps1 new file mode 100644 index 000000000..2f84a7a16 --- /dev/null +++ b/src/functions/public/Codespaces/New-GitHubCodespace.ps1 @@ -0,0 +1,159 @@ +function New-GitHubCodespace { + <# + .SYNOPSIS + Create a codespace. + + .PARAMETER Owner + The account owner of the repository. The name is not case sensitive. + + .PARAMETER Repository + The name of the repository. The name is not case sensitive. + + .PARAMETER Name + The name of the codespace. + + .PARAMETER Ref + Git ref (typically a branch name) for this codespace + + .PARAMETER Location + The requested location for a new codespace. Best efforts are made to respect this upon creation. Assigned by IP if not provided. + + .PARAMETER ClientIp + IP for location auto-detection when proxying a request + + .PARAMETER Machine + Machine type to use for this codespace + + .PARAMETER Devcontainer + Path to devcontainer.json config to use for this codespace + + .PARAMETER NoMultipleRepoPermissions + Whether to authorize requested permissions from devcontainer.json + + .PARAMETER WorkingDirectory + Working directory for this codespace + + .PARAMETER Timeout + Time in minutes before codespace stops from inactivity + + .PARAMETER DisplayName + Display name for this codespace + + .PARAMETER RetentionPeriod + Duration in minutes after codespace has gone idle in which it will be deleted. Must be integer minutes between 0 and 43200 (30 days). + + .EXAMPLE + > New-GitHubCodespace -Owner PSModule -Repository Sodium + + .OUTPUTS + [PSObject[]] + + .LINK + https://docs.github.com/en/rest/codespaces/codespaces?apiVersion=2022-11-28#create-a-codespace-in-a-repository + + .LINK + https://docs.github.com/en/rest/codespaces/codespaces?apiVersion=2022-11-28#create-a-codespace-from-a-pull-request + + .LINK + https://docs.github.com/en/rest/codespaces/codespaces?apiVersion=2022-11-28#create-a-codespace-for-the-authenticated-user + #> + [CmdletBinding(DefaultParameterSetName = 'User', SupportsShouldProcess)] + param ( + [Parameter(ParameterSetName = 'Repository', Mandatory)] + [Parameter(ParameterSetName = 'PullRequest', Mandatory)] + [string]$Owner, + + [Parameter(ParameterSetName = 'Repository', Mandatory)] + [Parameter(ParameterSetName = 'PullRequest', Mandatory)] + [string]$Repository, + + [Parameter(ParameterSetName = 'PullRequest', Mandatory)] + [Parameter(ParameterSetName = 'User')] + [int]$PullNumber, + + [Parameter(ParameterSetName = 'User', Mandatory)] + [int]$RepositoryId, + + [Parameter(ParameterSetName = 'Repository')] + [Parameter(ParameterSetName = 'User')] + [string]$Ref, + + [ValidateSet('EastUs', 'SouthEastAsia', 'WestEurope', 'WestUs2')] + [string]$Location, + [string]$ClientIp, + [ValidateSet('basicLinux32gb', 'standardLinux32gb', 'premiumLinux', 'largePremiumLinux')] + [string]$Machine, + [string]$Devcontainer, + [switch]$NoMultipleRepoPermissions, + [string]$WorkingDirectory, + [int]$Timeout, + [string]$DisplayName, + [int]$RetentionPeriod, + + # 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()] + [object] $Context = (Get-GitHubContext) + ) + begin { + $propertyMap = @{ + ClientIp = 'client_ip' + Devcontainer = 'devcontainer_path' + DisplayName = 'display_name' + Location = 'location' + Machine = 'machine' + Ref = 'ref' + RetentionPeriod = 'retention_period_minutes' + Timeout = 'idle_timeout_minutes' + WorkingDirectory = 'working_directory' + } + } + process { + if ($PSCmdLet.ShouldProcess( + 'Creating GitHub codespace', + 'Are you sure you want to create a new codespace?', + 'Create codespace' + )) { + $properties = @{ + multi_repo_permissions_opt_out = $NoMultipleRepoPermissions.IsPresent + } + foreach ($p in $PSBoundParameters.GetEnumerator()) { + if ($propertyMap.ContainsKey($p.Key) -and -not [string]::IsNullOrWhiteSpace($p.Value)) { + $properties.Add($propertyMap[$p.Key], $p.Value) + } + } + if ($PSCmdlet.ParameterSetName -eq 'User') { + if ($PSBoundParameters.ContainsKey('PullRequest')) { + $properties.Add('pull_request', [PSCustomObject]@{ + pull_request_number = $PullNumber + repository_id = $RepositoryId + }) + } + else { + $properties.Add('repository_id', $RepositoryId) + } + } + $postParams = @{ + APIEndpoint = switch ($PSCmdlet.ParameterSetName) { + 'PullRequest' { + "/repos/$Owner/$Repository/pulls/$PullNumber/codespaces" + break + } + 'Repository' { + "/repos/$Owner/$Repository/codespaces" + break + } + 'User' { + '/user/codespaces' + break + } + } + Body = [PSCustomObject]$properties | ConvertTo-Json + Context = $Context + Method = 'POST' + } + Invoke-GitHubAPI @postParams | Select-Object -ExpandProperty Response | ConvertTo-GitHubCodespace + # | Add-ObjectDetail -TypeName GitHub.Codespace -DefaultProperties name, display_name, location, state, created_at, updated_at, last_used_at + } + } +} diff --git a/src/functions/public/Codespaces/Remove-GitHubCodespace.ps1 b/src/functions/public/Codespaces/Remove-GitHubCodespace.ps1 new file mode 100644 index 000000000..7e386fa68 --- /dev/null +++ b/src/functions/public/Codespaces/Remove-GitHubCodespace.ps1 @@ -0,0 +1,72 @@ +function Remove-GitHubCodespace { + <# + .SYNOPSIS + Delete a codespace. + + .PARAMETER Organization + The organization name. The name is not case sensitive. + + .PARAMETER User + The handle for the GitHub user account. + + .PARAMETER Name + The name of the codespace. + + .EXAMPLE + > Remove-GitHubCodespace -Name urban-dollop-pqxgrq55v4c97g4 + + Request : {[Authentication, Bearer], [Method, Delete], [Token, System.Security.SecureString], [Headers, System.Collections.Hashtable]…} + Response : + Headers : @{Access-Control-Allow-Origin=*; Access-Control-Expose-Headers=ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, + X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset; Content-Length=2; + Content-Security-Policy=default-src 'none'; Content-Type=application/json; charset=utf-8; Date=Sun, 02 Feb 2025 00:51:10 GMT; github-authentication-token-expiration=2025-05-01 01:22:47 UTC; + Referrer-Policy=origin-when-cross-origin, strict-origin-when-cross-origin; Server=github.com; Strict-Transport-Security=max-age=31536000; includeSubdomains; preload; Vary=Accept-Encoding, + Accept, X-Requested-With; x-accepted-oauth-scopes=codespace; X-Content-Type-Options=nosniff; X-Frame-Options=deny; x-github-api-version-selected=2022-11-28; x-github-media-type=github.v3; + format=json; x-github-request-id=D313:3B725F:28042C0:50B3464:679EC17E; x-oauth-scopes=admin:enterprise, admin:gpg_key, admin:org, admin:org_hook, admin:public_key, admin:repo_hook, + admin:ssh_signing_key, audit_log, codespace, copilot, delete:packages, gist, notifications, project, repo, user, workflow, write:discussion, write:packages; x-ratelimit-limit=5000; + x-ratelimit-remaining=4991; x-ratelimit-reset=1738460387; x-ratelimit-resource=codespaces; x-ratelimit-used=9; X-XSS-Protection=0} + StatusCode : 202 + StatusDescription : Accepted + + .OUTPUTS + [PSObject[]] + + .LINK + https://docs.github.com/en/rest/codespaces/codespaces?apiVersion=2022-11-28#about-github-codespaces + + .LINK + https://docs.github.com/en/rest/codespaces/organizations?apiVersion=2022-11-28#list-codespaces-for-a-user-in-organization + #> + [CmdletBinding(DefaultParameterSetName = 'User', SupportsShouldProcess)] + param ( + [Parameter(ParameterSetName = 'Organization', Mandatory)] + [string]$Organization, + + [Parameter(ParameterSetName = 'Organization', Mandatory)] + [string]$User, + + [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)] + [string]$Name, + + # 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()] + [object] $Context = (Get-GitHubContext) + ) + process { + if ($PSCmdLet.ShouldProcess( + "Deleting GitHub codespace [$Name]", + "Are you sure you want to delete $($Name)?", + 'Delete codespace' + )) { + $delParams = @{ + APIEndpoint = $PSCmdlet.ParameterSetName -eq 'Organization' ? + "orgs/$Organization/members/$User/codespaces/$Name" : + "user/codespaces/$Name" + Context = $Context + Method = 'DELETE' + } + Invoke-GitHubAPI @delParams + } + } +} diff --git a/src/functions/public/Codespaces/Remove-GitHubCodespaceUser.ps1 b/src/functions/public/Codespaces/Remove-GitHubCodespaceUser.ps1 new file mode 100644 index 000000000..e3457481f --- /dev/null +++ b/src/functions/public/Codespaces/Remove-GitHubCodespaceUser.ps1 @@ -0,0 +1,54 @@ +function Remove-GitHubCodespaceUser { + <# + .SYNOPSIS + Removes users from Codespaces access for an organization. + + .DESCRIPTION + Codespaces for the specified users will no longer be billed to the organization. + To use this endpoint, the billing settings for the organization must be set to selected_members. + For information on how to change this setting please see [these docs](https://docs.github.com/rest/codespaces/organizations#manage-access-control-for-organization-codespaces) + You must authenticate using an access token with the admin:org scope to use this endpoint. + + .PARAMETER Organization + The organization name. The name is not case sensitive. + + .PARAMETER User + Handle for the GitHub user account(s). + + .EXAMPLE + > Remove-GitHubCodespaceUser -Organization PSModule -user fake_user_name + + .OUTPUTS + [PSObject[]] + + .LINK + https://docs.github.com/en/rest/codespaces/organizations?apiVersion=2022-11-28#remove-users-from-codespaces-access-for-an-organization + #> + [CmdletBinding(SupportsShouldProcess)] + param ( + [Parameter(Mandatory)] + [string]$Organization, + [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)] + [string[]]$User, + + # 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()] + [object] $Context = (Get-GitHubContext) + ) + process { + if ($PSCmdLet.ShouldProcess( + "Removing user [$($User -join ',')] from GitHub codespace access", + "Are you sure you want to remove $($User -join ',') from GitHub codespace access?", + 'Remove codespace users' + )) { + $delParams = @{ + APIEndpoint = "/orgs/$Organization/codespaces/access/selected_users" + Body = [PSCustomObject]@{ selected_usernames = @($User) } | ConvertTo-Json + Context = $Context + Method = 'DELETE' + } + Invoke-GitHubAPI @delParams | Select-Object -ExpandProperty Response + } + } +} diff --git a/src/functions/public/Codespaces/Set-GitHubCodespace.ps1 b/src/functions/public/Codespaces/Set-GitHubCodespace.ps1 new file mode 100644 index 000000000..917a7f1d3 --- /dev/null +++ b/src/functions/public/Codespaces/Set-GitHubCodespace.ps1 @@ -0,0 +1,94 @@ +function Set-GitHubCodespace { + <# + .SYNOPSIS + Update a codespace. + + .PARAMETER Name + The name of the codespace. + + .PARAMETER Ref + Git ref (typically a branch name) for this codespace + + .PARAMETER Location + The requested location for a new codespace. Best efforts are made to respect this upon creation. Assigned by IP if not provided. + + .PARAMETER ClientIp + IP for location auto-detection when proxying a request + + .PARAMETER Machine + Machine type to use for this codespace + + .PARAMETER Devcontainer + Path to devcontainer.json config to use for this codespace + + .PARAMETER NoMultipleRepoPermissions + Whether to authorize requested permissions from devcontainer.json + + .PARAMETER WorkingDirectory + Working directory for this codespace + + .PARAMETER Timeout + Time in minutes before codespace stops from inactivity + + .PARAMETER DisplayName + Display name for this codespace + + .PARAMETER RetentionPeriod + Duration in minutes after codespace has gone idle in which it will be deleted. Must be integer minutes between 0 and 43200 (30 days). + + .EXAMPLE + > Set-GitHubCodespace -Name fluffy-disco-v7xgv7j4j52pvw9 -DisplayName 'vigilant doodle' + + .OUTPUTS + [PSObject[]] + + .LINK + https://docs.github.com/en/rest/codespaces/codespaces?apiVersion=2022-11-28#update-a-codespace-for-the-authenticated-user + #> + [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Low')] + param ( + [Parameter(Mandatory)] + [string]$Name, + + [ValidateSet('basicLinux32gb', 'standardLinux32gb', 'premiumLinux', 'largePremiumLinux')] + [string]$Machine, + [string]$DisplayName, + [string[]]$RecentFolders, + + # 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()] + [object] $Context = (Get-GitHubContext) + ) + begin { + $propertyMap = @{ + DisplayName = 'display_name' + Machine = 'machine' + RecentFolders = 'recent_folders' + } + } + process { + $properties = @{} + foreach ($p in $PSBoundParameters.GetEnumerator()) { + if ($propertyMap.ContainsKey($p.Key) -and -not [string]::IsNullOrWhiteSpace($p.Value)) { + $properties.Add($propertyMap[$p.Key], $p.Value) + } + } + if ($properties.Count -gt 0) { + if ($PSCmdLet.ShouldProcess( + "Updating github codespace [$Name]", + "Are you sure you want to update github codespace [$Name]?", + 'Update codespace' + )) { + $patchParams = @{ + APIEndpoint = "/user/codespaces/$Name" + Body = [PSCustomObject]$properties | ConvertTo-Json + Context = $Context + Method = 'PATCH' + } + Invoke-GitHubAPI @patchParams | Select-Object -ExpandProperty Response | ConvertTo-GitHubCodespace + # | Add-ObjectDetail -TypeName GitHub.Codespace -DefaultProperties name,display_name,location,state,created_at,updated_at,last_used_at + } + } + } +} diff --git a/src/functions/public/Codespaces/Set-GitHubCodespaceVisibility.ps1 b/src/functions/public/Codespaces/Set-GitHubCodespaceVisibility.ps1 new file mode 100644 index 000000000..7aa2ea2ee --- /dev/null +++ b/src/functions/public/Codespaces/Set-GitHubCodespaceVisibility.ps1 @@ -0,0 +1,73 @@ +function Set-GitHubCodespaceVisibility { + <# + .SYNOPSIS + Manage access control for organization codespaces. + + .DESCRIPTION + 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. + You must authenticate using an access token with the admin:org scope to use this endpoint. + + .PARAMETER Organization + The organization name. The name is not case sensitive. + + .PARAMETER User + The usernames of the organization members who should have access to codespaces in the organization. + + Required when visibility is selected_members. The provided list of usernames will replace any existing value. + + .PARAMETER Visibility + Which users can access codespaces in the organization. disabled means that no users can access codespaces in the organization. + + .PARAMETER Force + When specified, forces execution without confirmation. + + .EXAMPLE + Set-GitHubCodespaceVisibility -Visibility selected_members -User fake_user_name -Force + + .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]$Organization, + [ValidateSet('disabled', 'selected_members', 'all_members', 'all_members_and_outside_collaborators')] + [string]$Visibility, + + [switch]$Force, + + # 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()] + [object] $Context = (Get-GitHubContext) + ) + dynamicparam { + if ($Visibility -eq 'selected_members') { + $paramDictionary = [Management.Automation.RuntimeDefinedParameterDictionary]::new() + # Add a mandatory string array parameter called 'User' + $attributeCollection = @( [Management.Automation.ParameterAttribute]@{ Mandatory = $true } ) + $paramDictionary.Add('User', [Management.Automation.RuntimeDefinedParameter]::new('User', [String[]], $attributeCollection)) + $paramDictionary + } + } + process { + if ($Force.IsPresent -and -not $Confirm) { $ConfirmPreference = 'None' } + if ($PSCmdLet.ShouldProcess( + "Changing codespace visibility to [$($Visibility)]", + "Are you sure you want to set visibility to $($Visibility)?", + 'Change codespace visibility')) { + $properties = @{ visibility = $Visibility } + if ($Visibility -eq 'selected_members') { + $properties.Add('selected_usernames', @($PSBoundParameters.User)) + } + $putParams = @{ + APIEndpoint = "/orgs/$Organization/codespaces/access" + Body = [PSCustomObject]$properties | ConvertTo-Json + Context = $Context + Method = 'PUT' + } + Invoke-GitHubAPI @putParams | Select-Object -ExpandProperty Response + } + } +} diff --git a/src/functions/public/Codespaces/Start-GitHubCodespace.ps1 b/src/functions/public/Codespaces/Start-GitHubCodespace.ps1 new file mode 100644 index 000000000..dcf796e6c --- /dev/null +++ b/src/functions/public/Codespaces/Start-GitHubCodespace.ps1 @@ -0,0 +1,55 @@ +function Start-GitHubCodespace { + <# + .SYNOPSIS + Start a codespace. + + .PARAMETER Name + The name of the codespace. + + .PARAMETER Wait + If present will wait for the codespace to start. + + .EXAMPLE + > Start-GitHubCodespace -Name urban-dollop-pqxgrq55v4c97g4 + + .OUTPUTS + [PSObject[]] + + .LINK + https://docs.github.com/en/rest/codespaces/codespaces?apiVersion=2022-11-28#start-a-codespace-for-the-authenticated-user + #> + [CmdletBinding(SupportsShouldProcess, ConfirmImpact='Low')] + param ( + [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)] + [string]$Name, + + [switch]$Wait, + + # 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()] + [object] $Context = (Get-GitHubContext) + ) + process { + if ($PSCmdLet.ShouldProcess( + "Starting GitHub codespace [$Name]", + "Are you sure you want to start GitHub codespace [$Name]?", + 'Start GitHub codespace' + )) { + $postParams = @{ + APIEndpoint = $PSCmdlet.ParameterSetName -eq 'Organization' ? + "/orgs/$Organization/members/$User/codespaces/$Name/start" : + "/user/codespaces/$Name/start" + Context = $Context + Method = 'POST' + } + $codespace = Invoke-GitHubAPI @postParams | Select-Object -ExpandProperty Response + # | Add-ObjectDetail -TypeName GitHub.Codespace -DefaultProperties name, display_name, location, state, created_at, updated_at, last_used_at + if ($Wait.IsPresent) { + $getParams = @{ Name = $Name } + $codespace = Wait-GitHubCodespaceAction -GetParameters $getParams + } + $codespace + } + } +} diff --git a/src/functions/public/Codespaces/Stop-GitHubCodespace.ps1 b/src/functions/public/Codespaces/Stop-GitHubCodespace.ps1 new file mode 100644 index 000000000..f1aa5cf06 --- /dev/null +++ b/src/functions/public/Codespaces/Stop-GitHubCodespace.ps1 @@ -0,0 +1,74 @@ +function Stop-GitHubCodespace { + <# + .SYNOPSIS + Stop a codespace. + + .PARAMETER Organization + The organization name. The name is not case sensitive. + + .PARAMETER User + The handle for the GitHub user account. + + .PARAMETER Name + The name of the codespace. + + .PARAMETER Wait + If present will wait for the codespace to stop. + + .EXAMPLE + > Stop-GitHubCodespace -Name urban-dollop-pqxgrq55v4c97g4 + + .EXAMPLE + > Stop-GitHubCodespace -User fake_user_name -Name urban-dollop-pqxgrq55v4c97g4 + + .OUTPUTS + [PSObject[]] + + .LINK + https://docs.github.com/en/rest/codespaces/codespaces?apiVersion=2022-11-28#stop-a-codespace-for-the-authenticated-user + + .LINK + https://docs.github.com/en/rest/codespaces/organizations?apiVersion=2022-11-28#stop-a-codespace-for-an-organization-user + #> + [CmdletBinding(DefaultParameterSetName = 'User', SupportsShouldProcess)] + param ( + [Parameter(ParameterSetName = 'Organization', Mandatory)] + [string]$Organization, + [Parameter(ParameterSetName = 'Organization', Mandatory)] + [string]$User, + + [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)] + [string]$Name, + + [switch]$Wait, + + # 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()] + [object] $Context = (Get-GitHubContext) + ) + process { + if ($PSCmdLet.ShouldProcess( + "Stopping GitHub codespace [$Name]", + "Are you sure you want to stop GitHub codespace $($Name)?", + 'Stop codespace' + )) { + $postParams = @{ + APIEndpoint = $PSCmdlet.ParameterSetName -eq 'Organization' ? + "/orgs/$Organization/members/$User/codespaces/$Name/stop" : + "/user/codespaces/$Name/stop" + Context = $Context + Method = 'POST' + } + $codespace = Invoke-GitHubAPI @postParams | Select-Object -ExpandProperty Response + # | Add-ObjectDetail -TypeName GitHub.Codespace -DefaultProperties name, display_name, location, state, created_at, updated_at, last_used_at + if ($Wait.IsPresent) { + $getParams = $PSCmdlet.ParameterSetName -eq 'Organization' ? + @{ Organization = $Organization; User = $User } : + @{ Name = $Name } + $codespace = Wait-GitHubCodespaceAction -GetParameters $getParams + } + $codespace + } + } +} diff --git a/src/functions/public/Codespaces/Test-GitHubCodespace.ps1 b/src/functions/public/Codespaces/Test-GitHubCodespace.ps1 new file mode 100644 index 000000000..aa801fcd4 --- /dev/null +++ b/src/functions/public/Codespaces/Test-GitHubCodespace.ps1 @@ -0,0 +1,73 @@ +function Test-GitHubCodespace { + <# + .SYNOPSIS + Determines whether a GitHub codespace exists. + + .PARAMETER Organization + The organization name. The name is not case sensitive. + + .PARAMETER User + The handle for the GitHub user account. + + .PARAMETER Owner + The account owner of the repository. The name is not case sensitive. + + .PARAMETER Repository + The name of the repository. The name is not case sensitive. + + .PARAMETER Name + The name of the codespace. + + .EXAMPLE + > Test-GitHubCodespace -Name urban-dollop-pqxgrq55v4c97g4 + + False + + .OUTPUTS + [bool] + + .LINK + https://docs.github.com/en/rest/codespaces/codespaces?apiVersion=2022-11-28#about-github-codespaces + + .LINK + https://docs.github.com/en/rest/codespaces/organizations?apiVersion=2022-11-28#list-codespaces-for-a-user-in-organization + #> + [CmdletBinding(DefaultParameterSetName = 'Scope')] + [OutputType([bool])] + param ( + [Parameter(ParameterSetName = 'Organization', Mandatory )] + [string]$Organization, + [Parameter(ParameterSetName = 'Organization')] + [string]$User, + + [Parameter(ParameterSetName = 'Repository', Mandatory)] + [string]$Owner, + + [Parameter(ParameterSetName = 'Repository', Mandatory)] + [string]$Repository, + + [Parameter(ParameterSetName = 'Name', Mandatory)] + [string]$Name, + + [Parameter(ParameterSetName = 'Scope')] + [ValidateSet('Organization','User')] + [string]$Scope='User', + + # 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()] + [object] $Context = (Get-GitHubContext) + ) + process { + try { + $codespace = Get-GitHubCodespace @PSBoundParameters + [bool]$codespace.id + } catch { + $false + # This part doesn't work as intended because of the error handling in Invoke-GitHubAPI :( + # if (404 -ne $_.Exception.Response.StatusCode.value__) { + # throw + # } + } + } +} diff --git a/src/functions/public/Codespaces/Test-IsCodespace.ps1 b/src/functions/public/Codespaces/Test-IsCodespace.ps1 new file mode 100644 index 000000000..06ac26159 --- /dev/null +++ b/src/functions/public/Codespaces/Test-IsCodespace.ps1 @@ -0,0 +1,6 @@ +function Test-IsCodespace { + [CmdletBinding()] + [OutputType([bool])] + param () + -not ($null -eq $env:CODESPACES) +} diff --git a/src/functions/public/Codespaces/Wait-GitHubCodespaceAction.ps1 b/src/functions/public/Codespaces/Wait-GitHubCodespaceAction.ps1 new file mode 100644 index 000000000..a05ea730d --- /dev/null +++ b/src/functions/public/Codespaces/Wait-GitHubCodespaceAction.ps1 @@ -0,0 +1,39 @@ +function Wait-GitHubCodespaceAction { + <# + .SYNOPSIS + Waits for a codespace action to complete. + + .DESCRIPTION + Polls using Get-GitHubCodespace every 5 seconds until the codespace state -notmatch 'ing' + + .PARAMETER GetParameters + Hashtable of parameters to splat for Get-GitHubCodespace polling. + + .EXAMPLE + > Wait-GitHubCodespaceAction -GetParameters @{ Name='urban-dollop-pqxgrq55v4c97g4' } + + .OUTPUTS + [PSObject] + #> + [CmdletBinding()] + param ( + [Parameter(Mandatory)] + [hashtable]$GetParameters + ) + process { + # Expected states for happy paths: + # Shutdown > Queued > Starting > Available + # Available > Queued > ShuttingDown > ShutDown + # + # To allow for unexpected results, loop until the state is something other than Queued or *ing + # All known states: + # *ings: Awaiting, Exporting, Provisioning, Rebuilding, ShuttingDown, Starting, Updating + # Other: Archived, Available, Created, Deleted, Failed, Moved, Queued, Shutdown, Unavailable, Unknown + do { + Start-Sleep -Seconds 5 + $_codespace = Get-GitHubCodespace @GetParameters + Write-Debug ($_codespace | Out-String) + } until($_codespace.state -notmatch 'Queued|ing') + $_codespace + } +} diff --git a/src/functions/public/Codespaces/Wait-GitHubCodespaceExport.ps1 b/src/functions/public/Codespaces/Wait-GitHubCodespaceExport.ps1 new file mode 100644 index 000000000..bc89b7523 --- /dev/null +++ b/src/functions/public/Codespaces/Wait-GitHubCodespaceExport.ps1 @@ -0,0 +1,47 @@ +function Wait-GitHubCodespaceExport { + <# + .SYNOPSIS + Waits for a codespace export to complete. + + .DESCRIPTION + Polls using Get-GitHubCodespaceExport every 5 seconds until the export state is -ne `in_progress` + + .PARAMETER Id + The ID of the export operation. + + .PARAMETER Name + The name of the codespace. + + .EXAMPLE + > Wait-GitHubCodespace -Name urban-dollop-pqxgrq55v4c97g4 + + + .OUTPUTS + [PSObject[]] + + .LINK + https://docs.github.com/en/rest/codespaces/codespaces?apiVersion=2022-11-28#export-a-codespace-for-the-authenticated-user + #> + [CmdletBinding()] + param ( + [Parameter()] + [string]$Id='latest', + + [Parameter(Mandatory)] + [string]$Name, + + # 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()] + [object] $Context = (Get-GitHubContext) + ) + process { + $_export = Get-GitHubCodespaceExport @PSBoundParameters + while ($_export.state -eq 'in_progress') { + $_export = Get-GitHubCodespaceExport -Name $Name + Write-Debug ($_export | Out-String) + Start-Sleep -Seconds 5 + } + $_export + } +} From f39f04efe222fd8e458192991af6de1d4139d722 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions@users.noreply.github.com> Date: Tue, 4 Feb 2025 10:44:39 +0000 Subject: [PATCH 2/2] Auto-generated changes --- Coverage.md | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/Coverage.md b/Coverage.md index 8b23f278b..9be081a39 100644 --- a/Coverage.md +++ b/Coverage.md @@ -9,15 +9,15 @@ Covered functions - 160 + 180 Missing functions - 855 + 835 Coverage - 15.76% + 17.73% @@ -146,9 +146,9 @@ | `/orgs/{org}/code-security/configurations/{configuration_id}/attach` | | | | :x: | | | `/orgs/{org}/code-security/configurations/{configuration_id}/defaults` | | | | | :x: | | `/orgs/{org}/code-security/configurations/{configuration_id}/repositories` | | :x: | | | | -| `/orgs/{org}/codespaces` | | :x: | | | | -| `/orgs/{org}/codespaces/access` | | | | | :x: | -| `/orgs/{org}/codespaces/access/selected_users` | :x: | | | :x: | | +| `/orgs/{org}/codespaces` | | :white_check_mark: | | | | +| `/orgs/{org}/codespaces/access` | | | | | :white_check_mark: | +| `/orgs/{org}/codespaces/access/selected_users` | :white_check_mark: | | | :white_check_mark: | | | `/orgs/{org}/codespaces/secrets` | | :x: | | | | | `/orgs/{org}/codespaces/secrets/public-key` | | :x: | | | | | `/orgs/{org}/codespaces/secrets/{secret_name}` | :x: | :x: | | | :x: | @@ -193,10 +193,10 @@ | `/orgs/{org}/invitations/{invitation_id}/teams` | | :x: | | | | | `/orgs/{org}/issues` | | :x: | | | | | `/orgs/{org}/members` | | :white_check_mark: | | | | -| `/orgs/{org}/members/{username}` | :x: | :x: | | | | -| `/orgs/{org}/members/{username}/codespaces` | | :x: | | | | +| `/orgs/{org}/members/{username}` | :x: | :white_check_mark: | | | | +| `/orgs/{org}/members/{username}/codespaces` | | :white_check_mark: | | | | | `/orgs/{org}/members/{username}/codespaces/{codespace_name}` | :x: | | | | | -| `/orgs/{org}/members/{username}/codespaces/{codespace_name}/stop` | | | | :x: | | +| `/orgs/{org}/members/{username}/codespaces/{codespace_name}/stop` | | | | :white_check_mark: | | | `/orgs/{org}/members/{username}/copilot` | | :x: | | | | | `/orgs/{org}/memberships/{username}` | :x: | :x: | | | :x: | | `/orgs/{org}/migrations` | | :x: | | :x: | | @@ -383,8 +383,8 @@ | `/repos/{owner}/{repo}/code-scanning/sarifs/{sarif_id}` | | :x: | | | | | `/repos/{owner}/{repo}/code-security-configuration` | | :x: | | | | | `/repos/{owner}/{repo}/codeowners/errors` | | :white_check_mark: | | | | -| `/repos/{owner}/{repo}/codespaces` | | :x: | | :x: | | -| `/repos/{owner}/{repo}/codespaces/devcontainers` | | :x: | | | | +| `/repos/{owner}/{repo}/codespaces` | | :white_check_mark: | | :white_check_mark: | | +| `/repos/{owner}/{repo}/codespaces/devcontainers` | | :white_check_mark: | | | | | `/repos/{owner}/{repo}/codespaces/machines` | | :x: | | | | | `/repos/{owner}/{repo}/codespaces/new` | | :x: | | | | | `/repos/{owner}/{repo}/codespaces/permissions_check` | | :x: | | | | @@ -511,13 +511,13 @@ | `/repos/{owner}/{repo}/private-vulnerability-reporting` | :white_check_mark: | :x: | | | :white_check_mark: | | `/repos/{owner}/{repo}/projects` | | :x: | | :x: | | | `/repos/{owner}/{repo}/properties/values` | | :white_check_mark: | :x: | | | -| `/repos/{owner}/{repo}/pulls` | | :white_check_mark: | | :x: | | +| `/repos/{owner}/{repo}/pulls` | | :white_check_mark: | | :white_check_mark: | | | `/repos/{owner}/{repo}/pulls/comments` | | :x: | | | | | `/repos/{owner}/{repo}/pulls/comments/{comment_id}` | :x: | :x: | :x: | | | | `/repos/{owner}/{repo}/pulls/comments/{comment_id}/reactions` | | :x: | | :x: | | | `/repos/{owner}/{repo}/pulls/comments/{comment_id}/reactions/{reaction_id}` | :x: | | | | | | `/repos/{owner}/{repo}/pulls/{pull_number}` | | :x: | :x: | | | -| `/repos/{owner}/{repo}/pulls/{pull_number}/codespaces` | | | | :x: | | +| `/repos/{owner}/{repo}/pulls/{pull_number}/codespaces` | | | | :white_check_mark: | | | `/repos/{owner}/{repo}/pulls/{pull_number}/comments` | | :x: | | :x: | | | `/repos/{owner}/{repo}/pulls/{pull_number}/comments/{comment_id}/replies` | | | | :x: | | | `/repos/{owner}/{repo}/pulls/{pull_number}/commits` | | :x: | | | | @@ -606,19 +606,19 @@ | `/user` | | :white_check_mark: | :white_check_mark: | | | | `/user/blocks` | | :white_check_mark: | | | | | `/user/blocks/{username}` | :white_check_mark: | :white_check_mark: | | | :white_check_mark: | -| `/user/codespaces` | | :x: | | :x: | | +| `/user/codespaces` | | :white_check_mark: | | :white_check_mark: | | | `/user/codespaces/secrets` | | :x: | | | | | `/user/codespaces/secrets/public-key` | | :x: | | | | | `/user/codespaces/secrets/{secret_name}` | :x: | :x: | | | :x: | | `/user/codespaces/secrets/{secret_name}/repositories` | | :x: | | | :x: | | `/user/codespaces/secrets/{secret_name}/repositories/{repository_id}` | :x: | | | | :x: | -| `/user/codespaces/{codespace_name}` | :x: | :x: | :x: | | | -| `/user/codespaces/{codespace_name}/exports` | | | | :x: | | -| `/user/codespaces/{codespace_name}/exports/{export_id}` | | :x: | | | | +| `/user/codespaces/{codespace_name}` | :x: | :white_check_mark: | :white_check_mark: | | | +| `/user/codespaces/{codespace_name}/exports` | | | | :white_check_mark: | | +| `/user/codespaces/{codespace_name}/exports/{export_id}` | | :white_check_mark: | | | | | `/user/codespaces/{codespace_name}/machines` | | :x: | | | | | `/user/codespaces/{codespace_name}/publish` | | | | :x: | | -| `/user/codespaces/{codespace_name}/start` | | | | :x: | | -| `/user/codespaces/{codespace_name}/stop` | | | | :x: | | +| `/user/codespaces/{codespace_name}/start` | | | | :white_check_mark: | | +| `/user/codespaces/{codespace_name}/stop` | | | | :white_check_mark: | | | `/user/docker/conflicts` | | :x: | | | | | `/user/email/visibility` | | | :white_check_mark: | | | | `/user/emails` | :white_check_mark: | :white_check_mark: | | :white_check_mark: | |