From d24f9fe8184a03149843cb79c88f531c274ed97d Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Mon, 10 Feb 2025 14:56:35 +0100 Subject: [PATCH 1/5] =?UTF-8?q?=F0=9F=A9=B9=20[Patch]:=20Update=20module?= =?UTF-8?q?=20dependencies=20and=20add=20Get-GitHubRepositoryContent=20fun?= =?UTF-8?q?ction=20with=20tests?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/functions/public/API/Invoke-GitHubAPI.ps1 | 4 +- .../Contents/Get-GitHubRepositoryContent.ps1 | 91 +++++++++++++++++++ tests/GitHub.Tests.ps1 | 53 +++++++++++ tools/utilities/Get-GitHubAPIDescriptor.ps1 | 72 +++++++++++++++ tools/utilities/New-FunctionTemplate.ps1 | 49 +++------- 5 files changed, 230 insertions(+), 39 deletions(-) create mode 100644 src/functions/public/Repositories/Contents/Get-GitHubRepositoryContent.ps1 create mode 100644 tools/utilities/Get-GitHubAPIDescriptor.ps1 diff --git a/src/functions/public/API/Invoke-GitHubAPI.ps1 b/src/functions/public/API/Invoke-GitHubAPI.ps1 index 797463741..3e5e9fcb4 100644 --- a/src/functions/public/API/Invoke-GitHubAPI.ps1 +++ b/src/functions/public/API/Invoke-GitHubAPI.ps1 @@ -1,4 +1,4 @@ -#Requires -Modules @{ ModuleName = 'Web'; RequiredVersion = '1.0.0' } +#Requires -Modules @{ ModuleName = 'Uri'; RequiredVersion = '1.1.0' } filter Invoke-GitHubAPI { <# @@ -164,7 +164,7 @@ filter Invoke-GitHubAPI { Write-Debug "Setting per_page to the default value in context [$($Context.PerPage)]." $Body['per_page'] = $Context.PerPage } - $queryString = $Body | ConvertTo-WebQueryString + $queryString = $Body | ConvertTo-UriQueryString $APICall.Uri = $APICall.Uri + $queryString } elseif ($Body -is [string]) { # Use body to create the form data diff --git a/src/functions/public/Repositories/Contents/Get-GitHubRepositoryContent.ps1 b/src/functions/public/Repositories/Contents/Get-GitHubRepositoryContent.ps1 new file mode 100644 index 000000000..26980d48b --- /dev/null +++ b/src/functions/public/Repositories/Contents/Get-GitHubRepositoryContent.ps1 @@ -0,0 +1,91 @@ +function Get-GitHubRepositoryContent { + <# + .SYNOPSIS + Retrieves the contents of a file or directory from a GitHub repository. + + .DESCRIPTION + This function calls the GitHub API endpoint that returns the contents of a repository. + You can specify a file or directory path using -Path. If you leave -Path empty, + the function will return the repository's root directory contents. + + Optionally, you can supply a specific commit, branch, or tag via -Ref. + The function relies on the provided GitHub context for authentication and configuration. + + .EXAMPLE + Get-GitHubRepositoryContent -Owner "octocat" -Repo "Hello-World" -Path "README.md" -Ref "main" + + Output: + ```powershell + { + "name": "README.md", + "path": "README.md", + "sha": "123abc456def", + "size": 1256, + "url": "https://api.github.com/repos/octocat/Hello-World/contents/README.md", + "type": "file" + } + ``` + + Retrieves the README.md file from the main branch of the repository. + + .OUTPUTS + System.Object. The response object containing details about the repository contents. + + .LINK + https://psmodule.io/GitHub/Functions/Get-GitHubRepositoryContent/ + #> + [CmdletBinding(SupportsShouldProcess)] + param( + # The GitHub account owner. + [Parameter(Mandatory)] + [Alias('Organization')] + [Alias('User')] + [string] $Owner, + + # The name of the repository. + [Parameter(Mandatory)] + [string] $Repository, + + # The file or directory path in the repository. + [Parameter()] + [string] $Path, + + # Optional reference (commit, branch, or tag) to get content from. + [Parameter()] + [string] $Ref, + + # The GitHub context for the API call. + [Parameter()] + [object] $Context = (Get-GitHubContext) + ) + + begin { + $stackPath = Get-PSCallStackPath + Write-Debug "[$stackPath] - Start" + $Context = Resolve-GitHubContext -Context $Context + Assert-GitHubContext -Context $Context -AuthType IAT, PAT, UAT + } + + process { + # Build the query parameters (only ref is supported here). + $body = @{ + ref = $Ref + } + + # Prepare the input for the GitHub API call. + $inputObject = @{ + Method = 'GET' + APIEndpoint = "/repos/$Owner/$Repository/contents/$Path" + Body = $body + Context = $Context + } + + Invoke-GitHubAPI @inputObject | ForEach-Object { + Write-Output $_.Response + } + } + + end { + Write-Debug "[$stackPath] - End" + } +} diff --git a/tests/GitHub.Tests.ps1 b/tests/GitHub.Tests.ps1 index 5fa4a693b..c03588243 100644 --- a/tests/GitHub.Tests.ps1 +++ b/tests/GitHub.Tests.ps1 @@ -1197,4 +1197,57 @@ Describe 'As a GitHub App - Organization (APP_ORG)' { } | Should -Not -Throw } } + Context 'Repository' { + Context 'Content' { + It 'Get-GitHubRepositoryContent - retrieves README.md from main branch' { + $Owner = 'github' + $Repository = 'rest-api-description' + $Path = 'README.md' + $Ref = 'main' + + $Result = Get-GitHubRepositoryContent -Owner $Owner -Repository $Repository -Path $Path -Ref $Ref + + $Result | Should -Not -BeNullOrEmpty + $Result | Should -BeOfType 'System.Object' + $result | Format-List | Out-String -Stream | ForEach-Object { Write-Verbose $_ } + $Result.name | Should -Be 'README.md' + } + } + + Context 'Get-GitHubRepositoryContent - Retrieve repository root contents' { + It 'Get-GitHubRepositoryContent - retrieves root directory contents' { + $Owner = 'github' + $Repository = 'rest-api-description' + + $Result = Get-GitHubRepositoryContent -Owner $Owner -Repository $Repository + $result | Format-List | Out-String -Stream | ForEach-Object { Write-Verbose $_ } + $Result | Should -Not -BeNullOrEmpty + $Result | Should -BeOfType 'System.Object' + } + } + + Context 'Get-GitHubRepositoryContent - Handle invalid path' { + It 'Get-GitHubRepositoryContent - returns error for non-existent file' { + $Owner = 'github' + $Repository = 'rest-api-description' + $Path = 'nonexistentfile.md' + + { Get-GitHubRepositoryContent -Owner $Owner -Repository $Repository -Path $Path } | Should -Throw + } + } + + Context 'Get-GitHubRepositoryContent - Retrieve content from a specific branch' { + It 'Get-GitHubRepositoryContent - retrieves content from a feature branch' { + $Owner = 'github' + $Repository = 'rest-api-description' + $Path = 'README.md' + $Ref = 'feature-branch' + + $Result = Get-GitHubRepositoryContent -Owner $Owner -Repository $Repository -Path $Path -Ref $Ref + $result | Format-List | Out-String -Stream | ForEach-Object { Write-Verbose $_ } + $Result | Should -Not -BeNullOrEmpty + $Result | Should -BeOfType 'System.Object' + } + } + } } diff --git a/tools/utilities/Get-GitHubAPIDescriptor.ps1 b/tools/utilities/Get-GitHubAPIDescriptor.ps1 new file mode 100644 index 000000000..1f387a894 --- /dev/null +++ b/tools/utilities/Get-GitHubAPIDescriptor.ps1 @@ -0,0 +1,72 @@ +### +### List Top-Level Folders and Subdirectories in a GitHub Repository +### + +# Define repository owner and name +$owner = 'github' +$repo = 'rest-api-description' + +# GitHub API URL for root contents +$rootUrl = "https://api.github.com/repos/$owner/$repo/contents" + +# Invoke the REST API to get root contents (add User-Agent header if needed) +$rootContents = Invoke-RestMethod -Uri $rootUrl -Headers @{ 'Accept' = 'application/vnd.github.v3+json' } + +# Filter for directories in the root (type equals "dir") +$topLevelFolders = $rootContents | Where-Object { $_.type -eq 'dir' } | Select-Object -ExpandProperty name + +# Output the top-level folder names +$topLevelFolders + + +### +### List Subdirectories for Each Top Folder: +### + +foreach ($folder in $topLevelFolders) { + Write-Host "`nSubfolders in '$folder':" + # Construct URL for the folder's contents + $folderUrl = "https://api.github.com/repos/$owner/$repo/contents/$folder" + $folderContents = Invoke-RestMethod -Uri $folderUrl -Headers @{ 'Accept' = 'application/vnd.github.v3+json' } + + # Filter for subdirectories (type "dir") within this folder + $subDirs = $folderContents | Where-Object { $_.type -eq 'dir' } | Select-Object -ExpandProperty name + + # Print each subfolder name + $subDirs | ForEach-Object { Write-Host "- $_" } +} + + + +# (Optional) Get entire tree recursively - may return a lot of data for large repos +$branch = 'main' # or specify default branch/commit SHA +$treeUrl = "https://api.github.com/repos/$owner/$repo/git/trees/$branch`?recursive=1" +$treeData = Invoke-RestMethod -Uri $treeUrl -Headers @{ 'Accept' = 'application/vnd.github.v3+json' } + +# $treeData.tree is a list of all paths in the repository (each with type "blob" for file or "tree" for folder). +# We can filter this list for type "tree" to get directories. +$allDirs = $treeData.tree | Where-Object { $_.type -eq 'tree' } | Select-Object -ExpandProperty path + + + +function Get-GitHubAPIDescription { + <# + .SYNOPSIS + Retrieves the GitHub REST API description from the GitHub REST API description repository. + + .DESCRIPTION + Retrieves the GitHub REST API description from the GitHub REST API description repository. + The API description is used to generate the GitHub REST API functions. + + .EXAMPLE + Get-GitHubAPIDescription + #> + + # GitHub REST API description repository + $APIDocURI = 'https://raw.githubusercontent.com/github/rest-api-description/main' + $Bundled = '/descriptions/api.github.com/api.github.com.json' + $APIDocURI = $APIDocURI + $Bundled + $response = Invoke-RestMethod -Uri $APIDocURI -Method Get + + return $response +} diff --git a/tools/utilities/New-FunctionTemplate.ps1 b/tools/utilities/New-FunctionTemplate.ps1 index 5716bf6f0..a02c8c0f3 100644 --- a/tools/utilities/New-FunctionTemplate.ps1 +++ b/tools/utilities/New-FunctionTemplate.ps1 @@ -33,53 +33,28 @@ Write-Debug "[$stackPath] - Start" $Context = Resolve-GitHubContext -Context $Context Assert-GitHubContext -Context $Context -AuthType IAT, PAT, UAT - - if ([string]::IsNullOrEmpty($Enterprise)) { - $Enterprise = $Context.Enterprise - } - Write-Debug "Enterprise : [$($Context.Enterprise)]" - - if ([string]::IsNullOrEmpty($Owner)) { - $Owner = $Context.Owner - } - Write-Debug "Owner : [$($Context.Owner)]" - - if ([string]::IsNullOrEmpty($Repo)) { - $Repo = $Context.Repo - } - Write-Debug "Repo : [$($Context.Repo)]" } process { - try { - $body = @{ - per_page = $PerPage - } + $body = @{ + per_page = $PerPage + } - $inputObject = @{ - Context = $Context - APIEndpoint = "/orgs/$OrganizationName/blocks" - Method = 'GET' - Body = $body - } + $inputObject = @{ + Method = 'GET' + APIEndpoint = "/orgs/$OrganizationName/blocks" + Body = $body + Context = $Context + } - if ($PSCmdlet.ShouldProcess('Target', 'Operation')) { - Invoke-GitHubAPI @inputObject | ForEach-Object { - Write-Output $_.Response - } + if ($PSCmdlet.ShouldProcess('Target', 'Operation')) { + Invoke-GitHubAPI @inputObject | ForEach-Object { + Write-Output $_.Response } - } catch { - Write-Debug "Error: $_" - } finally { - Write-Debug 'Finally' } } end { Write-Debug "[$stackPath] - End" } - - clean { - Write-Debug 'Clean' - } } From 9b1a1e0ee25313de0c5c765246589178d780002c Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions@users.noreply.github.com> Date: Mon, 10 Feb 2025 13:58:09 +0000 Subject: [PATCH 2/5] Auto-generated changes --- Coverage.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Coverage.md b/Coverage.md index 8b23f278b..e19e8c0d6 100644 --- a/Coverage.md +++ b/Coverage.md @@ -9,15 +9,15 @@ Covered functions - 160 + 162 Missing functions - 855 + 853 Coverage - 15.76% + 15.96% @@ -409,7 +409,7 @@ | `/repos/{owner}/{repo}/commits/{ref}/statuses` | | :x: | | | | | `/repos/{owner}/{repo}/community/profile` | | :x: | | | | | `/repos/{owner}/{repo}/compare/{basehead}` | | :x: | | | | -| `/repos/{owner}/{repo}/contents/{path}` | :x: | :x: | | | :x: | +| `/repos/{owner}/{repo}/contents/{path}` | :x: | :white_check_mark: | | | :x: | | `/repos/{owner}/{repo}/contributors` | | :white_check_mark: | | | | | `/repos/{owner}/{repo}/dependabot/alerts` | | :x: | | | | | `/repos/{owner}/{repo}/dependabot/alerts/{alert_number}` | | :x: | :x: | | | @@ -530,7 +530,7 @@ | `/repos/{owner}/{repo}/pulls/{pull_number}/reviews/{review_id}/dismissals` | | | | | :x: | | `/repos/{owner}/{repo}/pulls/{pull_number}/reviews/{review_id}/events` | | | | :x: | | | `/repos/{owner}/{repo}/pulls/{pull_number}/update-branch` | | | | | :x: | -| `/repos/{owner}/{repo}/readme` | | :x: | | | | +| `/repos/{owner}/{repo}/readme` | | :white_check_mark: | | | | | `/repos/{owner}/{repo}/readme/{dir}` | | :x: | | | | | `/repos/{owner}/{repo}/releases` | | :white_check_mark: | | :white_check_mark: | | | `/repos/{owner}/{repo}/releases/assets/{asset_id}` | :white_check_mark: | :white_check_mark: | :white_check_mark: | | | From c48069833d4e2c9c487c85cd520f36c94a5a9808 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Mon, 10 Feb 2025 22:23:59 +0100 Subject: [PATCH 3/5] =?UTF-8?q?=F0=9F=A9=B9=20[Patch]:=20Update=20document?= =?UTF-8?q?ation=20for=20Get-GitHubRepositoryContent=20function=20to=20cla?= =?UTF-8?q?rify=20output=20and=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Repositories/Contents/Get-GitHubRepositoryContent.ps1 | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/functions/public/Repositories/Contents/Get-GitHubRepositoryContent.ps1 b/src/functions/public/Repositories/Contents/Get-GitHubRepositoryContent.ps1 index 26980d48b..38a5363fe 100644 --- a/src/functions/public/Repositories/Contents/Get-GitHubRepositoryContent.ps1 +++ b/src/functions/public/Repositories/Contents/Get-GitHubRepositoryContent.ps1 @@ -29,7 +29,10 @@ Retrieves the README.md file from the main branch of the repository. .OUTPUTS - System.Object. The response object containing details about the repository contents. + System.Object + + .NOTES + The response object containing details about the repository contents. .LINK https://psmodule.io/GitHub/Functions/Get-GitHubRepositoryContent/ From 1bb59b1cdbdff9450358e066703df61c4f92cf25 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Mon, 10 Feb 2025 22:37:12 +0100 Subject: [PATCH 4/5] No code changes made. --- tests/GitHub.Tests.ps1 | 275 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 254 insertions(+), 21 deletions(-) diff --git a/tests/GitHub.Tests.ps1 b/tests/GitHub.Tests.ps1 index be1dd08ed..ecb732ce7 100644 --- a/tests/GitHub.Tests.ps1 +++ b/tests/GitHub.Tests.ps1 @@ -370,10 +370,10 @@ line } Describe 'As a user - Fine-grained PAT token - user account access (USER_FG_PAT)' { - BeforeAll { + BeforeEach { Connect-GitHubAccount -Token $env:TEST_USER_USER_FG_PAT } - AfterAll { + AfterEach { Get-GitHubContext -ListAvailable | Disconnect-GitHubAccount } Context 'Auth' { @@ -467,6 +467,54 @@ Describe 'As a user - Fine-grained PAT token - user account access (USER_FG_PAT) It 'Get-GitHubRepository - Gets all repositories from a user (USER_FG_PAT)' { { Get-GitHubRepository -Username 'MariusStorhaug' } | Should -Not -Throw } + Context 'Content' { + BeforeEach { + Connect-GitHub + } + AfterEach { + Get-GitHubContext -ListAvailable | Disconnect-GitHubAccount + } + It 'Get-GitHubRepositoryContent - retrieves README.md from main branch' { + $Owner = 'github' + $Repository = 'rest-api-description' + $Path = 'README.md' + $Ref = 'main' + + $Result = Get-GitHubRepositoryContent -Owner $Owner -Repository $Repository -Path $Path -Ref $Ref + + $Result | Should -Not -BeNullOrEmpty + $Result | Should -BeOfType 'System.Object' + $result | Format-List | Out-String -Stream | ForEach-Object { Write-Verbose $_ } + $Result.name | Should -Be 'README.md' + } + It 'Get-GitHubRepositoryContent - retrieves root directory contents' { + $Owner = 'github' + $Repository = 'rest-api-description' + + $Result = Get-GitHubRepositoryContent -Owner $Owner -Repository $Repository + $result | Format-List | Out-String -Stream | ForEach-Object { Write-Verbose $_ } + $Result | Should -Not -BeNullOrEmpty + $Result | Should -BeOfType 'System.Object' + } + It 'Get-GitHubRepositoryContent - returns error for non-existent file' { + $Owner = 'github' + $Repository = 'rest-api-description' + $Path = 'nonexistentfile.md' + + { Get-GitHubRepositoryContent -Owner $Owner -Repository $Repository -Path $Path } | Should -Throw + } + It 'Get-GitHubRepositoryContent - retrieves content from a feature branch' { + $Owner = 'github' + $Repository = 'rest-api-description' + $Path = 'README.md' + $Ref = 'feature-branch' + + $Result = Get-GitHubRepositoryContent -Owner $Owner -Repository $Repository -Path $Path -Ref $Ref + $result | Format-List | Out-String -Stream | ForEach-Object { Write-Verbose $_ } + $Result | Should -Not -BeNullOrEmpty + $Result | Should -BeOfType 'System.Object' + } + } } Context 'GitIgnore' { It 'Get-GitHubGitignore - Gets a list of all gitignore templates names (USER_FG_PAT)' { @@ -527,10 +575,10 @@ Describe 'As a user - Fine-grained PAT token - user account access (USER_FG_PAT) } Describe 'As a user - Fine-grained PAT token - organization account access (ORG_FG_PAT)' { - BeforeAll { + BeforeEach { Connect-GitHubAccount -Token $env:TEST_USER_ORG_FG_PAT } - AfterAll { + AfterEach { Get-GitHubContext -ListAvailable | Disconnect-GitHubAccount } Context 'Auth' { @@ -703,13 +751,57 @@ Describe 'As a user - Fine-grained PAT token - organization account access (ORG_ } | Should -Not -Throw } } + Context 'Repository' { + Context 'Content' { + It 'Get-GitHubRepositoryContent - retrieves README.md from main branch' { + $Owner = 'github' + $Repository = 'rest-api-description' + $Path = 'README.md' + $Ref = 'main' + + $Result = Get-GitHubRepositoryContent -Owner $Owner -Repository $Repository -Path $Path -Ref $Ref + + $Result | Should -Not -BeNullOrEmpty + $Result | Should -BeOfType 'System.Object' + $result | Format-List | Out-String -Stream | ForEach-Object { Write-Verbose $_ } + $Result.name | Should -Be 'README.md' + } + It 'Get-GitHubRepositoryContent - retrieves root directory contents' { + $Owner = 'github' + $Repository = 'rest-api-description' + + $Result = Get-GitHubRepositoryContent -Owner $Owner -Repository $Repository + $result | Format-List | Out-String -Stream | ForEach-Object { Write-Verbose $_ } + $Result | Should -Not -BeNullOrEmpty + $Result | Should -BeOfType 'System.Object' + } + It 'Get-GitHubRepositoryContent - returns error for non-existent file' { + $Owner = 'github' + $Repository = 'rest-api-description' + $Path = 'nonexistentfile.md' + + { Get-GitHubRepositoryContent -Owner $Owner -Repository $Repository -Path $Path } | Should -Throw + } + It 'Get-GitHubRepositoryContent - retrieves content from a feature branch' { + $Owner = 'github' + $Repository = 'rest-api-description' + $Path = 'README.md' + $Ref = 'feature-branch' + + $Result = Get-GitHubRepositoryContent -Owner $Owner -Repository $Repository -Path $Path -Ref $Ref + $result | Format-List | Out-String -Stream | ForEach-Object { Write-Verbose $_ } + $Result | Should -Not -BeNullOrEmpty + $Result | Should -BeOfType 'System.Object' + } + } + } } Describe 'As a user - Classic PAT token (PAT)' { - BeforeAll { + BeforeEach { Connect-GitHubAccount -Token $env:TEST_USER_PAT } - AfterAll { + AfterEach { Get-GitHubContext -ListAvailable | Disconnect-GitHubAccount } Context 'Auth' { @@ -855,13 +947,57 @@ Describe 'As a user - Classic PAT token (PAT)' { $members.login | Should -Contain 'psmodule-user' } } + Context 'Repository' { + Context 'Content' { + It 'Get-GitHubRepositoryContent - retrieves README.md from main branch' { + $Owner = 'github' + $Repository = 'rest-api-description' + $Path = 'README.md' + $Ref = 'main' + + $Result = Get-GitHubRepositoryContent -Owner $Owner -Repository $Repository -Path $Path -Ref $Ref + + $Result | Should -Not -BeNullOrEmpty + $Result | Should -BeOfType 'System.Object' + $result | Format-List | Out-String -Stream | ForEach-Object { Write-Verbose $_ } + $Result.name | Should -Be 'README.md' + } + It 'Get-GitHubRepositoryContent - retrieves root directory contents' { + $Owner = 'github' + $Repository = 'rest-api-description' + + $Result = Get-GitHubRepositoryContent -Owner $Owner -Repository $Repository + $result | Format-List | Out-String -Stream | ForEach-Object { Write-Verbose $_ } + $Result | Should -Not -BeNullOrEmpty + $Result | Should -BeOfType 'System.Object' + } + It 'Get-GitHubRepositoryContent - returns error for non-existent file' { + $Owner = 'github' + $Repository = 'rest-api-description' + $Path = 'nonexistentfile.md' + + { Get-GitHubRepositoryContent -Owner $Owner -Repository $Repository -Path $Path } | Should -Throw + } + It 'Get-GitHubRepositoryContent - retrieves content from a feature branch' { + $Owner = 'github' + $Repository = 'rest-api-description' + $Path = 'README.md' + $Ref = 'feature-branch' + + $Result = Get-GitHubRepositoryContent -Owner $Owner -Repository $Repository -Path $Path -Ref $Ref + $result | Format-List | Out-String -Stream | ForEach-Object { Write-Verbose $_ } + $Result | Should -Not -BeNullOrEmpty + $Result | Should -BeOfType 'System.Object' + } + } + } } Describe 'As GitHub Actions (GHA)' { - BeforeAll { + BeforeEach { Connect-GitHubAccount } - AfterAll { + AfterEach { Get-GitHubContext -ListAvailable | Disconnect-GitHubAccount } Context 'Auth' { @@ -987,6 +1123,56 @@ Describe 'As GitHub Actions (GHA)' { { Get-GitHubUser -Username 'Octocat' } | Should -Not -Throw } } + Context 'Repository' { + Context 'Content' { + BeforeEach { + Connect-GitHub + } + AfterEach { + Get-GitHubContext -ListAvailable | Disconnect-GitHubAccount + } + It 'Get-GitHubRepositoryContent - retrieves README.md from main branch' { + $Owner = 'github' + $Repository = 'rest-api-description' + $Path = 'README.md' + $Ref = 'main' + + $Result = Get-GitHubRepositoryContent -Owner $Owner -Repository $Repository -Path $Path -Ref $Ref + + $Result | Should -Not -BeNullOrEmpty + $Result | Should -BeOfType 'System.Object' + $result | Format-List | Out-String -Stream | ForEach-Object { Write-Verbose $_ } + $Result.name | Should -Be 'README.md' + } + It 'Get-GitHubRepositoryContent - retrieves root directory contents' { + $Owner = 'github' + $Repository = 'rest-api-description' + + $Result = Get-GitHubRepositoryContent -Owner $Owner -Repository $Repository + $result | Format-List | Out-String -Stream | ForEach-Object { Write-Verbose $_ } + $Result | Should -Not -BeNullOrEmpty + $Result | Should -BeOfType 'System.Object' + } + It 'Get-GitHubRepositoryContent - returns error for non-existent file' { + $Owner = 'github' + $Repository = 'rest-api-description' + $Path = 'nonexistentfile.md' + + { Get-GitHubRepositoryContent -Owner $Owner -Repository $Repository -Path $Path } | Should -Throw + } + It 'Get-GitHubRepositoryContent - retrieves content from a feature branch' { + $Owner = 'github' + $Repository = 'rest-api-description' + $Path = 'README.md' + $Ref = 'feature-branch' + + $Result = Get-GitHubRepositoryContent -Owner $Owner -Repository $Repository -Path $Path -Ref $Ref + $result | Format-List | Out-String -Stream | ForEach-Object { Write-Verbose $_ } + $Result | Should -Not -BeNullOrEmpty + $Result | Should -BeOfType 'System.Object' + } + } + } } Describe 'As a GitHub App - Enterprise (APP_ENT)' { @@ -1058,13 +1244,63 @@ Describe 'As a GitHub App - Enterprise (APP_ENT)' { { Update-GitHubOrganization -Organization 'psmodule-test-org3' -Blog 'https://psmodule.io' } | Should -Not -Throw } } + Context 'Repository' { + Context 'Content' { + BeforeEach { + Connect-GitHubApp -Organization 'psmodule-test-org3' -Default + } + AfterEach { + Get-GitHubContext -ListAvailable | Disconnect-GitHubAccount + } + It 'Get-GitHubRepositoryContent - retrieves README.md from main branch' { + $Owner = 'github' + $Repository = 'rest-api-description' + $Path = 'README.md' + $Ref = 'main' + + $Result = Get-GitHubRepositoryContent -Owner $Owner -Repository $Repository -Path $Path -Ref $Ref + + $Result | Should -Not -BeNullOrEmpty + $Result | Should -BeOfType 'System.Object' + $result | Format-List | Out-String -Stream | ForEach-Object { Write-Verbose $_ } + $Result.name | Should -Be 'README.md' + } + It 'Get-GitHubRepositoryContent - retrieves root directory contents' { + $Owner = 'github' + $Repository = 'rest-api-description' + + $Result = Get-GitHubRepositoryContent -Owner $Owner -Repository $Repository + $result | Format-List | Out-String -Stream | ForEach-Object { Write-Verbose $_ } + $Result | Should -Not -BeNullOrEmpty + $Result | Should -BeOfType 'System.Object' + } + It 'Get-GitHubRepositoryContent - returns error for non-existent file' { + $Owner = 'github' + $Repository = 'rest-api-description' + $Path = 'nonexistentfile.md' + + { Get-GitHubRepositoryContent -Owner $Owner -Repository $Repository -Path $Path } | Should -Throw + } + It 'Get-GitHubRepositoryContent - retrieves content from a feature branch' { + $Owner = 'github' + $Repository = 'rest-api-description' + $Path = 'README.md' + $Ref = 'feature-branch' + + $Result = Get-GitHubRepositoryContent -Owner $Owner -Repository $Repository -Path $Path -Ref $Ref + $result | Format-List | Out-String -Stream | ForEach-Object { Write-Verbose $_ } + $Result | Should -Not -BeNullOrEmpty + $Result | Should -BeOfType 'System.Object' + } + } + } } Describe 'As a GitHub App - Organization (APP_ORG)' { - BeforeAll { + BeforeEach { Connect-GitHubAccount -ClientID $env:TEST_APP_ORG_CLIENT_ID -PrivateKey $env:TEST_APP_ORG_PRIVATE_KEY } - AfterAll { + AfterEach { Get-GitHubContext -ListAvailable | Disconnect-GitHubAccount } Context 'Auth' { @@ -1139,10 +1375,10 @@ Describe 'As a GitHub App - Organization (APP_ORG)' { } } Context 'Organization' { - BeforeAll { + BeforeEach { Connect-GitHubApp -Organization 'psmodule-test-org' -Default } - AfterAll { + AfterEach { Get-GitHubContext -ListAvailable | Disconnect-GitHubAccount } It 'Get-GitHubOrganization - Gets a specific organization (APP_ORG)' { @@ -1196,6 +1432,12 @@ Describe 'As a GitHub App - Organization (APP_ORG)' { } Context 'Repository' { Context 'Content' { + BeforeEach { + Connect-GitHubApp -Organization 'psmodule-test-org' -Default + } + AfterEach { + Get-GitHubContext -ListAvailable | Disconnect-GitHubAccount + } It 'Get-GitHubRepositoryContent - retrieves README.md from main branch' { $Owner = 'github' $Repository = 'rest-api-description' @@ -1209,9 +1451,6 @@ Describe 'As a GitHub App - Organization (APP_ORG)' { $result | Format-List | Out-String -Stream | ForEach-Object { Write-Verbose $_ } $Result.name | Should -Be 'README.md' } - } - - Context 'Get-GitHubRepositoryContent - Retrieve repository root contents' { It 'Get-GitHubRepositoryContent - retrieves root directory contents' { $Owner = 'github' $Repository = 'rest-api-description' @@ -1221,9 +1460,6 @@ Describe 'As a GitHub App - Organization (APP_ORG)' { $Result | Should -Not -BeNullOrEmpty $Result | Should -BeOfType 'System.Object' } - } - - Context 'Get-GitHubRepositoryContent - Handle invalid path' { It 'Get-GitHubRepositoryContent - returns error for non-existent file' { $Owner = 'github' $Repository = 'rest-api-description' @@ -1231,9 +1467,6 @@ Describe 'As a GitHub App - Organization (APP_ORG)' { { Get-GitHubRepositoryContent -Owner $Owner -Repository $Repository -Path $Path } | Should -Throw } - } - - Context 'Get-GitHubRepositoryContent - Retrieve content from a specific branch' { It 'Get-GitHubRepositoryContent - retrieves content from a feature branch' { $Owner = 'github' $Repository = 'rest-api-description' From 335d4ae7e76e169adf98c72918da45fd0b474fd2 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Mon, 10 Feb 2025 22:37:37 +0100 Subject: [PATCH 5/5] =?UTF-8?q?=F0=9F=9A=80=20[Feature]:=20Add=20Get-GitHu?= =?UTF-8?q?bAPIDescriptor=20script=20to=20list=20top-level=20folders=20and?= =?UTF-8?q?=20subdirectories=20in=20a=20GitHub=20repository?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tools/Get-GitHubAPIDescriptor.ps1 | 81 +++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 tools/Get-GitHubAPIDescriptor.ps1 diff --git a/tools/Get-GitHubAPIDescriptor.ps1 b/tools/Get-GitHubAPIDescriptor.ps1 new file mode 100644 index 000000000..d01a65a83 --- /dev/null +++ b/tools/Get-GitHubAPIDescriptor.ps1 @@ -0,0 +1,81 @@ +### +### List Top-Level Folders and Subdirectories in a GitHub Repository +### + +# Define repository owner and name +$owner = 'github' +$Repository = 'rest-api-description' + +# GitHub API URL for root contents +$rootUrl = "https://api.github.com/repos/$owner/$Repository/contents" + +# Invoke the REST API to get root contents (add User-Agent header if needed) +$rootContents = Invoke-RestMethod -Uri $rootUrl -Headers @{ 'Accept' = 'application/vnd.github.v3+json' } +$rootContents | Format-Table -AutoSize + +$rootContents | ForEach-Object { + [PSCustomObject]@{ + Name = $_.name + Size = $_.size + Type = $_.type + } +} | Sort-Object Type, Name | Format-Table + +# Filter for directories in the root (type equals "dir") +$topLevelFolders = $rootContents | Where-Object { $_.type -eq 'dir' } | Select-Object -ExpandProperty name + +# Output the top-level folder names +$topLevelFolders + + +### +### List Subdirectories for Each Top Folder: +### + +foreach ($folder in $topLevelFolders) { + Write-Host "`nSubfolders in '$folder':" + # Construct URL for the folder's contents + $folderUrl = "https://api.github.com/repos/$owner/$Repository/contents/$folder" + $folderContents = Invoke-RestMethod -Uri $folderUrl -Headers @{ 'Accept' = 'application/vnd.github.v3+json' } + + # Filter for subdirectories (type "dir") within this folder + $subDirs = $folderContents | Where-Object { $_.type -eq 'dir' } | Select-Object -ExpandProperty name + + # Print each subfolder name + $subDirs | ForEach-Object { Write-Host "- $_" } +} + + + +# (Optional) Get entire tree recursively - may return a lot of data for large repos +$branch = 'main' # or specify default branch/commit SHA +$treeUrl = "https://api.github.com/repos/$owner/$Repository/git/trees/$branch`?recursive=1" +$treeData = Invoke-RestMethod -Uri $treeUrl -Headers @{ 'Accept' = 'application/vnd.github.v3+json' } + +# $treeData.tree is a list of all paths in the repository (each with type "blob" for file or "tree" for folder). +# We can filter this list for type "tree" to get directories. +$allDirs = $treeData.tree | Where-Object { $_.type -eq 'tree' } | Select-Object -ExpandProperty path + + + +function Get-GitHubAPIDescription { + <# + .SYNOPSIS + Retrieves the GitHub REST API description from the GitHub REST API description repository. + + .DESCRIPTION + Retrieves the GitHub REST API description from the GitHub REST API description repository. + The API description is used to generate the GitHub REST API functions. + + .EXAMPLE + Get-GitHubAPIDescription + #> + + # GitHub REST API description repository + $APIDocURI = 'https://raw.githubusercontent.com/github/rest-api-description/main' + $Bundled = '/descriptions/api.github.com/api.github.com.json' + $APIDocURI = $APIDocURI + $Bundled + $response = Invoke-RestMethod -Uri $APIDocURI -Method Get + + return $response +}