From 40106fd0d2e5b6c1c149da262565f442ed112e74 Mon Sep 17 00:00:00 2001 From: rulasg Date: Tue, 4 Feb 2025 17:54:24 +0100 Subject: [PATCH] Add functions for managing Octodemo demo issues and codespaces, including removal and creation capabilities --- README.md | 55 +++++++ private/importMyModule.ps1 | 49 ++++++ public/OctodemoDemoIssues/getdemoissues.ps1 | 91 ----------- public/codespaces/codespaces.ps1 | 132 +++++++++++++++ public/globals.ps1 | 3 + public/issues/closedemoissues.ps1 | 29 ++++ public/issues/getdemoissues.ps1 | 169 ++++++++++++++++++++ 7 files changed, 437 insertions(+), 91 deletions(-) create mode 100644 private/importMyModule.ps1 delete mode 100644 public/OctodemoDemoIssues/getdemoissues.ps1 create mode 100644 public/codespaces/codespaces.ps1 create mode 100644 public/globals.ps1 create mode 100644 public/issues/closedemoissues.ps1 create mode 100644 public/issues/getdemoissues.ps1 diff --git a/README.md b/README.md index 2374c1a..ddd1f52 100644 --- a/README.md +++ b/README.md @@ -2,4 +2,59 @@ A powershell module that will hold Powershell functionality. +## Use cases + +### Create a Octodemo project issue + +Use bootstrap template to create a new issue in the Octodemo project. +We have not created automation to create a new issue in the Octodemo project yet. + +### List Octodemo project issues + +List all issues in the Octodemo project. + +```powershell +> Get-OctodemoDemos + +Title : Demo :: octodemo/animated-octo-carnival :: copilot_nodejs_basic_v1.5 +Repository : octodemo/bootstrap +Url : https://github.com/octodemo/bootstrap/issues/1086 +Repo : octodemo/animated-octo-carnival +RepoUrl : https://github.com/octodemo/animated-octo-carnival +Labels : template , demo , demo::provisioned +``` +### Create Codespaces for each demo + +Create a Codespace for each demo in the Octodemo project. + +```powershell +> Get-OctodemoDemos | New-LabCodespace + ✓ Codespaces usage for this repository is paid for by octodemo +``` +### Check if the octodemo demos have a Codespace + +Check if the octodemo demos have a Codespace. + +```powershell +> Get-OctodemoDemos -codespacesInfo + +Title : Demo :: octodemo/animated-octo-carnival :: copilot_nodejs_basic_v1.5 +Repository : octodemo/bootstrap +Url : https://github.com/octodemo/bootstrap/issues/1086 +Repo : octodemo/animated-octo-carnival +RepoUrl : https://github.com/octodemo/animated-octo-carnival +Labels : template , demo , demo::provisioned +CodespaceDisplayName : redesigned waffle +CodespaceName : redesigned-waffle-gwrv5v5vvxcvv5g +``` +### Remove Octodemo demo issues + +Remove all issues in the Octodemo issue to destroy the demos. + +```powershell +Get-OctodemoDemos | Remove-OctodemoDemo +``` + +This command will close the issue to have the demo removed and removes the Codespace associated with the demo. + > Module generated with [TestingHelper Powershell Module](https://www.powershellgallery.com/packages/TestingHelper/) diff --git a/private/importMyModule.ps1 b/private/importMyModule.ps1 new file mode 100644 index 0000000..ce4fdf6 --- /dev/null +++ b/private/importMyModule.ps1 @@ -0,0 +1,49 @@ +# function Import-DemoHelper{ + +# ## Check if demohelper avaialble in the peer folder + +# $local = $PSScriptRoot | Split-Path -Parent + +# $sideBySide = $local | Split-Path -Parent | Join-Path -ChildPath "DemoHelper" + +# if(Test-Path -path $sideBySide){ +# Import-Module $sideBySide -Force +# } else { +# Import-MyModule -Name Import-DemoHelper -PassThru +# } + + +# } + + +# function Import-DependencyModule{ +# [CmdletBinding()] +# param ( +# [Parameter(Mandatory)][string]$Name, +# [Parameter()][string]$Version, +# [Parameter()][switch]$AllowPrerelease, +# [Parameter()][switch]$PassThru +# ) + +# if ($Version) { +# $V = $Version.Split('-') +# $semVer = $V[0] +# $AllowPrerelease = ($AllowPrerelease -or ($null -ne $V[1])) +# } + +# # Check if module is already installed. Use the version parameter transformation +# $module = Import-Module -Name $Name -PassThru -ErrorAction SilentlyContinue -RequiredVersion:$semVer -Verbose + +# # If module not installed, install it +# if ($null -eq $module) { +# # Install module from PowershellGallery +# $installed = Install-Module -Name $Name -Force -PassThru -AllowPrerelease:$AllowPrerelease -RequiredVersion:$Version -Verbose + +# # Now install the module that we have just installed +# $module = Import-Module -Name $installed.Name -Force -PassThru -RequiredVersion ($installed.Version.Split('-')[0]) -Verbose +# } + +# if ($PassThru) { +# $module +# } +# } \ No newline at end of file diff --git a/public/OctodemoDemoIssues/getdemoissues.ps1 b/public/OctodemoDemoIssues/getdemoissues.ps1 deleted file mode 100644 index c280a4d..0000000 --- a/public/OctodemoDemoIssues/getdemoissues.ps1 +++ /dev/null @@ -1,91 +0,0 @@ - <# - .SYNOPSIS - Retrieves open issues from a repository that were created by the current user. - - .DESCRIPTION - This function uses the GitHub CLI to search for open issues in a specified repository - that were created by the current user. - - .PARAMETER Org - The organization name. Default is 'octodemo'. - - .PARAMETER Repo - The repository name. Default is 'bootstrap'. - - .EXAMPLE - Get-OctodemoIssues -Org 'octodemo' -Repo 'bootstrap' - #> - function Get-OctodemoIssues { - [CmdletBinding()] - param( - [string]$Org = "octodemo", - [string]$Repo = "bootstrap" - ) - $command = "gh search issues repo:$Org/$Repo author:@me is:open --json title,repository,url" - $json = Invoke-Expression $command - - $json | Write-Verbose - - $issues = $json | ConvertFrom-Json -Depth 10 - - $ret = $issues | ForEach-Object { - - $repoInfo = Get-OctodemoRepoFromIssue -url $_.url - - [PSCustomObject]@{ - Title = $_.title - Repository = $_.repository.nameWithOwner - Url = $_.url - Repo = "$($repoInfo.Owner)/$($repoInfo.Repo)" - RepoUrl = $repoInfo.Url - } - } - - $ret | Write-Verbose - - return $ret - - } Export-ModuleMember -Function 'Get-OctodemoIssues' - -function Get-OctodemoRepoFromIssue{ - [CmdletBinding()] - param( - [string]$url - ) - $json = gh issue view $url --comments --json comments - - $issue = $json | ConvertFrom-Json -Depth 10 - - # last comment - $comment = $issue.comments[-1].body - - $lines = $comment -split "`n" - - $line = $lines | Select-String -Pattern "Demo repository" - - $ret = Get-RepoInfoFromString -inputString $line - - return $ret -} - -function Get-RepoInfoFromString{ - [CmdletBinding()] - param( - [string]$inputString - ) - # Extract the owner, repo, and url from the input string - $pattern = '\[(?[^\/]+)\/(?[^\]]+)\]\((?[^)]+)\)' - $match = [regex]::Matches($inputString, $pattern) - if ($match.Count -gt 0) { - $owner = $match[0].Groups['owner'].Value - $repo = $match[0].Groups['repo'].Value - $url = $match[0].Groups['url'].Value - return [PSCustomObject]@{ - Owner = $owner - Repo = $repo - Url = $url - } - } else { - Write-Error "No match found" - } -} Export-ModuleMember -Function 'Get-RepoInfoFromString' \ No newline at end of file diff --git a/public/codespaces/codespaces.ps1 b/public/codespaces/codespaces.ps1 new file mode 100644 index 0000000..f66d7ac --- /dev/null +++ b/public/codespaces/codespaces.ps1 @@ -0,0 +1,132 @@ +<# +.SYNOPSIS +Creates a codespace in the main branch of a specified GitHub repository. + +.DESCRIPTION +The `New-Codespace` function creates a new codespace in the main branch of a specified GitHub repository using the GitHub CLI (`gh`). + +.PARAMETER Repo +The repository name with the owner in the format 'owner/repo'. + +.EXAMPLE +New-Codespace -RepoWithOwner 'octodemo/mushy-chainsaw' +This example creates a codespace in the main branch of the 'octodemo/mushy-chainsaw' repository. + +.NOTES +Requires GitHub CLI (gh) to be installed and authenticated. +#> +function New-LabCodespace { + [CmdletBinding()] + param( + [Parameter(Position=0,ValueFromPipelineByPropertyName)][string]$Repo, + [Parameter(Position=1)][string]$DisplayName, + [switch]$CheckIfExists + ) + + begin{ + + if($CheckIfExists){ + $codespaces = Get-LabCodespaces + } + } + + process{ + + if ($CheckIfExists ) { + + # Check if repo name starts with $LAB_ORG + $orgName, $repoName = $Repo -split '/' + if ($orgName -ne $LAB_ORG) { + Write-Error "Repository $Repo does not belong to organization $LAB_ORG" + return + } + + # check if codespace already exists + $existingCodespace = $codespaces | Where-Object { $_.Repository -eq $Repo } + if ($existingCodespace) { + Write-Warning "Codespace in repository $Repo already exists" + return + } + } + + "Creating codespace in the main branch of $Repo" | Write-Verbose + + $ghCommand = "gh cs create -b main -m 'standardLinux32gb' -R $Repo" + if ($PSCmdlet.MyInvocation.BoundParameters.ContainsKey('DisplayName')) { + $ghCommand += " --display-name '$DisplayName'" + } + + $result = Invoke-Expression $ghCommand + + $result | Write-Verbose + + return $result + } + +} Export-ModuleMember -Function 'New-LabCodespace' + + +function Get-LabCodespaces { + [CmdletBinding()] + param( + [Parameter(Position=0)][string]$owner = 'octodemo' + ) + + "Getting codespaces in the $Repo repository" | Write-Verbose + + $json = gh cs list --json name,displayName,owner,repository + + $json | Write-Verbose + + $codespaces = $json | ConvertFrom-Json -Depth 10 + + $result = $codespaces | Where-Object { $_.repository -Like "$owner/*" } | ForEach-Object { + [PSCustomObject]@{ + Name = $_.name + DisplayName = $_.displayName + Owner = $_.owner + Repository = $_.repository + } + } + + $result | Write-Verbose + + return $result +} Export-ModuleMember -Function 'Get-LabCodespaces' + +function Remove-LabCodespace{ + [CmdletBinding(SupportsShouldProcess)] + param( + [Parameter(Mandatory,ValueFromPipelineByPropertyName,Position=0)] + [Alias("CodespaceName")] + [string]$Name, + [Parameter()][object]$CodespaceList + ) + + begin{ + $codespacesList ??= Get-LabCodespaces + + $codespacesList | Write-Verbose + } + + process{ + + $command = "gh codespace delete --codespace $Name --force" + + $codespacesList ??= Get-LabCodespaces + + $codespace = $codespacesList | Where-Object { $_.Name -eq $Name } + + if($codespace){ + if ($PSCmdlet.ShouldProcess($Command)) { + "Removing codespace $Name" | Write-Verbose + $result = Invoke-Expression $command + $result | Write-Output + "Codespace $Name removed" | Write-Host + } + } else { + "Codespace $Name not found" | Write-Host + } + + } +} Export-ModuleMember -Function 'Remove-LabCodespace' \ No newline at end of file diff --git a/public/globals.ps1 b/public/globals.ps1 new file mode 100644 index 0000000..780c87a --- /dev/null +++ b/public/globals.ps1 @@ -0,0 +1,3 @@ + + +$LAB_ORG= "octodemo" \ No newline at end of file diff --git a/public/issues/closedemoissues.ps1 b/public/issues/closedemoissues.ps1 new file mode 100644 index 0000000..f85ac15 --- /dev/null +++ b/public/issues/closedemoissues.ps1 @@ -0,0 +1,29 @@ +function Remove-OctodemoDemo{ + [CmdletBinding(SupportsShouldProcess)] + param( + [Parameter(Mandatory,ValueFromPipelineByPropertyName,Position=0)][string]$Url, + [Parameter(ValueFromPipelineByPropertyName,Position=0)][string]$CodespaceName + ) + + process{ + + $command = "gh issue close $Url" + + if ($PSCmdlet.ShouldProcess($Command)) { + $result = Invoke-Expression $command + } + + $result | Write-Output + + if($CodespaceName){ + if ($PSCmdlet.ShouldProcess($CodespaceName)) { + Remove-LabCodespace -Name $CodespaceName + } else { + Remove-LabCodespace -Name $CodespaceName -WhatIf + } + } + + + } + +} Export-ModuleMember -Function Remove-OctodemoDemo \ No newline at end of file diff --git a/public/issues/getdemoissues.ps1 b/public/issues/getdemoissues.ps1 new file mode 100644 index 0000000..6e740fa --- /dev/null +++ b/public/issues/getdemoissues.ps1 @@ -0,0 +1,169 @@ +<# + .SYNOPSIS + Retrieves open issues from a repository that were created by the current user. + + .DESCRIPTION + This function uses the GitHub CLI to search for open issues in a specified repository + that were created by the current user. + + .PARAMETER Org + The organization name. Default is 'octodemo'. + + .PARAMETER Repo + The repository name. Default is 'bootstrap'. + + .EXAMPLE + Get-OctodemoDemos -Org 'octodemo' -Repo 'bootstrap' +#> +function Get-OctodemoDemos { + [CmdletBinding()] + param( + [string]$Org = "octodemo", + [string]$Repo = "bootstrap", + [string]$Title, + [switch]$Web, + [switch]$OpenRepos, + [switch]$codespacesInfo + ) + + if($Web){ + + Open-OctodemoMyIssues -Org $Org -Repo $Repo + return + } + + $command = "gh search issues repo:$Org/$Repo author:@me is:open --json title,repository,url,labels" + $json = Invoke-Expression $command + + $json | Write-Verbose + + $issues = $json | ConvertFrom-Json -Depth 10 + + if ($Title) { + $issues = $issues | Where-Object { $_.title -like "*$Title*" } + } + + if($codespacesInfo){ + $codespacesList = Get-LabCodespaces + } + + $ret = $issues | ForEach-Object { + + $labels = $_.labels.name -join " , " + + $repoInfo = Get-OctodemoRepoFromIssue -url $_.url + + if ($repoInfo){ + $envRepo = "$($repoInfo.Owner)/$($repoInfo.Repo)" + $envRepoUrl = $repoInfo.Url + + # Open the repository in the browser if $openRepos is set + if($OpenRepos){ + Open-Url -url $envRepoUrl + } + } + + $customObject = [PSCustomObject]@{ + Title = $_.title + Repository = $_.repository.nameWithOwner + Url = $_.url + Repo = $envRepo + RepoUrl = $envRepoUrl + Labels = $labels + } + + if ($codespacesList -and $envRepo) { + $codespace = $codespacesList | Where-Object { $_.Repository -eq $envRepo } + if ($codespace) { + $customObject | Add-Member -MemberType NoteProperty -Name CodespaceDisplayName -Value $codespace.DisplayName + $customObject | Add-Member -MemberType NoteProperty -Name CodespaceName -Value $codespace.Name + } + } + + $customObject + } + + $ret | Write-Verbose + + return $ret + +} Export-ModuleMember -Function 'Get-OctodemoDemos' + +function Get-OctodemoRepoFromIssue{ + [CmdletBinding()] + param( + [string]$url + ) + $json = gh issue view $url --comments --json comments + + $issue = $json | ConvertFrom-Json -Depth 10 + + # last comment + $comment = $issue.comments[-1].body + + $lines = $comment -split "`n" + + $line = $lines | Select-String -Pattern "Demo repository" + + #if line is null return null + if($null -eq $line){ + return $null + } + + $ret = Get-RepoInfoFromString -inputString $line + + return $ret +} + +function Get-RepoInfoFromString{ + [CmdletBinding()] + param( + [string]$inputString + ) + # Extract the owner, repo, and url from the input string + $pattern = '\[(?[^\/]+)\/(?[^\]]+)\]\((?[^)]+)\)' + $match = [regex]::Matches($inputString, $pattern) + if ($match.Count -gt 0) { + $owner = $match[0].Groups['owner'].Value + $repo = $match[0].Groups['repo'].Value + $url = $match[0].Groups['url'].Value + return [PSCustomObject]@{ + Owner = $owner + Repo = $repo + Url = $url + } + } else { + Write-Error "No match found" + } +} Export-ModuleMember -Function 'Get-RepoInfoFromString' + +function Open-OctodemoMyIssues{ + [CmdletBinding()] + param( + [string]$Org = "octodemo", + [string]$Repo = "bootstrap" + ) + + $url = "https://github.com/{org}/{repo}/issues?q=is%3Aissue%20state%3Aopen%20author%3A%40me" + $url = $url -replace "{org}", $Org + $url = $url -replace "{repo}", $Repo + + Open-Url $url +} Export-ModuleMember -Function 'Open-OctodemoMyIssues' + +function Open-Url{ + [CmdletBinding()] + param( + [Parameter(Position=0)][string]$url + ) + + if ($IsWindows) { + Start-Process $url + } elseif ($IsMacOS) { + open $url + } elseif ($IsLinux) { + xdg-open $url + } else { + Write-Error "Unsupported platform" + } +} Export-ModuleMember -Function 'Open-Url' \ No newline at end of file