From fb2a87ed84b336df2af193fa07d6c0a5a97064d6 Mon Sep 17 00:00:00 2001 From: Raymond LaRose Date: Thu, 24 Apr 2025 16:34:18 -0400 Subject: [PATCH] Add PreserveRelativePath and type parameters to Invoke-GiteaFileDownload; enhance Get-GiteaChildItem with File switch for filtering --- PS-GiteaUtilities/PS-GiteaUtilities.psm1 | 156 ++++++++++++++++++++--- 1 file changed, 138 insertions(+), 18 deletions(-) diff --git a/PS-GiteaUtilities/PS-GiteaUtilities.psm1 b/PS-GiteaUtilities/PS-GiteaUtilities.psm1 index 9c15be8..8226903 100644 --- a/PS-GiteaUtilities/PS-GiteaUtilities.psm1 +++ b/PS-GiteaUtilities/PS-GiteaUtilities.psm1 @@ -297,6 +297,15 @@ Function Invoke-GiteaFileDownload { .PARAMETER force A switch parameter to force overwriting of an existing file at the output path. + + .PARAMETER PreserveRelativePath + A switch parameter to preserve the relative path structure from the repository. + If used without specifying an outputPath, creates the directory structure in the current location. + If used with outputPath, treats the outputPath as the base directory to append the relative path to. + + .PARAMETER type + The type of the item to download (file, dir, lfs). This parameter is typically used with pipeline input from Get-GiteaChildItem. + Directories will be skipped with a verbose message. .EXAMPLE # Example 1: Download a file to the current directory @@ -309,16 +318,29 @@ Function Invoke-GiteaFileDownload { .EXAMPLE # Example 3: Download files in pipeline from Get-GiteaChildItem Get-GiteaChildItem -path "docs" | Where-Object { $_.type -eq 'file' } | Invoke-GiteaFileDownload + + .EXAMPLE + # Example 4: Download files preserving their relative paths + Get-GiteaChildItem -path "docs" -Recurse | Where-Object { $_.type -eq 'file' } | Invoke-GiteaFileDownload -PreserveRelativePath + + .EXAMPLE + # Example 5: Download files preserving their relative paths with a base output directory + Get-GiteaChildItem -path "docs" -Recurse | Where-Object { $_.type -eq 'file' } | Invoke-GiteaFileDownload -PreserveRelativePath -outputPath "C:\Downloads" #> [cmdletbinding()] param( - [Parameter(Mandatory, ValueFromPipelineByPropertyName)] + [Parameter(ValueFromPipelineByPropertyName)] [string]$downloadURL, [string]$token, [Parameter(ValueFromPipelineByPropertyName)] [string]$outputPath, - [switch]$force + [switch]$force, + [Parameter(ValueFromPipelineByPropertyName)] + [string]$filePath, + [switch]$PreserveRelativePath, + [Parameter(ValueFromPipelineByPropertyName)] + [string]$type ) begin { @@ -338,6 +360,7 @@ Function Invoke-GiteaFileDownload { Write-Verbose "Parameters:" Write-Verbose "token: $token (length: $(if($token){$token.Length}else{0}))" + Write-Verbose "PreserveRelativePath: $PreserveRelativePath" # Create a WebClient to be reused $webClient = New-Object System.Net.WebClient @@ -345,31 +368,103 @@ Function Invoke-GiteaFileDownload { } process { + Write-Verbose "File path: $filePath" + Write-Verbose "Type: $type" + + # Skip directories and provide a verbose message + if ($type -eq "dir") { + Write-Verbose "Skipping directory: $filePath" + return [PSCustomObject]@{ + Path = $filePath + SourceURL = $downloadURL + FilePath = $filePath + Type = $type + Success = $true + Error = "Skipped - item is a directory" + Timestamp = Get-Date + FileSize = $null + } + } + + # Handle invalid entry types + if ([string]::IsNullOrEmpty($type) -and [string]::IsNullOrEmpty($downloadURL)) { + $errorMsg = "Invalid item type or missing download URL for path: $filePath" + Write-Error $errorMsg + return [PSCustomObject]@{ + Path = $filePath + SourceURL = $downloadURL + FilePath = $filePath + Type = $type + Success = $false + Error = $errorMsg + Timestamp = Get-Date + FileSize = $null + } + } + Write-Verbose "Processing download URL: $downloadURL" - # If the output path is not specified, use the current directory and the file name from the download URL - $currentOutputPath = $outputPath - if (-not $currentOutputPath) { - # Get the file name from the download URL - $outputFileName = $downloadURL.Substring($downloadURL.LastIndexOf("/") + 1) - # Clean the file name by removing any query string parameters and HTML encoded characters - $outputFileName = [System.Uri]::UnescapeDataString($outputFileName.Split("?")[0]) + # Set the output path based on parameters + $currentOutputPath = "" + + # Get the file name from the download URL or file path + $outputFileName = $downloadURL.Substring($downloadURL.LastIndexOf("/") + 1) + # Clean the file name by removing any query string parameters and HTML encoded characters + $outputFileName = [System.Uri]::UnescapeDataString($outputFileName.Split("?")[0]) + + if ($PreserveRelativePath -and $filePath) { + # If PreserveRelativePath is used, set up the directory structure + Write-Verbose "Preserving relative path structure for: $filePath" - # Append the outputFileName to the current location - $currentOutputPath = Join-Path -Path (Get-Location) -ChildPath $outputFileName + if ($outputPath) { + # If outputPath is specified, use it as the base directory + $baseDir = $outputPath + Write-Verbose "Using base directory: $baseDir" + + # Combine the base directory with the file path + $currentOutputPath = Join-Path -Path $baseDir -ChildPath $filePath + } else { + # If no outputPath is specified, use the current directory as base + $currentOutputPath = Join-Path -Path (Get-Location) -ChildPath $filePath + } + + # Normalize path separators + $currentOutputPath = $currentOutputPath.Replace("/", [System.IO.Path]::DirectorySeparatorChar) + Write-Verbose "Output path with preserved structure: $currentOutputPath" + } else { + # Standard path handling (unchanged from original) + if ($outputPath) { + $currentOutputPath = $outputPath + } else { + # Append the outputFileName to the current location + $currentOutputPath = Join-Path -Path (Get-Location) -ChildPath $outputFileName + } + Write-Verbose "Output path: $currentOutputPath" } - Write-Verbose "Output path: $currentOutputPath" + $result = [PSCustomObject]@{ + Path = $currentOutputPath + SourceURL = $downloadURL + FilePath = $filePath + Type = $type + Success = $false + Error = $null + Timestamp = Get-Date + FileSize = $null + } if((Test-Path -Path $currentOutputPath -PathType Leaf) -and (-not $force)) { - Write-Error "The file '$currentOutputPath' already exists. Use the -Force switch to overwrite the file." - return $false + $errorMsg = "The file '$currentOutputPath' already exists. Use the -Force switch to overwrite the file." + Write-Error $errorMsg + $result.Error = $errorMsg + return $result } try { # Create the directory structure if it doesn't exist $directory = Split-Path -Path $currentOutputPath -Parent if (-not (Test-Path -Path $directory -PathType Container) -and $directory) { + Write-Verbose "Creating directory structure: $directory" New-Item -Path $directory -ItemType Directory -Force | Out-Null } @@ -380,11 +475,21 @@ Function Invoke-GiteaFileDownload { $webClient.DownloadFile($downloadURL, $currentOutputPath) Write-Verbose "File downloaded successfully to $currentOutputPath" - return $true + + # Get file info for the result object + if (Test-Path -Path $currentOutputPath -PathType Leaf) { + $fileInfo = Get-Item -Path $currentOutputPath + $result.FileSize = $fileInfo.Length + } + + $result.Success = $true + return $result } catch { - Write-Error "Failed to download file from Gitea: $_" - return $false + $errorMsg = "Failed to download file from Gitea: $_" + Write-Error $errorMsg + $result.Error = $errorMsg + return $result } } @@ -428,6 +533,9 @@ Function Get-GiteaChildItem { .PARAMETER Depth Specifies the number of subdirectory levels to include in the recursion. Default is unlimited if Recurse is specified. + + .PARAMETER File + Returns only files, omitting directories from the results. .EXAMPLE # List items in the root directory of a repository @@ -446,6 +554,10 @@ Function Get-GiteaChildItem { .EXAMPLE # List items with a maximum depth of 2 subdirectories Get-GiteaChildItem -repoOwner "owner" -repoName "repo" -Path "src" -Recurse -Depth 2 -token "your_token" + + .EXAMPLE + # List only files (no directories) in a repository + Get-GiteaChildItem -repoOwner "owner" -repoName "repo" -Path "src" -File -token "your_token" #> [CmdletBinding()] @@ -462,7 +574,8 @@ Function Get-GiteaChildItem { [string]$branch = "main", [string]$token, [switch]$Recurse, - [int]$Depth + [int]$Depth, + [switch]$File ) begin { @@ -503,6 +616,7 @@ Function Get-GiteaChildItem { Write-Verbose "repoName: $repoName" Write-Verbose "Path: $Path" Write-Verbose "token: $token" + Write-Verbose "File: $File" $headers = @{ "Authorization" = "token $token" @@ -699,6 +813,12 @@ Function Get-GiteaChildItem { } end { + # Filter out directories if -File switch is specified + if ($File) { + Write-Verbose "Filtering results to include only files" + $results = $results | Where-Object { $_.type -ne 'dir' } + } + return $results } }