3 Commits
1.3.1 ... 1.4.0

Author SHA1 Message Date
1c09d40253 Resolve -preserverelativepath issue in invoke-giteafiledownload, update pester tests, and bump module version to 1.4.0
Some checks failed
Publish Powershell Module to Gitea Repository / deploy (push) Has been cancelled
Publish Powershell Module to Gitea Repository / test (push) Has been cancelled
2025-05-27 11:31:00 -04:00
1d0c8b43bc Update module path in test files for consistency 2025-05-27 08:19:53 -04:00
445fa8a429 Move tests to root 2025-05-27 08:16:01 -04:00
8 changed files with 121 additions and 74 deletions

View File

@@ -12,7 +12,7 @@
RootModule = 'PS-GiteaUtilities.psm1' RootModule = 'PS-GiteaUtilities.psm1'
# Version number of this module. # Version number of this module.
ModuleVersion = '1.3.1' ModuleVersion = '1.4.0'
# Supported PSEditions # Supported PSEditions
# CompatiblePSEditions = @() # CompatiblePSEditions = @()

View File

@@ -324,15 +324,21 @@ Function Invoke-GiteaFileDownload {
A personal access token for the Gitea server. A personal access token for the Gitea server.
.PARAMETER outputPath .PARAMETER outputPath
The path where the downloaded file should be saved. If not specified, the file will be saved in the current directory using the filename from the URL. The path where the downloaded file should be saved. If not specified, the file will be saved in the current directory with its original name from the download URL.
If using PreserveRelativePath, outputPath is treated as the base directory to append the relative path to.
.PARAMETER force .PARAMETER outputName
A switch parameter to force overwriting of an existing file at the output path. The name of the file to save the downloaded content as. If not specified, the file will be saved with its original name from the download URL.
Cannot be used with PreserveRelativePath.
.PARAMETER PreserveRelativePath .PARAMETER PreserveRelativePath
A switch parameter to preserve the relative path structure from the repository. 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 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. If used with outputPath, treats the outputPath as the base directory to append the relative path to.
Cannot be used with outputName.
.PARAMETER force
A switch parameter to force overwriting of an existing file at the output path.
.PARAMETER type .PARAMETER type
The type of the item to download (file, dir, lfs). This parameter is typically used with pipeline input from Get-GiteaChildItem. The type of the item to download (file, dir, lfs). This parameter is typically used with pipeline input from Get-GiteaChildItem.
@@ -344,7 +350,7 @@ Function Invoke-GiteaFileDownload {
.EXAMPLE .EXAMPLE
# Example 2: Download a file to a specific location with force overwrite # Example 2: Download a file to a specific location with force overwrite
Invoke-GiteaFileDownload -downloadURL "https://gitea.example.com/api/v1/repos/owner/repo/raw/path/to/file.txt" -token "your_token" -outputPath "C:\Downloads\file.txt" -force Invoke-GiteaFileDownload -downloadURL "https://gitea.example.com/api/v1/repos/owner/repo/raw/path/to/file.txt" -token "your_token" -outputPath "C:\Downloads\" -outputName "file.txt" -force
.EXAMPLE .EXAMPLE
# Example 3: Download files in pipeline from Get-GiteaChildItem # Example 3: Download files in pipeline from Get-GiteaChildItem
@@ -366,10 +372,10 @@ Function Invoke-GiteaFileDownload {
[string]$token, [string]$token,
[Parameter(ValueFromPipelineByPropertyName)] [Parameter(ValueFromPipelineByPropertyName)]
[string]$outputPath, [string]$outputPath,
[switch]$force,
[Parameter(ValueFromPipelineByPropertyName)] [Parameter(ValueFromPipelineByPropertyName)]
[string]$filePath, [string]$outputName,
[switch]$PreserveRelativePath, [switch]$PreserveRelativePath,
[switch]$force,
[Parameter(ValueFromPipelineByPropertyName)] [Parameter(ValueFromPipelineByPropertyName)]
[string]$type [string]$type
) )
@@ -393,24 +399,26 @@ Function Invoke-GiteaFileDownload {
Write-Debug "Token: $token (length: $(if($token){$token.Length}else{0}))" Write-Debug "Token: $token (length: $(if($token){$token.Length}else{0}))"
Write-Verbose "PreserveRelativePath: $PreserveRelativePath" Write-Verbose "PreserveRelativePath: $PreserveRelativePath"
# Ensure PreserveRelativePath is not used with outputName
if ($PreserveRelativePath -and $PSBoundParameters.ContainsKey('outputName')) {
throw "The -PreserveRelativePath switch cannot be used with the -outputName parameter. Use -outputPath instead."
}
# Create a WebClient to be reused # Create a WebClient to be reused
$webClient = New-Object System.Net.WebClient $webClient = New-Object System.Net.WebClient
$webClient.Headers.Add("Authorization", "token $token") $webClient.Headers.Add("Authorization", "token $token")
} }
process { process {
# Normalize path separators for cross-platform compatibility
$filePath = $filePath -replace '\\', '/'
Write-Verbose "File path: $filePath"
Write-Verbose "Type: $type" Write-Verbose "Type: $type"
# Handle the type parameter
# Skip directories and provide a verbose message # Skip directories and provide a verbose message
if ($type -eq "dir") { if ($type -eq "dir") {
Write-Verbose "Skipping directory: $filePath" Write-Verbose "Skipping directory"
return [PSCustomObject]@{ return [PSCustomObject]@{
Path = $filePath
SourceURL = $downloadURL SourceURL = $downloadURL
FilePath = $filePath
Type = $type Type = $type
Success = $true Success = $true
Error = "Skipped - item is a directory" Error = "Skipped - item is a directory"
@@ -421,12 +429,10 @@ Function Invoke-GiteaFileDownload {
# Handle invalid entry types # Handle invalid entry types
if ([string]::IsNullOrEmpty($type) -and [string]::IsNullOrEmpty($downloadURL)) { if ([string]::IsNullOrEmpty($type) -and [string]::IsNullOrEmpty($downloadURL)) {
$errorMsg = "Invalid item type or missing download URL for path: $filePath" $errorMsg = "Invalid item type or missing download URL for path"
Write-Error $errorMsg Write-Error $errorMsg
return [PSCustomObject]@{ return [PSCustomObject]@{
Path = $filePath
SourceURL = $downloadURL SourceURL = $downloadURL
FilePath = $filePath
Type = $type Type = $type
Success = $false Success = $false
Error = $errorMsg Error = $errorMsg
@@ -435,59 +441,70 @@ Function Invoke-GiteaFileDownload {
} }
} }
Write-Verbose "Processing download URL: $downloadURL"
# Set the output path based on parameters # Parse the download URL to extract the file path and file name
$currentOutputPath = ""
# Get the file name from the download URL or file path # Separate the URL into its components: Gitea URL, Repo Owner, Repo Name, branch, file path, and file name
$outputFileName = $downloadURL.Substring($downloadURL.LastIndexOf("/") + 1) $uri = New-Object System.Uri($downloadURL)
# Clean the file name by removing any query string parameters and HTML encoded characters $pathSegments = $uri.AbsolutePath.Trim('/').Split('/')
$outputFileName = [System.Uri]::UnescapeDataString($outputFileName.Split("?")[0]) # Everything after the branch + 1 segment is considered the file path with the last segment being the file name
$branchIndex = [Array]::IndexOf($pathSegments, "branch") + 2
$DownloadFilePath = $pathSegments[$branchIndex..($pathSegments.Length - 2)] -join '/'
$DownloadFileName = $pathSegments[-1]
if ($PreserveRelativePath -and $filePath) { if ($PreserveRelativePath -and $outputPath) {
# If PreserveRelativePath is used, set up the directory structure # If PreserveRelativePath is used, set up the directory structure
Write-Verbose "Preserving relative path structure for: $filePath" Write-Verbose "Preserving relative path structure for: $DownloadFileName"
if ($outputPath) { $baseDir = $outputPath
# If outputPath is specified, use it as the base directory Write-Verbose "Using base directory: $baseDir"
$baseDir = $outputPath
Write-Verbose "Using base directory: $baseDir"
# Combine the base directory with the file path # Combine the base directory with the file path
$currentOutputPath = Join-Path -Path $baseDir -ChildPath $filePath $fileOutputPath = Join-Path -Path $baseDir -ChildPath $DownloadFilePath
} else { # Combine with the file name
# If no outputPath is specified, use the current directory as base $fileOutputPath = Join-Path -Path $fileOutputPath -ChildPath $DownloadFileName
$currentOutputPath = Join-Path -Path (Get-Location) -ChildPath $filePath
} }
elseif($PreserveRelativePath) {
# Normalize path separators # If no outputPath is specified, use the current directory as base and the file name from the download URL
$currentOutputPath = $currentOutputPath.Replace("/", [System.IO.Path]::DirectorySeparatorChar) $fileOutputPath = Join-Path -Path (Get-Location) -ChildPath $DownloadFilePath
Write-Verbose "Output path with preserved structure: $currentOutputPath" # Combine with the file name
} else { $fileOutputPath = Join-Path -Path $fileOutputPath -ChildPath $DownloadFileName
# Standard path handling (unchanged from original) }
if ($outputPath) { elseif ($outputPath -and $outputName) {
$currentOutputPath = $outputPath # If both outputPath and outputName are specified, use outputPath as the base directory with the file name specified by outputName
} else { $fileOutputPath = Join-Path -Path $outputPath -ChildPath $outputName
# Append the outputFileName to the current location }
$currentOutputPath = Join-Path -Path (Get-Location) -ChildPath $outputFileName elseif ($outputPath) {
} # If outputPath is specified, use it as the base directory with the file name from the download URL
Write-Verbose "Output path: $currentOutputPath" $fileOutputPath = Join-Path -Path $outputPath -ChildPath $DownloadFileName
}
elseif($outputName) {
# If only outputName is specified, use the current directory with the specified outputName
$fileOutputPath = Join-Path -Path (Get-Location) -ChildPath $outputName
}
else {
# Fallback to current directory with the file name from the download URL
$fileOutputPath = Join-Path -Path (Get-Location) -ChildPath $DownloadFileName
} }
# Normalize path separators
$fileOutputPath = $fileOutputPath.Replace("/", [System.IO.Path]::DirectorySeparatorChar)
Write-Verbose "Output path: $fileOutputPath"
$result = [PSCustomObject]@{ $result = [PSCustomObject]@{
Path = $currentOutputPath Path = $fileOutputPath
SourceURL = $downloadURL SourceURL = $downloadURL
FilePath = $filePath Type = "file"
Type = $type
Success = $false Success = $false
Error = $null Error = $null
Timestamp = Get-Date Timestamp = Get-Date
FileSize = $null FileSize = $null
} }
if((Test-Path -Path $currentOutputPath -PathType Leaf) -and (-not $force)) { if((Test-Path -Path $fileOutputPath -PathType Leaf) -and (-not $force)) {
$errorMsg = "The file '$currentOutputPath' already exists. Use the -Force switch to overwrite the file." $errorMsg = "The file '$fileOutputPath' already exists. Use the -Force switch to overwrite the file."
Write-Error $errorMsg Write-Error $errorMsg
$result.Error = $errorMsg $result.Error = $errorMsg
return $result return $result
@@ -495,23 +512,23 @@ Function Invoke-GiteaFileDownload {
try { try {
# Create the directory structure if it doesn't exist # Create the directory structure if it doesn't exist
$directory = Split-Path -Path $currentOutputPath -Parent $directory = Split-Path -Path $fileOutputPath -Parent
if (-not (Test-Path -Path $directory -PathType Container) -and $directory) { if (-not (Test-Path -Path $directory -PathType Container) -and $directory) {
Write-Verbose "Creating directory structure: $directory" Write-Verbose "Creating directory structure: $directory"
New-Item -Path $directory -ItemType Directory -Force | Out-Null New-Item -Path $directory -ItemType Directory -Force | Out-Null
} }
# Download the file # Download the file
Write-Verbose "Downloading from $downloadURL to $currentOutputPath" Write-Verbose "Downloading from $downloadURL to $fileOutputPath"
# Use synchronous download # Use synchronous download
$webClient.DownloadFile($downloadURL, $currentOutputPath) $webClient.DownloadFile($downloadURL, $fileOutputPath)
Write-Verbose "File downloaded successfully to $currentOutputPath" Write-Verbose "File downloaded successfully to $fileOutputPath"
# Get file info for the result object # Get file info for the result object
if (Test-Path -Path $currentOutputPath -PathType Leaf) { if (Test-Path -Path $fileOutputPath -PathType Leaf) {
$fileInfo = Get-Item -Path $currentOutputPath $fileInfo = Get-Item -Path $fileOutputPath
$result.FileSize = $fileInfo.Length $result.FileSize = $fileInfo.Length
} }

View File

@@ -1,7 +1,7 @@
# Get-GiteaChildItem.Tests.ps1 # Get-GiteaChildItem.Tests.ps1
# Import the module under test # Import the module under test
$modulePath = Join-Path -Path $PSScriptRoot -ChildPath '..\PS-GiteaUtilities.psm1' $modulePath = Join-Path -Path $PSScriptRoot -ChildPath '..\PS-GiteaUtilities\PS-GiteaUtilities.psm1'
Import-Module -Name $modulePath -Force Import-Module -Name $modulePath -Force
Describe 'Get-GiteaChildItem' { Describe 'Get-GiteaChildItem' {

View File

@@ -1,7 +1,7 @@
# Get-GiteaConfiguration.Tests.ps1 # Get-GiteaConfiguration.Tests.ps1
# Import the module under test # Import the module under test
$modulePath = Join-Path -Path $PSScriptRoot -ChildPath '..\PS-GiteaUtilities.psm1' $modulePath = Join-Path -Path $PSScriptRoot -ChildPath '..\PS-GiteaUtilities\PS-GiteaUtilities.psm1'
Import-Module -Name $modulePath -Force Import-Module -Name $modulePath -Force
Describe 'Get-GiteaConfiguration' { Describe 'Get-GiteaConfiguration' {

View File

@@ -1,7 +1,7 @@
# Get-GiteaFileContent.Tests.ps1 # Get-GiteaFileContent.Tests.ps1
# Import the module under test # Import the module under test
$modulePath = Join-Path -Path $PSScriptRoot -ChildPath '..\PS-GiteaUtilities.psm1' $modulePath = Join-Path -Path $PSScriptRoot -ChildPath '..\PS-GiteaUtilities\PS-GiteaUtilities.psm1'
Import-Module -Name $modulePath -Force Import-Module -Name $modulePath -Force
Describe 'Get-GiteaFileContent' { Describe 'Get-GiteaFileContent' {

View File

@@ -1,7 +1,7 @@
# Get-GiteaReleases.Tests.ps1 # Get-GiteaReleases.Tests.ps1
# Import the module # Import the module
$modulePath = Join-Path -Path $PSScriptRoot -ChildPath '..\PS-GiteaUtilities.psm1' $modulePath = Join-Path -Path $PSScriptRoot -ChildPath '..\PS-GiteaUtilities\PS-GiteaUtilities.psm1'
Import-Module -Name $modulePath -Force Import-Module -Name $modulePath -Force
Describe 'Get-GiteaReleases' { Describe 'Get-GiteaReleases' {

View File

@@ -1,7 +1,7 @@
# Invoke-GiteaFileDownload.Tests.ps1 # Invoke-GiteaFileDownload.Tests.ps1
# Import the module under test # Import the module under test
$modulePath = Join-Path -Path $PSScriptRoot -ChildPath '..\PS-GiteaUtilities.psm1' $modulePath = Join-Path -Path $PSScriptRoot -ChildPath '..\PS-GiteaUtilities\PS-GiteaUtilities.psm1'
Import-Module -Name $modulePath -Force Import-Module -Name $modulePath -Force
Describe 'Invoke-GiteaFileDownload' { Describe 'Invoke-GiteaFileDownload' {
@@ -13,7 +13,7 @@ Describe 'Invoke-GiteaFileDownload' {
} }
# Correct webClient Mock: headers, download, dispose # Correct webClient Mock: headers, download, dispose
Mock -CommandName New-Object -ModuleName PS-GiteaUtilities -MockWith { Mock -CommandName New-Object -ModuleName PS-GiteaUtilities -ParameterFilter { $TypeName -eq 'System.Net.WebClient' } -MockWith {
$headers = New-Object System.Net.WebHeaderCollection $headers = New-Object System.Net.WebHeaderCollection
# Create a real object # Create a real object
@@ -54,19 +54,49 @@ Describe 'Invoke-GiteaFileDownload' {
} }
} }
Context 'When preserving relative paths' { Context 'When specifying an outputPath' {
It 'Should create a path based on filePath with PreserveRelativePath' { It 'Should create a path based on outputPath' {
$result = Invoke-GiteaFileDownload -downloadURL 'https://gitea.example.com/raw/path/to/file.txt' ` $result = Invoke-GiteaFileDownload -downloadURL 'https://gitea.example.com/raw/path/to/file.txt' `
-filePath 'docs/manual/file.txt' -PreserveRelativePath -token 'abc123' -outputPath 'docs/manual/' -token 'abc123'
$result.Path | Should -Match 'docs[\\/]+manual[\\/]+file\.txt$' $result.Path | Should -Match 'docs[\\/]+manual[\\/]+file\.txt$'
$result.Success | Should -Be $true $result.Success | Should -Be $true
} }
} }
Context 'When specifying an outputName' {
It 'Should download file and name it based on outputName' {
$result = Invoke-GiteaFileDownload -downloadURL 'https://gitea.example.com/raw/path/to/file.txt' `
-outputName 'newfile.txt' -token 'abc123'
$result.Path | Should -Match 'newfile\.txt$'
$result.Success | Should -Be $true
}
}
Context 'When specifying an outputName and outputPath' {
It 'Should download file and name it based on outputName' {
$result = Invoke-GiteaFileDownload -downloadURL 'https://gitea.example.com/raw/path/to/file.txt' `
-outputPath 'docs/manual/' -outputName 'newfile.txt' -token 'abc123'
$result.Path | Should -Match 'docs[\\/]+manual[\\/]+newfile\.txt$'
$result.Success | Should -Be $true
}
}
Context 'When using -PreserveRelativePath' {
It 'Should preserve the relative path structure' {
$result = Invoke-GiteaFileDownload -downloadURL 'https://gitea.example.com/raw/path/to/file.txt' `
-PreserveRelativePath -token 'abc123'
$result.Path | Should -Match 'path[\\/]+to[\\/]+file\.txt$'
$result.Success | Should -Be $true
}
}
Context 'When skipping directories' { Context 'When skipping directories' {
It 'Should skip items with type = dir' { It 'Should skip items with type = dir' {
$result = Invoke-GiteaFileDownload -filePath 'docs/' -type 'dir' -token 'abc123' $result = Invoke-GiteaFileDownload -outputPath 'docs/' -type 'dir' -token 'abc123'
$result.Success | Should -Be $true $result.Success | Should -Be $true
$result.Error | Should -Match 'Skipped' $result.Error | Should -Match 'Skipped'
@@ -93,7 +123,7 @@ Describe 'Invoke-GiteaFileDownload' {
Context 'When download fails' { Context 'When download fails' {
BeforeEach { BeforeEach {
Mock -CommandName New-Object -ModuleName PS-GiteaUtilities -MockWith { Mock -CommandName New-Object -ModuleName PS-GiteaUtilities -ParameterFilter { $TypeName -eq 'System.Net.WebClient' } -MockWith {
$headers = New-Object System.Net.WebHeaderCollection $headers = New-Object System.Net.WebHeaderCollection
$webClient = New-Object PSObject $webClient = New-Object PSObject

View File

@@ -4,7 +4,7 @@ Describe 'Set-GiteaConfiguration' {
BeforeAll { BeforeAll {
# Import the module dynamically # Import the module dynamically
$modulePath = Join-Path -Path $PSScriptRoot -ChildPath '..\PS-GiteaUtilities.psm1' $modulePath = Join-Path -Path $PSScriptRoot -ChildPath '..\PS-GiteaUtilities\PS-GiteaUtilities.psm1'
Import-Module -Name $modulePath -Force Import-Module -Name $modulePath -Force
} }