24 Commits
1.1.0 ... 1.4.3

Author SHA1 Message Date
5854940f75 Bump module version to 1.4.3 and update download URL in Get-GiteaChildItem function to allow for getting LFS files from other branches
All checks were successful
Publish Powershell Module to Gitea Repository / test (push) Successful in 10s
Publish Powershell Module to Gitea Repository / deploy (push) Successful in 32s
2025-05-27 12:11:54 -04:00
02f83e6633 Bump module version to 1.4.2 and update exported functions in the module manifest
All checks were successful
Publish Powershell Module to Gitea Repository / test (push) Successful in 10s
Publish Powershell Module to Gitea Repository / deploy (push) Successful in 33s
2025-05-27 11:52:31 -04:00
d4c7aa5a92 Bump module version to 1.4.1 in the module manifest 2025-05-27 11:41:09 -04:00
15381257e5 Exclude "Tests" directory when retrieving module name for publishing
All checks were successful
Publish Powershell Module to Gitea Repository / test (push) Successful in 10s
Publish Powershell Module to Gitea Repository / deploy (push) Successful in 34s
2025-05-27 11:35:41 -04:00
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
dd47a1b7e2 Bump module version to 1.3.1 in the module manifest.
All checks were successful
Publish Powershell Module to Gitea Repository / test (push) Successful in 9s
Publish Powershell Module to Gitea Repository / deploy (push) Successful in 32s
2025-04-30 15:23:44 -04:00
c65cde9fbc Refactor path handling for cross-platform compatibility and update configuration directory path 2025-04-30 15:20:52 -04:00
c18f68bc46 Remove unnecessary blank line in Pester test execution step 2025-04-30 15:05:05 -04:00
51a3a15a4f Bump module version to 1.3.0 in the module manifest 2025-04-30 15:02:25 -04:00
0451788a13 Merge pull request 'Merge in Get-GiteaReleases and related pester tests' (#3) from pester-dev into main
Reviewed-on: public/ps-modules-giteautilities#3
2025-04-30 15:00:34 -04:00
34fcb73d32 Add unit tests for Get-GiteaReleases function to validate behavior and error handling 2025-04-30 14:59:51 -04:00
172922ba61 Add Get-GiteaReleases function to retrieve releases from a Gitea repository 2025-04-29 17:18:00 -04:00
24f2e90cc9 Merge pull request 'Merge in pester tests' (#2) from pester-dev into main
Reviewed-on: public/ps-modules-giteautilities#2
2025-04-28 08:21:34 -04:00
135af8dfb8 Add CI workflow to run Pester tests before deployment 2025-04-28 08:21:09 -04:00
c5c9f81c50 Add unit tests for Set-GiteaConfiguration, Get-GiteaConfiguration, and Get-GiteaFileContent functions 2025-04-25 17:33:38 -04:00
68da95104b Bump module version to 1.2.0
All checks were successful
Publish Powershell Module to Gitea Repository / deploy (push) Successful in 34s
2025-04-25 16:12:59 -04:00
6ec0944a32 Merge pull request '1.2.0-rc1' (#1) from 1.2.0-rc1 into main
Reviewed-on: public/ps-modules-giteautilities#1
2025-04-25 16:12:25 -04:00
9cdcfeddff Refactor Get-GiteaChildItem to improve LFS handling; extract size and SHA from LFS pointers. Remove excess entries in returned object. 2025-04-25 16:10:52 -04:00
8e1a5f187a Enhance Get-GiteaChildItem to retrieve and parse LFS pointer details for size and SHA; update download URL for LFS files. 2025-04-25 15:44:37 -04:00
dfb34f240b Refactor verbose logging to debug level for token information; enhance Get-GiteaChildItem to handle LFS pointer details and update download URLs. 2025-04-25 15:03:33 -04:00
0c40d07bcd Remove Get-GiteaLFSFile function to streamline codebase; enhance clarity and maintainability. 2025-04-25 14:25:13 -04:00
3cdf0a6610 Add LoadVariables and Force switches to Get-GiteaConfiguration; implement Get-GiteaLFSFile for LFS file retrieval 2025-04-25 14:07:34 -04:00
9 changed files with 1017 additions and 93 deletions

View File

@@ -8,7 +8,25 @@ on:
workflow_dispatch: workflow_dispatch:
jobs: jobs:
test:
runs-on: ubuntu-22.04
container:
image: catthehacker/ubuntu:pwsh-latest
steps:
- name: Check out repository code
uses: actions/checkout@v4
- name: Run pester tests and verify all tests pass
shell: pwsh
run: |
Write-Host "Changing to repository directory: ${{ github.workspace }}"
Set-Location -Path ${{ github.workspace }}
Write-Host "Running Pester tests"
Invoke-Pester -Path . -PassThru | Select-Object -ExpandProperty FailedCount | Should -Be 0 -ErrorAction stop
deploy: deploy:
needs: test
runs-on: ubuntu-22.04 runs-on: ubuntu-22.04
container: container:
image: catthehacker/ubuntu:pwsh-latest image: catthehacker/ubuntu:pwsh-latest
@@ -54,7 +72,7 @@ jobs:
Set-Location -Path ${{ github.workspace }} Set-Location -Path ${{ github.workspace }}
$moduleVersion = git describe --tags $moduleVersion = git describe --tags
Write-Host "Module version: $moduleVersion" Write-Host "Module version: $moduleVersion"
$module = get-childItem -path . -Directory -Exclude ".*" | Select-Object -ExpandProperty Name $module = get-childItem -path . -Directory -Exclude (".*", "Tests") | Select-Object -ExpandProperty Name
Write-Host "Publishing module $module to Gitea" Write-Host "Publishing module $module to Gitea"
Publish-Module -Path $module -Repository 'GiteaPublic' -NuGetApiKey "${{ secrets.PACKAGES_TOKEN }}" Publish-Module -Path $module -Repository 'GiteaPublic' -NuGetApiKey "${{ secrets.PACKAGES_TOKEN }}"
Write-Host "Module $module published to Gitea" Write-Host "Module $module published to Gitea"

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.1.0' ModuleVersion = '1.4.3'
# Supported PSEditions # Supported PSEditions
# CompatiblePSEditions = @() # CompatiblePSEditions = @()
@@ -69,7 +69,7 @@ PowerShellVersion = '5.1'
# NestedModules = @() # NestedModules = @()
# Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export. # Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export.
FunctionsToExport = 'Set-GiteaConfiguration', 'Get-GiteaConfiguration', 'Get-GiteaFileContent', 'Invoke-GiteaFileDownload', 'Get-GiteaChildItem' FunctionsToExport = 'Set-GiteaConfiguration', 'Get-GiteaConfiguration', 'Get-GiteaFileContent', 'Invoke-GiteaFileDownload', 'Get-GiteaChildItem', 'Get-GiteaReleases'
# Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export. # Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export.
CmdletsToExport = '*' CmdletsToExport = '*'

View File

@@ -11,7 +11,7 @@ Function Set-GiteaConfiguration {
) )
# Create configuration directory if it doesn't exist # Create configuration directory if it doesn't exist
$configDir = Join-Path -Path $env:USERPROFILE -ChildPath ".giteautils" $configDir = Join-Path -Path $HOME -ChildPath ".giteautils"
if (-not (Test-Path -Path $configDir)) { if (-not (Test-Path -Path $configDir)) {
New-Item -Path $configDir -ItemType Directory | Out-Null New-Item -Path $configDir -ItemType Directory | Out-Null
} }
@@ -31,12 +31,42 @@ Function Set-GiteaConfiguration {
Function Get-GiteaConfiguration { Function Get-GiteaConfiguration {
[CmdletBinding()] [CmdletBinding()]
param() param(
[switch]$LoadVariables,
[switch]$Force
)
$configPath = Join-Path -Path $env:USERPROFILE -ChildPath ".giteautils\config.xml" $configPath = Join-Path -Path $HOME -ChildPath ".giteautils/config.xml"
if (Test-Path -Path $configPath) { if (Test-Path -Path $configPath) {
return Import-Clixml -Path $configPath $config = Import-Clixml -Path $configPath
# If LoadVariables switch is used, set each config value as a variable in the global scope
if ($LoadVariables) {
foreach ($key in $config.Keys) {
# Check if variable exists in global scope
$variableExists = $false
try {
$existingVar = Get-Variable -Name $key -Scope Global -ErrorAction Stop
$variableExists = $true
}
catch {
$variableExists = $false
}
# Set variable if it doesn't exist or if Force is used
if (-not $variableExists -or $Force) {
Write-Verbose "Loading configuration variable: $key = $($config[$key])"
Set-Variable -Name $key -Value $config[$key] -Scope Global
Write-Host "Created global variable: `$$key" -ForegroundColor Green
}
else {
Write-Verbose "Skipping existing variable: $key (use -Force to override)"
}
}
}
return $config
} }
else { else {
Write-Warning "Gitea configuration not found. Use Set-GiteaConfiguration to set up." Write-Warning "Gitea configuration not found. Use Set-GiteaConfiguration to set up."
@@ -139,7 +169,7 @@ Function Get-GiteaFileContent {
Write-Verbose "repoOwner: $repoOwner" Write-Verbose "repoOwner: $repoOwner"
Write-Verbose "repoName: $repoName" Write-Verbose "repoName: $repoName"
Write-Verbose "branch: $branch" Write-Verbose "branch: $branch"
Write-Verbose "token: $token" Write-Debug "Token: $token"
Write-Verbose "decode: $decode" Write-Verbose "decode: $decode"
$headers = @{ $headers = @{
@@ -150,6 +180,8 @@ Function Get-GiteaFileContent {
process { process {
foreach ($file in $filePath) { foreach ($file in $filePath) {
# Normalize path separators for cross-platform compatibility
$file = $file -replace '\\', '/'
Write-Verbose "Processing file: $file" Write-Verbose "Processing file: $file"
$encodedFile = [System.Uri]::EscapeDataString($file) $encodedFile = [System.Uri]::EscapeDataString($file)
Write-Verbose "Encoded file: $encodedFile" Write-Verbose "Encoded file: $encodedFile"
@@ -161,7 +193,6 @@ Function Get-GiteaFileContent {
$url += "/$encodedFile" $url += "/$encodedFile"
$url += "?ref=$branch" $url += "?ref=$branch"
Write-Verbose "URL: $url" Write-Verbose "URL: $url"
try { try {
$fileContent = Invoke-RestMethod -Uri $url -Method Get -Headers $headers $fileContent = Invoke-RestMethod -Uri $url -Method Get -Headers $headers
@@ -249,7 +280,7 @@ Function Get-GiteaLFSConfiguration {
Write-Verbose "giteaURL: $giteaURL" Write-Verbose "giteaURL: $giteaURL"
Write-Verbose "repoOwner: $repoOwner" Write-Verbose "repoOwner: $repoOwner"
Write-Verbose "repoName: $repoName" Write-Verbose "repoName: $repoName"
Write-Verbose "token: $token" Write-Debug "Token: $token"
$filePath = ".gitattributes" $filePath = ".gitattributes"
@@ -293,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.
@@ -313,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
@@ -335,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
) )
@@ -359,25 +396,29 @@ Function Invoke-GiteaFileDownload {
} }
Write-Verbose "Parameters:" Write-Verbose "Parameters:"
Write-Verbose "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 {
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"
@@ -388,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
@@ -402,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
@@ -462,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
} }
@@ -615,7 +665,7 @@ Function Get-GiteaChildItem {
Write-Verbose "repoOwner: $repoOwner" Write-Verbose "repoOwner: $repoOwner"
Write-Verbose "repoName: $repoName" Write-Verbose "repoName: $repoName"
Write-Verbose "Path: $Path" Write-Verbose "Path: $Path"
Write-Verbose "token: $token" Write-Debug "Token: $token"
Write-Verbose "File: $File" Write-Verbose "File: $File"
$headers = @{ $headers = @{
@@ -634,11 +684,10 @@ Function Get-GiteaChildItem {
$paths = $path $paths = $path
foreach ($path in $paths) { foreach ($path in $paths) {
Write-Verbose "Processing path: $path" Write-Verbose "Processing path: $path"
# Normalize the path format - replace backslashes with forward slashes and trim trailing slashes # Normalize path separators for cross-platform compatibility
$normalizedPath = $path -replace '\\', '/' -replace '/$', '' $normalizedPath = $path -replace '\\', '/' -replace '/$', ''
$encodedPath = [System.Uri]::EscapeDataString($normalizedPath)
Write-Verbose "Normalized path: $normalizedPath" Write-Verbose "Normalized path: $normalizedPath"
Write-Verbose "Encoded path: $encodedPath" $encodedPath = [System.Uri]::EscapeDataString($normalizedPath)
$url = "$giteaURL" $url = "$giteaURL"
$url += "/api/v1/repos" $url += "/api/v1/repos"
$url += "/$repoOwner" $url += "/$repoOwner"
@@ -668,21 +717,31 @@ Function Get-GiteaChildItem {
} }
if ($isLFS) { if ($isLFS) {
$item.type = "lfs" $item.type = "lfs"
$item.download_url = "$giteaURL/api/v1/repos/$repoOwner/$repoName/media/$($item.path)" # Save the original download URL for LFS pointers to be used to populate the size and sha fields
$GitLFSPointerURL = $item.download_url
# Use the Gitea API to get the LFS pointer details
# For LFS files, we need to get the pointer file and parse it for details
$lfsPointerContent = Invoke-RestMethod -Uri $GitLFSPointerURL -Method Get -Headers $headers -ErrorAction Stop
Write-Debug "LFS Pointer Content: $($lfsPointerContent)"
# Parse the LFS pointer to extract size and SHA
if ($lfsPointerContent -match 'oid sha256:([a-f0-9]+)') {
$item.sha = $matches[1]
}
if ($lfsPointerContent -match 'size (\d+)') {
$item.size = [long]$matches[1]
}
# Set the download URL to the media endpoint for LFS files to download the actual file
$item.download_url = "$giteaURL/api/v1/repos/$repoOwner/$repoName/media/$($item.path)?ref=$branch"
} }
$itemObj = [PSCustomObject]@{ $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 name = $item.name
Path = $item.path
type = $item.type
size = $item.size size = $item.size
sha = $item.sha sha = $item.sha
downloadURL = $item.download_url
Success = $true Success = $true
Error = $null Error = $null
Level = 0 Level = 0
@@ -741,21 +800,31 @@ Function Get-GiteaChildItem {
} }
if ($isLFS) { if ($isLFS) {
$subItem.type = "lfs" $subItem.type = "lfs"
# Save the original download URL for LFS pointers to be used to populate the size and sha fields
$GitLFSPointerURL = $subItem.download_url
# Use the Gitea API to get the LFS pointer details
# For LFS files, we need to get the pointer file and parse it for details
$lfsPointerContent = Invoke-RestMethod -Uri $GitLFSPointerURL -Method Get -Headers $headers -ErrorAction Stop
Write-Debug "LFS Pointer Content: $($lfsPointerContent)"
# Parse the LFS pointer to extract size and SHA
if ($lfsPointerContent -match 'oid sha256:([a-f0-9]+)') {
$subItem.sha = $matches[1]
}
if ($lfsPointerContent -match 'size (\d+)') {
$subItem.size = [long]$matches[1]
}
# Set the download URL to the media endpoint for LFS files to download the actual file
$subItem.download_url = "$giteaURL/api/v1/repos/$repoOwner/$repoName/media/$($subItem.path)" $subItem.download_url = "$giteaURL/api/v1/repos/$repoOwner/$repoName/media/$($subItem.path)"
} }
$subItemObj = [PSCustomObject]@{ $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 name = $subItem.name
Path = $subItem.path
type = $subItem.type
size = $subItem.size size = $subItem.size
sha = $subItem.sha sha = $subItem.sha
downloadURL = $subItem.download_url
Success = $true Success = $true
Error = $null Error = $null
Level = $currentLevel Level = $currentLevel
@@ -793,7 +862,6 @@ Function Get-GiteaChildItem {
Write-Error "Failed to retrieve '$normalizedPath' from Gitea: $($_.Exception.Message)`n$($errorDetails | ConvertTo-Json -Depth 1 -Compress)" Write-Error "Failed to retrieve '$normalizedPath' from Gitea: $($_.Exception.Message)`n$($errorDetails | ConvertTo-Json -Depth 1 -Compress)"
$results += [PSCustomObject]@{ $results += [PSCustomObject]@{
filePath = $normalizedPath
Path = $normalizedPath Path = $normalizedPath
repoOwner = $repoOwner repoOwner = $repoOwner
repoName = $repoName repoName = $repoName
@@ -823,4 +891,142 @@ Function Get-GiteaChildItem {
} }
} }
Export-ModuleMember -Function Set-GiteaConfiguration, Get-GiteaConfiguration, Get-GiteaFileContent, Invoke-GiteaFileDownload, Get-GiteaChildItem function Get-GiteaReleases {
<#
.SYNOPSIS
Retrieves the releases for a Gitea repository.
.DESCRIPTION
This function retrieves the releases for a Gitea repository using the Gitea API. The function requires the URL of the Gitea server, the owner of the repository, the name of the repository, and a personal access token.
#>
[cmdletbinding()]
param(
[Parameter(ValueFromPipelineByPropertyName)]
[string]$giteaURL = "https://gitea.norwichct.tech",
[Parameter(ValueFromPipelineByPropertyName)]
[string]$repoOwner,
[Parameter(ValueFromPipelineByPropertyName)]
[string]$repoName,
[int]$page,
[int]$limit,
[switch]$includeDrafts,
[switch]$includePreReleases,
[string]$token
)
begin {
# Use configuration if parameters aren't provided
if (-not $PSBoundParameters.ContainsKey('giteaURL') -or
-not $PSBoundParameters.ContainsKey('repoOwner') -or
-not $PSBoundParameters.ContainsKey('token')) {
$config = Get-GiteaConfiguration
if ($config) {
if (-not $PSBoundParameters.ContainsKey('giteaURL')) { $giteaURL = $config.giteaURL }
if (-not $PSBoundParameters.ContainsKey('repoOwner')) { $repoOwner = $config.defaultOwner }
if (-not $PSBoundParameters.ContainsKey('token')) { $token = $config.token }
}
}
# Validate that we have all required parameters
$missingParams = @()
if (-not $giteaURL) { $missingParams += "giteaURL" }
if (-not $repoOwner) { $missingParams += "repoOwner" }
if (-not $token) { $missingParams += "token" }
if ($missingParams.Count -gt 0) {
throw "Missing required parameters: $($missingParams -join ', '). Either provide them directly or set them with Set-GiteaConfiguration."
}
$headers = @{
"Authorization" = "token $token"
"Accept" = "application/json"
}
}
process {
Write-Verbose "Parameters:"
Write-Verbose "giteaURL: $giteaURL"
Write-Verbose "repoOwner: $repoOwner"
Write-Verbose "repoName: $repoName"
Write-Verbose "page: $page"
Write-Verbose "limit: $limit"
Write-Verbose "includeDrafts: $includeDrafts"
Write-Verbose "includePreReleases: $includePreReleases"
Write-Debug "Token: $token"
Write-Verbose "Processing releases for repository: $repoName"
$url = "$giteaURL"
$url += "/api/v1/repos"
$url += "/$repoOwner"
$url += "/$repoName"
$url += "/releases"
if ($includeDrafts -eq $true) {
$url += "?draft=true"
}
else {
$url += "?draft=false"
}
if ($includePreReleases -eq $true) {
$url += "&pre-release=true"
}
if ($page -gt 1) {
$url += "&page=$page"
}
if ($limit -gt 0) {
$url += "&limit=$limit"
}
Write-Verbose "URL: $url"
try {
$result = Invoke-RestMethod -Uri $url -Method Get -Headers $headers
$releases = foreach ($release in $result) {
# Process each asset into its own object
$assets = foreach ($asset in $release.assets) {
[PSCustomObject]@{
Id = $asset.id
Name = $asset.name
Size = $asset.size
DownloadCount = $asset.download_count
CreatedAt = [DateTime]$asset.created_at
UUID = $asset.uuid
DownloadURL = $asset.browser_download_url
}
}
# Create the release object with nested author and assets
[PSCustomObject]@{
Id = $release.id
TagName = $release.tag_name
TargetCommit = $release.target_commitish
Name = $release.name
Description = $release.body
URL = $release.url
HTMLURL = $release.html_url
TarballURL = $release.tarball_url
ZipballURL = $release.zipball_url
UploadURL = $release.upload_url
IsDraft = $release.draft
IsPrerelease = $release.prerelease
CreatedAt = [DateTime]$release.created_at
PublishedAt = [DateTime]$release.published_at
Author = [PSCustomObject]@{
Id = $release.author.id
Username = $release.author.username
Email = $release.author.email
AvatarURL = $release.author.avatar_url
HTMLURL = $release.author.html_url
}
Assets = $assets
}
}
return $releases
}
catch {
Write-Error "Failed to retrieve file '$file' from Gitea: $_"
}
}
}
# Export-ModuleMember -Function Set-GiteaConfiguration, Get-GiteaConfiguration, Get-GiteaFileContent, Invoke-GiteaFileDownload, Get-GiteaChildItem, Get-GiteaReleases

View File

@@ -0,0 +1,173 @@
# Get-GiteaChildItem.Tests.ps1
# Import the module under test
$modulePath = Join-Path -Path $PSScriptRoot -ChildPath '..\PS-GiteaUtilities\PS-GiteaUtilities.psm1'
Import-Module -Name $modulePath -Force
Describe 'Get-GiteaChildItem' {
BeforeEach {
# Mock config load
Mock -CommandName Get-GiteaConfiguration -ModuleName PS-GiteaUtilities -MockWith {
return @{
giteaURL = 'https://mock.gitea.com'
defaultOwner = 'mockuser'
defaultRepo = 'mockrepo'
defaultBranch = 'main'
token = 'mocktoken'
}
}
# Mock LFS config
Mock -CommandName Get-GiteaLFSConfiguration -ModuleName PS-GiteaUtilities -MockWith {
return @(".lfs")
}
# Mock web call to list files
Mock -CommandName Invoke-RestMethod -ModuleName PS-GiteaUtilities -MockWith {
return @(
@{
name = "file1.txt"
path = "file1.txt"
type = "file"
size = 123
sha = "abc123"
download_url = "https://mock.gitea.com/file1.txt"
},
@{
name = "subdir"
path = "subdir"
type = "dir"
size = 0
sha = $null
download_url = $null
}
)
}
Mock -CommandName Write-Error -ModuleName PS-GiteaUtilities -MockWith { }
}
Context 'When listing basic items' {
It 'Should return files and directories correctly' {
$result = Get-GiteaChildItem -repoOwner 'mockuser' -repoName 'mockrepo' -Path '' -token 'mocktoken'
$result | Should -Not -BeNullOrEmpty
$result.Count | Should -BeGreaterThan 0
$file = $result | Where-Object { $_.type -eq 'file' }
$dir = $result | Where-Object { $_.type -eq 'dir' }
$file.name | Should -Be 'file1.txt'
$dir.name | Should -Be 'subdir'
}
}
Context 'When filtering only files' {
It 'Should return only files when -File is used' {
$result = Get-GiteaChildItem -repoOwner 'mockuser' -repoName 'mockrepo' -Path '' -token 'mocktoken' -File
$result | Should -Not -BeNullOrEmpty
$result.Count | Should -Be 1
$result[0].type | Should -Be 'file'
}
}
Context 'When recursing into directories' {
BeforeEach {
# Mock recursion call
Mock -CommandName Invoke-RestMethod -ModuleName PS-GiteaUtilities -MockWith {
param($Uri)
if ($Uri -like "*subdir*") {
return @(
@{
name = "nestedfile.txt"
path = "subdir/nestedfile.txt"
type = "file"
size = 456
sha = "def456"
download_url = "https://mock.gitea.com/subdir/nestedfile.txt"
}
)
} else {
return @(
@{
name = "file1.txt"
path = "file1.txt"
type = "file"
size = 123
sha = "abc123"
download_url = "https://mock.gitea.com/file1.txt"
},
@{
name = "subdir"
path = "subdir"
type = "dir"
size = 0
sha = $null
download_url = $null
}
)
}
}
}
It 'Should retrieve nested files when -Recurse is used' {
$result = Get-GiteaChildItem -repoOwner 'mockuser' -repoName 'mockrepo' -Path '' -token 'mocktoken' -Recurse
$nested = $result | Where-Object { $_.Path -eq 'subdir/nestedfile.txt' }
$nested | Should -Not -BeNullOrEmpty
$nested.size | Should -Be 456
}
}
Context 'When recursion depth limit is enforced' {
BeforeEach {
# Same recursion mock as above
Mock -CommandName Invoke-RestMethod -ModuleName PS-GiteaUtilities -MockWith {
param($Uri)
if ($Uri -like "*subdir*") {
return @(
@{
name = "nestedfile.txt"
path = "subdir/nestedfile.txt"
type = "file"
size = 456
sha = "def456"
download_url = "https://mock.gitea.com/subdir/nestedfile.txt"
}
)
} else {
return @(
@{
name = "file1.txt"
path = "file1.txt"
type = "file"
size = 123
sha = "abc123"
download_url = "https://mock.gitea.com/file1.txt"
},
@{
name = "subdir"
path = "subdir"
type = "dir"
size = 0
sha = $null
download_url = $null
}
)
}
}
}
It 'Should not recurse deeper than depth limit' {
$result = Get-GiteaChildItem -repoOwner 'mockuser' -repoName 'mockrepo' -Path '' -token 'mocktoken' -Recurse -Depth 0
$nested = $result | Where-Object { $_.Path -eq 'subdir/nestedfile.txt' }
$nested | Should -BeNullOrEmpty
}
}
}

View File

@@ -0,0 +1,75 @@
# Get-GiteaConfiguration.Tests.ps1
# Import the module under test
$modulePath = Join-Path -Path $PSScriptRoot -ChildPath '..\PS-GiteaUtilities\PS-GiteaUtilities.psm1'
Import-Module -Name $modulePath -Force
Describe 'Get-GiteaConfiguration' {
Context 'When configuration file exists' {
BeforeEach {
# Mock filesystem and import
Mock -CommandName Test-Path -ModuleName PS-GiteaUtilities -MockWith { $true }
Mock -CommandName Import-Clixml -ModuleName PS-GiteaUtilities -MockWith {
return @{
giteaURL = 'https://gitea.example.com'
token = 'ABC123'
}
}
}
It 'Should import configuration and return it' {
$result = Get-GiteaConfiguration
$result | Should -Not -BeNullOrEmpty
$result.giteaURL | Should -Be 'https://gitea.example.com'
$result.token | Should -Be 'ABC123'
}
It 'Should create global variables when LoadVariables is used' {
# Remove any existing variables first
Remove-Variable -Name giteaURL, token -Scope Global -ErrorAction SilentlyContinue
Get-GiteaConfiguration -LoadVariables
(Get-Variable -Name giteaURL -Scope Global).Value | Should -Be 'https://gitea.example.com'
(Get-Variable -Name token -Scope Global).Value | Should -Be 'ABC123'
}
It 'Should overwrite existing variables when Force is used' {
# Set initial variables
Set-Variable -Name giteaURL -Value 'OldURL' -Scope Global
Set-Variable -Name token -Value 'OldToken' -Scope Global
Get-GiteaConfiguration -LoadVariables -Force
(Get-Variable -Name giteaURL -Scope Global).Value | Should -Be 'https://gitea.example.com'
(Get-Variable -Name token -Scope Global).Value | Should -Be 'ABC123'
}
It 'Should not overwrite existing variables without Force' {
# Set initial variables
Set-Variable -Name giteaURL -Value 'OldURL' -Scope Global
Set-Variable -Name token -Value 'OldToken' -Scope Global
Get-GiteaConfiguration -LoadVariables
(Get-Variable -Name giteaURL -Scope Global).Value | Should -Be 'OldURL'
(Get-Variable -Name token -Scope Global).Value | Should -Be 'OldToken'
}
}
Context 'When configuration file does not exist' {
BeforeEach {
Mock -CommandName Test-Path -ModuleName PS-GiteaUtilities -MockWith { $false }
Mock -CommandName Write-Warning -ModuleName PS-GiteaUtilities -MockWith { }
}
It 'Should return null and warn the user' {
$result = Get-GiteaConfiguration
$result | Should -BeNullOrEmpty
Assert-MockCalled -CommandName Write-Warning -ModuleName PS-GiteaUtilities -Times 1
}
}
}

View File

@@ -0,0 +1,119 @@
# Get-GiteaFileContent.Tests.ps1
# Import the module under test
$modulePath = Join-Path -Path $PSScriptRoot -ChildPath '..\PS-GiteaUtilities\PS-GiteaUtilities.psm1'
Import-Module -Name $modulePath -Force
Describe 'Get-GiteaFileContent' {
Context 'When all parameters are provided' {
BeforeEach {
Mock -CommandName Invoke-RestMethod -ModuleName PS-GiteaUtilities -MockWith {
return @{
content = [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes('Hello World'))
size = 11
sha = 'fake-sha'
}
}
}
It 'Should return file content without decoding by default' {
$result = Get-GiteaFileContent -repoOwner 'user' -repoName 'repo' -filePath 'README.md' -token 'abc123'
$result | Should -Not -BeNullOrEmpty
$result.Success | Should -Be $true
$result.Content | Should -Be ([System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes('Hello World')))
}
It 'Should decode content when -decode is used' {
$result = Get-GiteaFileContent -repoOwner 'user' -repoName 'repo' -filePath 'README.md' -token 'abc123' -decode
$result | Should -Not -BeNullOrEmpty
$result.Success | Should -Be $true
$result.Content | Should -Be 'Hello World'
}
}
Context 'When missing parameters and config is loaded' {
BeforeEach {
Mock -CommandName Get-GiteaConfiguration -ModuleName PS-GiteaUtilities -MockWith {
return @{
giteaURL = 'https://mock.gitea.com'
defaultOwner = 'mockuser'
defaultRepo = 'mockrepo'
defaultBranch = 'main'
token = 'mocktoken'
}
}
Mock -CommandName Invoke-RestMethod -ModuleName PS-GiteaUtilities -MockWith {
return @{
content = [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes('Mock Config Content'))
size = 20
sha = 'mock-sha'
}
}
}
It 'Should load configuration from Get-GiteaConfiguration if not all parameters are given' {
$result = Get-GiteaFileContent -filePath 'test.ps1'
$result | Should -Not -BeNullOrEmpty
$result.Path | Should -Be 'test.ps1'
$result.Content | Should -Be ([System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes('Mock Config Content')))
}
}
Context 'When API call fails' {
BeforeEach {
Mock -CommandName Invoke-RestMethod -ModuleName PS-GiteaUtilities -MockWith { throw "API call failed" }
Mock -CommandName Write-Error -ModuleName PS-GiteaUtilities -MockWith { }
}
It 'Should capture error and mark result as unsuccessful' {
$result = Get-GiteaFileContent -repoOwner 'user' -repoName 'repo' -filePath 'badfile.ps1' -token 'abc123'
$result.Success | Should -Be $false
$result.Error | Should -Match 'API call failed'
}
}
Context 'Parameter validation' {
It 'Should throw if required parameters are missing and no config' {
Mock -CommandName Get-GiteaConfiguration -ModuleName PS-GiteaUtilities -MockWith { return $null }
{ Get-GiteaFileContent -filePath 'something.ps1' } | Should -Throw
}
}
Context 'When decoding file content' {
BeforeEach {
# Simulate API returning Base64 content
Mock -CommandName Invoke-RestMethod -ModuleName PS-GiteaUtilities -MockWith {
return @{
content = [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes('Hello from API!'))
size = 15
sha = 'fake-sha-for-decode'
}
}
}
It 'Should return base64 encoded content by default (no decode switch)' {
$result = Get-GiteaFileContent -repoOwner 'user' -repoName 'repo' -filePath 'testfile.txt' -token 'abc123'
$result | Should -Not -BeNullOrEmpty
$result.Success | Should -Be $true
$result.Content | Should -Be ([System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes('Hello from API!')))
}
It 'Should decode base64 content into plain text when -decode is used' {
$result = Get-GiteaFileContent -repoOwner 'user' -repoName 'repo' -filePath 'testfile.txt' -token 'abc123' -decode
$result | Should -Not -BeNullOrEmpty
$result.Success | Should -Be $true
$result.Content | Should -Be 'Hello from API!'
}
}
}

View File

@@ -0,0 +1,117 @@
# Get-GiteaReleases.Tests.ps1
# Import the module
$modulePath = Join-Path -Path $PSScriptRoot -ChildPath '..\PS-GiteaUtilities\PS-GiteaUtilities.psm1'
Import-Module -Name $modulePath -Force
Describe 'Get-GiteaReleases' {
BeforeEach {
# Mock configuration load
Mock -CommandName Get-GiteaConfiguration -ModuleName PS-GiteaUtilities -MockWith {
return @{
giteaURL = 'https://mock.gitea.com'
defaultOwner = 'mockuser'
token = 'mocktoken'
}
}
# Mock API response
Mock -CommandName Invoke-RestMethod -ModuleName PS-GiteaUtilities -MockWith {
return @(
@{
id = 1
tag_name = "v1.0"
target_commitish = "main"
name = "Release 1"
body = "Initial release"
url = "https://mock.gitea.com/api/v1/releases/1"
html_url = "https://mock.gitea.com/releases/v1.0"
tarball_url = "https://mock.gitea.com/releases/v1.0.tar.gz"
zipball_url = "https://mock.gitea.com/releases/v1.0.zip"
upload_url = "https://mock.gitea.com/releases/uploads"
draft = $false
prerelease = $false
created_at = "2024-01-01T00:00:00Z"
published_at = "2024-01-01T01:00:00Z"
author = @{
id = 101
username = "devuser"
email = "dev@norwichct.org"
avatar_url = "https://mock.gitea.com/avatar.jpg"
html_url = "https://mock.gitea.com/devuser"
}
assets = @(
@{
id = 201
name = "tool.exe"
size = 123456
download_count = 42
created_at = "2024-01-01T00:30:00Z"
uuid = "uuid-1234"
browser_download_url = "https://mock.gitea.com/releases/assets/tool.exe"
}
)
}
)
}
Mock -CommandName Write-Error -ModuleName PS-GiteaUtilities -MockWith { }
}
Context 'Basic usage' {
It 'Should return a list of release objects' {
$result = Get-GiteaReleases -repoOwner 'mockuser' -repoName 'mockrepo' -token 'mocktoken'
$result | Should -Not -BeNullOrEmpty
$result[0].Name | Should -Be 'Release 1'
$result[0].Author.Username | Should -Be 'devuser'
$result[0].Assets.Count | Should -Be 1
$result[0].Assets[0].Name | Should -Be 'tool.exe'
}
}
Context 'Optional switches affect URL' {
It 'Should include draft=true and pre-release=true in URL if switches are set' {
$calledUrl = $null
Mock -CommandName Invoke-RestMethod -ModuleName PS-GiteaUtilities -MockWith {
param($Uri)
$script:calledUrl = $Uri
return @()
}
Get-GiteaReleases -repoOwner 'mockuser' -repoName 'mockrepo' -includeDrafts -includePreReleases -page 2 -limit 10 -token 'mocktoken'
$script:calledUrl | Should -Match 'draft=true'
$script:calledUrl | Should -Match 'pre-release=true'
$script:calledUrl | Should -Match 'page=2'
$script:calledUrl | Should -Match 'limit=10'
}
}
Context 'When required params are missing and no config' {
BeforeEach {
Mock -CommandName Get-GiteaConfiguration -ModuleName PS-GiteaUtilities -MockWith { return $null }
}
It 'Should throw when required parameters are not supplied' {
{ Get-GiteaReleases -repoName 'mockrepo' } | Should -Throw
}
}
Context 'When API call fails' {
BeforeEach {
Mock -CommandName Invoke-RestMethod -ModuleName PS-GiteaUtilities -MockWith {
throw "API call failed"
}
Mock -CommandName Write-Error -ModuleName PS-GiteaUtilities -MockWith { }
}
It 'Should catch and handle the error' {
$null = Get-GiteaReleases -repoOwner 'mockuser' -repoName 'mockrepo' -token 'mocktoken'
# Expect no throw, Write-Error should catch it
}
}
}

View File

@@ -0,0 +1,152 @@
# Invoke-GiteaFileDownload.Tests.ps1
# Import the module under test
$modulePath = Join-Path -Path $PSScriptRoot -ChildPath '..\PS-GiteaUtilities\PS-GiteaUtilities.psm1'
Import-Module -Name $modulePath -Force
Describe 'Invoke-GiteaFileDownload' {
BeforeEach {
# Always mock Get-GiteaConfiguration to avoid needing real config
Mock -CommandName Get-GiteaConfiguration -ModuleName PS-GiteaUtilities -MockWith {
return @{ token = 'mocktoken' }
}
# Correct webClient Mock: headers, download, dispose
Mock -CommandName New-Object -ModuleName PS-GiteaUtilities -ParameterFilter { $TypeName -eq 'System.Net.WebClient' } -MockWith {
$headers = New-Object System.Net.WebHeaderCollection
# Create a real object
$webClient = New-Object PSObject
# Add Headers
Add-Member -InputObject $webClient -MemberType NoteProperty -Name Headers -Value $headers
# Add a DownloadFile method
Add-Member -InputObject $webClient -MemberType ScriptMethod -Name DownloadFile -Value {
param($url, $path)
# Fake download (do nothing)
}
# Add a Dispose method
Add-Member -InputObject $webClient -MemberType ScriptMethod -Name Dispose -Value {
# Fake dispose (do nothing)
}
return $webClient
}
# Mock filesystem behavior
Mock -CommandName Test-Path -ModuleName PS-GiteaUtilities -MockWith { $false }
Mock -CommandName New-Item -ModuleName PS-GiteaUtilities -MockWith { }
# Clean captured download data before each test
$script:LastDownloadUrl = $null
$script:LastDownloadPath = $null
}
Context 'When all parameters are valid' {
It 'Should download a file successfully' {
$result = Invoke-GiteaFileDownload -downloadURL 'https://gitea.example.com/raw/path/to/file.txt' -token 'abc123'
$result | Should -Not -BeNullOrEmpty
$result.Success | Should -Be $true
$result.Path | Should -Match 'file\.txt$'
}
}
Context 'When specifying an outputPath' {
It 'Should create a path based on outputPath' {
$result = Invoke-GiteaFileDownload -downloadURL 'https://gitea.example.com/raw/path/to/file.txt' `
-outputPath 'docs/manual/' -token 'abc123'
$result.Path | Should -Match 'docs[\\/]+manual[\\/]+file\.txt$'
$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' {
It 'Should skip items with type = dir' {
$result = Invoke-GiteaFileDownload -outputPath 'docs/' -type 'dir' -token 'abc123'
$result.Success | Should -Be $true
$result.Error | Should -Match 'Skipped'
}
}
Context 'When file exists and -Force is not used' {
BeforeEach {
# Simulate file already exists
Mock -CommandName Test-Path -ModuleName PS-GiteaUtilities -MockWith {
param($Path, $PathType)
if ($PathType -eq 'Leaf') { return $true } else { return $false }
}
Mock -CommandName Write-Error -ModuleName PS-GiteaUtilities -MockWith { }
}
It 'Should not overwrite existing file without Force' {
$result = Invoke-GiteaFileDownload -downloadURL 'https://gitea.example.com/raw/path/to/existingfile.txt' -token 'abc123'
$result.Success | Should -Be $false
$result.Error | Should -Match 'already exists'
}
}
Context 'When download fails' {
BeforeEach {
Mock -CommandName New-Object -ModuleName PS-GiteaUtilities -ParameterFilter { $TypeName -eq 'System.Net.WebClient' } -MockWith {
$headers = New-Object System.Net.WebHeaderCollection
$webClient = New-Object PSObject
Add-Member -InputObject $webClient -MemberType NoteProperty -Name Headers -Value $headers
Add-Member -InputObject $webClient -MemberType ScriptMethod -Name DownloadFile -Value {
throw "Download failed intentionally"
}
Add-Member -InputObject $webClient -MemberType ScriptMethod -Name Dispose -Value {
return $true
}
return $webClient
}
Mock -CommandName Test-Path -ModuleName PS-GiteaUtilities -MockWith { $false }
Mock -CommandName New-Item -ModuleName PS-GiteaUtilities -MockWith { }
Mock -CommandName Write-Error -ModuleName PS-GiteaUtilities -MockWith { }
}
It 'Should capture error and mark download as failed' {
$result = Invoke-GiteaFileDownload -downloadURL 'https://gitea.example.com/raw/path/to/badfile.txt' -token 'abc123'
$result.Success | Should -Be $false
$result.Error | Should -Match 'Failed to download'
}
}
}

View File

@@ -0,0 +1,64 @@
# Set-GiteaConfiguration.Tests.ps1
Describe 'Set-GiteaConfiguration' {
BeforeAll {
# Import the module dynamically
$modulePath = Join-Path -Path $PSScriptRoot -ChildPath '..\PS-GiteaUtilities\PS-GiteaUtilities.psm1'
Import-Module -Name $modulePath -Force
}
Context 'Parameter Validation' {
It 'Should throw a MissingArgument exception if required parameters are missing' {
{ Set-GiteaConfiguration -giteaURL -token} | Should -Throw
}
}
Context 'Configuration Setup' {
BeforeEach {
Mock -CommandName Test-Path -ModuleName PS-GiteaUtilities -MockWith { $false }
Mock -CommandName New-Item -ModuleName PS-GiteaUtilities -MockWith { return $null }
Mock -CommandName Export-Clixml -ModuleName PS-GiteaUtilities -MockWith { return $null }
}
It 'Should create the configuration directory if it does not exist' {
Set-GiteaConfiguration -giteaURL 'https://gitea.example.com' -token 'ABC123' -defaultOwner 'testuser' -defaultRepo 'testrepo'
Assert-MockCalled -CommandName New-Item -ModuleName PS-GiteaUtilities -Times 1
}
It 'Should export the configuration to an XML file' {
Set-GiteaConfiguration -giteaURL 'https://gitea.example.com' -token 'ABC123' -defaultOwner 'testuser' -defaultRepo 'testrepo'
Assert-MockCalled -CommandName Export-Clixml -ModuleName PS-GiteaUtilities -Times 1
}
It 'Should set defaultBranch to "main" if not specified' {
# Important: override the Export-Clixml mock to capture the config
Mock -CommandName Export-Clixml -ModuleName PS-GiteaUtilities -MockWith {
param($Path, $InputObject)
$script:CapturedConfig = $InputObject
}
Set-GiteaConfiguration -giteaURL 'https://gitea.example.com' -token 'ABC123' -defaultOwner 'testuser' -defaultRepo 'testrepo'
$script:CapturedConfig.defaultBranch | Should -Be 'main'
}
}
Context 'When directory already exists' {
BeforeEach {
Mock -CommandName Test-Path -MockWith { $true }
Mock -CommandName New-Item -MockWith { throw "Should not be called" }
Mock -CommandName Export-Clixml -MockWith { return $null }
}
It 'Should not attempt to create the directory' {
Set-GiteaConfiguration -giteaURL 'https://gitea.example.com' -token 'ABC123' -defaultOwner 'testuser' -defaultRepo 'testrepo'
Assert-MockCalled -CommandName New-Item -Times 0
}
}
}