From d8dfb1d1fe272c6effdcb89084156a4e6221ddf5 Mon Sep 17 00:00:00 2001 From: Raymond LaRose Date: Thu, 24 Apr 2025 15:57:17 -0400 Subject: [PATCH] Enhance Get-GiteaChildItem function with recursion and depth parameters for improved directory listing --- PS-GiteaUtilities/PS-GiteaUtilities.psm1 | 163 ++++++++++++++++++++--- 1 file changed, 143 insertions(+), 20 deletions(-) diff --git a/PS-GiteaUtilities/PS-GiteaUtilities.psm1 b/PS-GiteaUtilities/PS-GiteaUtilities.psm1 index c82182e..9c15be8 100644 --- a/PS-GiteaUtilities/PS-GiteaUtilities.psm1 +++ b/PS-GiteaUtilities/PS-GiteaUtilities.psm1 @@ -422,6 +422,12 @@ Function Get-GiteaChildItem { .PARAMETER token A personal access token for the Gitea server. + + .PARAMETER Recurse + Recursively lists items in all subdirectories. + + .PARAMETER Depth + Specifies the number of subdirectory levels to include in the recursion. Default is unlimited if Recurse is specified. .EXAMPLE # List items in the root directory of a repository @@ -432,6 +438,14 @@ Function Get-GiteaChildItem { Get-GiteaChildItem -repoOwner "owner" -repoName "repo" -Path "docs" -token "your_token" | Where-Object { $_.type -eq "file" } | Get-GiteaFileContent -token "your_token" -decode + + .EXAMPLE + # Recursively list all items in a repository + Get-GiteaChildItem -repoOwner "owner" -repoName "repo" -Path "src" -Recurse -token "your_token" + + .EXAMPLE + # List items with a maximum depth of 2 subdirectories + Get-GiteaChildItem -repoOwner "owner" -repoName "repo" -Path "src" -Recurse -Depth 2 -token "your_token" #> [CmdletBinding()] @@ -446,13 +460,13 @@ Function Get-GiteaChildItem { [string[]]$Path, [Parameter(ValueFromPipelineByPropertyName)] [string]$branch = "main", - [string]$token + [string]$token, + [switch]$Recurse, + [int]$Depth ) begin { # Initialize results array - # This is used to store the results of the API calls later - # It is initialized here to avoid re-initializing it in the process block $results = @() # Use configuration if parameters aren't provided @@ -497,20 +511,29 @@ Function Get-GiteaChildItem { # Load the GIT LFS configuration if needed $lfsExtensions = Get-GiteaLFSConfiguration -giteaURL $giteaURL -repoOwner $repoOwner -repoName $repoName -token $token + + # Initialize a queue for directories to process when using recursion + $directoryQueue = [System.Collections.Queue]::new() } process { $paths = $path foreach ($path in $paths) { Write-Verbose "Processing path: $path" - $encodedPath = [System.Uri]::EscapeDataString($path) + # Normalize the path format - replace backslashes with forward slashes and trim trailing slashes + $normalizedPath = $path -replace '\\', '/' -replace '/$', '' + $encodedPath = [System.Uri]::EscapeDataString($normalizedPath) + Write-Verbose "Normalized path: $normalizedPath" Write-Verbose "Encoded path: $encodedPath" $url = "$giteaURL" $url += "/api/v1/repos" $url += "/$repoOwner" $url += "/$repoName" $url += "/contents" - $url += "/$encodedPath" + # Only add the path component if it's not empty + if (-not [string]::IsNullOrWhiteSpace($normalizedPath)) { + $url += "/$encodedPath" + } $url += "?ref=$branch" Write-Verbose "URL: $url" @@ -534,31 +557,130 @@ Function Get-GiteaChildItem { $item.download_url = "$giteaURL/api/v1/repos/$repoOwner/$repoName/media/$($item.path)" } - $results += [PSCustomObject]@{ - # Properties for direct pipeline binding with other functions in this module - filePath = $item.path # Maps to -filePath parameter - Path = $item.path # Also include original name (alias) - repoOwner = $repoOwner # Maps to -repoOwner parameter - repoName = $repoName # Maps to -repoName parameter - giteaURL = $giteaURL # Maps to -giteaURL parameter - downloadURL = $item.download_url # Maps to -downloadURL parameter - branch = $branch # Maps to -branch parameter - - # Additional useful properties - type = $item.type # 'file' or 'dir' + $itemObj = [PSCustomObject]@{ + filePath = $item.path + Path = $item.path + repoOwner = $repoOwner + repoName = $repoName + giteaURL = $giteaURL + downloadURL = $item.download_url + branch = $branch + type = $item.type name = $item.name size = $item.size sha = $item.sha Success = $true Error = $null + Level = 0 + } + + $results += $itemObj + + # If the item is a directory and we're recursing, add it to the queue + if ($Recurse -and $item.type -eq 'dir') { + $directoryQueue.Enqueue(@{ + Path = $item.path + Level = 1 + }) + } + } + + # Process the directory queue if recursion is enabled + if ($Recurse) { + while ($directoryQueue.Count -gt 0) { + $currentDir = $directoryQueue.Dequeue() + $currentPath = $currentDir.Path + $currentLevel = $currentDir.Level + + # Check depth limit if specified + if ($PSBoundParameters.ContainsKey('Depth') -and $currentLevel -gt $Depth) { + continue + } + + Write-Verbose "Recursing into: $currentPath (Level: $currentLevel)" + + # Normalize the path format for consistent handling + $normalizedSubPath = $currentPath -replace '\\', '/' -replace '/$', '' + $encodedSubPath = [System.Uri]::EscapeDataString($normalizedSubPath) + + Write-Verbose "Normalized sub path: $normalizedSubPath" + Write-Verbose "Encoded sub path: $encodedSubPath" + + # Fix the URL construction for subdirectories + $subUrl = "$giteaURL/api/v1/repos/$repoOwner/$repoName/contents/$encodedSubPath" + # Add the branch reference separately to avoid issues + $subUrl += "?ref=$branch" + Write-Verbose "Sub URL: $subUrl" + + try { + $subResponse = Invoke-RestMethod -Uri $subUrl -Method Get -Headers $headers -ErrorAction Stop + $subItems = if ($subResponse -is [array]) { $subResponse } else { @($subResponse) } + + foreach ($subItem in $subItems) { + # Check if the name ends with any of the LFS extensions + $isLFS = $false + foreach ($ext in $lfsExtensions) { + if ($subItem.name.EndsWith($ext, [System.StringComparison]::InvariantCultureIgnoreCase)) { + $isLFS = $true + break + } + } + if ($isLFS) { + $subItem.type = "lfs" + $subItem.download_url = "$giteaURL/api/v1/repos/$repoOwner/$repoName/media/$($subItem.path)" + } + + $subItemObj = [PSCustomObject]@{ + filePath = $subItem.path + Path = $subItem.path + repoOwner = $repoOwner + repoName = $repoName + giteaURL = $giteaURL + downloadURL = $subItem.download_url + branch = $branch + type = $subItem.type + name = $subItem.name + size = $subItem.size + sha = $subItem.sha + Success = $true + Error = $null + Level = $currentLevel + } + + $results += $subItemObj + + # If the subitem is a directory, add it to the queue for further processing + if ($subItem.type -eq 'dir') { + $nextLevel = $currentLevel + 1 + # Only add to queue if we haven't hit our depth limit + if (-not $PSBoundParameters.ContainsKey('Depth') -or $nextLevel -le $Depth) { + $directoryQueue.Enqueue(@{ + Path = $subItem.path + Level = $nextLevel + }) + } + } + } + } + catch { + $errorDetails = if ($_.ErrorDetails) { + try { $_.ErrorDetails.Message | ConvertFrom-Json -ErrorAction SilentlyContinue } catch { $_.ErrorDetails.Message } + } else { $null } + + Write-Error "Failed to recursively retrieve path '$normalizedSubPath' from Gitea: $($_.Exception.Message)`n$($errorDetails | ConvertTo-Json -Depth 1 -Compress)" + } } } } catch { - Write-Error "Failed to retrieve '$path' from Gitea: $_" + $errorDetails = if ($_.ErrorDetails) { + try { $_.ErrorDetails.Message | ConvertFrom-Json -ErrorAction SilentlyContinue } catch { $_.ErrorDetails.Message } + } else { $null } + + Write-Error "Failed to retrieve '$normalizedPath' from Gitea: $($_.Exception.Message)`n$($errorDetails | ConvertTo-Json -Depth 1 -Compress)" $results += [PSCustomObject]@{ - filePath = $path - Path = $path + filePath = $normalizedPath + Path = $normalizedPath repoOwner = $repoOwner repoName = $repoName giteaURL = $giteaURL @@ -570,6 +692,7 @@ Function Get-GiteaChildItem { downloadURL = $null Success = $false Error = $_.Exception.Message + Level = 0 } } }