Compare commits
30 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| edb1501f40 | |||
| 5854940f75 | |||
| 02f83e6633 | |||
| d4c7aa5a92 | |||
| 15381257e5 | |||
| 1c09d40253 | |||
| 1d0c8b43bc | |||
| 445fa8a429 | |||
| dd47a1b7e2 | |||
| c65cde9fbc | |||
| c18f68bc46 | |||
| 51a3a15a4f | |||
| 0451788a13 | |||
| 34fcb73d32 | |||
| 172922ba61 | |||
| 24f2e90cc9 | |||
| 135af8dfb8 | |||
| c5c9f81c50 | |||
| 68da95104b | |||
| 6ec0944a32 | |||
| 9cdcfeddff | |||
| 8e1a5f187a | |||
| dfb34f240b | |||
| 0c40d07bcd | |||
| 3cdf0a6610 | |||
| d1d2366c4a | |||
| fb2a87ed84 | |||
| d8dfb1d1fe | |||
| 305ec3c2fb | |||
| e9d6aa255f |
@@ -8,7 +8,25 @@ on:
|
||||
workflow_dispatch:
|
||||
|
||||
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:
|
||||
needs: test
|
||||
runs-on: ubuntu-22.04
|
||||
container:
|
||||
image: catthehacker/ubuntu:pwsh-latest
|
||||
@@ -22,9 +40,9 @@ jobs:
|
||||
run: |
|
||||
$creds = New-Object System.Management.Automation.PSCredential('actions', ('{{ secrets.PACKAGES_TOKEN }}' | ConvertTo-SecureString -AsPlainText -Force))
|
||||
try {
|
||||
Register-PSRepository -Name 'GiteaPrivate' `
|
||||
-SourceLocation 'https://gitea.norwichct.tech/api/packages/cityofnorwich/nuget' `
|
||||
-PublishLocation 'https://gitea.norwichct.tech/api/packages/cityofnorwich/nuget' `
|
||||
Register-PSRepository -Name 'GiteaPublic' `
|
||||
-SourceLocation 'https://gitea.norwichct.tech/api/packages/public/nuget' `
|
||||
-PublishLocation 'https://gitea.norwichct.tech/api/packages/public/nuget' `
|
||||
-installationPolicy Trusted `
|
||||
-Credential $creds
|
||||
}
|
||||
@@ -54,9 +72,9 @@ jobs:
|
||||
Set-Location -Path ${{ github.workspace }}
|
||||
$moduleVersion = git describe --tags
|
||||
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"
|
||||
Publish-Module -Path $module -Repository 'GiteaPrivate' -NuGetApiKey "${{ secrets.PACKAGES_TOKEN }}"
|
||||
Publish-Module -Path $module -Repository 'GiteaPublic' -NuGetApiKey "${{ secrets.PACKAGES_TOKEN }}"
|
||||
Write-Host "Module $module published to Gitea"
|
||||
|
||||
- name: Create draft release
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
RootModule = 'PS-GiteaUtilities.psm1'
|
||||
|
||||
# Version number of this module.
|
||||
ModuleVersion = '1.0.0'
|
||||
ModuleVersion = '1.4.4'
|
||||
|
||||
# Supported PSEditions
|
||||
# CompatiblePSEditions = @()
|
||||
@@ -69,7 +69,7 @@ PowerShellVersion = '5.1'
|
||||
# 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.
|
||||
FunctionsToExport = 'Get-GiteaFileContent', 'Invoke-GiteaFileDownload'
|
||||
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.
|
||||
CmdletsToExport = '*'
|
||||
|
||||
@@ -1,3 +1,79 @@
|
||||
Function Set-GiteaConfiguration {
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[Parameter(Mandatory)]
|
||||
[string]$giteaURL,
|
||||
[Parameter(Mandatory)]
|
||||
[string]$token,
|
||||
[string]$defaultOwner,
|
||||
[string]$defaultRepo,
|
||||
[string]$defaultBranch = "main"
|
||||
)
|
||||
|
||||
# Create configuration directory if it doesn't exist
|
||||
$configDir = Join-Path -Path $HOME -ChildPath ".giteautils"
|
||||
if (-not (Test-Path -Path $configDir)) {
|
||||
New-Item -Path $configDir -ItemType Directory | Out-Null
|
||||
}
|
||||
|
||||
# Save configuration
|
||||
$config = @{
|
||||
giteaURL = $giteaURL
|
||||
token = $token
|
||||
defaultOwner = $defaultOwner
|
||||
defaultRepo = $defaultRepo
|
||||
defaultBranch = $defaultBranch
|
||||
}
|
||||
|
||||
$configPath = Join-Path -Path $configDir -ChildPath "config.xml"
|
||||
$config | Export-Clixml -Path $configPath -Force
|
||||
}
|
||||
|
||||
Function Get-GiteaConfiguration {
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[switch]$LoadVariables,
|
||||
[switch]$Force
|
||||
)
|
||||
|
||||
$configPath = Join-Path -Path $HOME -ChildPath ".giteautils/config.xml"
|
||||
|
||||
if (Test-Path -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 {
|
||||
Write-Warning "Gitea configuration not found. Use Set-GiteaConfiguration to set up."
|
||||
return $null
|
||||
}
|
||||
}
|
||||
|
||||
Function Get-GiteaFileContent {
|
||||
|
||||
<#
|
||||
@@ -42,28 +118,58 @@ Function Get-GiteaFileContent {
|
||||
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[Parameter(ValueFromPipelineByPropertyName)]
|
||||
[string]$giteaURL = "https://gitea.norwichct.tech",
|
||||
[Parameter(Mandatory)]
|
||||
[Parameter(ValueFromPipelineByPropertyName)]
|
||||
[string]$repoOwner,
|
||||
[Parameter(Mandatory)]
|
||||
[Parameter(ValueFromPipelineByPropertyName)]
|
||||
[string]$repoName,
|
||||
[Parameter(ValueFromPipelineByPropertyName)]
|
||||
[string]$branch = "main",
|
||||
[Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)]
|
||||
[Parameter(ValueFromPipeline, ValueFromPipelineByPropertyName)]
|
||||
[Alias('FullName', 'Path')]
|
||||
[string[]]$filePath,
|
||||
[Parameter(Mandatory)]
|
||||
[string]$token,
|
||||
[switch]$decode
|
||||
)
|
||||
|
||||
begin {
|
||||
# Use configuration if parameters aren't provided
|
||||
if (-not $PSBoundParameters.ContainsKey('giteaURL') -or
|
||||
-not $PSBoundParameters.ContainsKey('repoOwner') -or
|
||||
-not $PSBoundParameters.ContainsKey('repoName') -or
|
||||
-not $PSBoundParameters.ContainsKey('branch') -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('repoName')) { $repoName = $config.defaultRepo }
|
||||
if (-not $PSBoundParameters.ContainsKey('branch')) { $branch = $config.defaultBranch }
|
||||
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 $repoName) { $missingParams += "repoName" }
|
||||
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."
|
||||
}
|
||||
|
||||
|
||||
$results = @()
|
||||
Write-Verbose "Parameters:"
|
||||
Write-Verbose "giteaURL: $giteaURL"
|
||||
Write-Verbose "repoOwner: $repoOwner"
|
||||
Write-Verbose "repoName: $repoName"
|
||||
Write-Verbose "branch: $branch"
|
||||
Write-Verbose "token: $token"
|
||||
Write-Debug "Token: $token"
|
||||
Write-Verbose "decode: $decode"
|
||||
|
||||
$headers = @{
|
||||
@@ -74,6 +180,8 @@ Function Get-GiteaFileContent {
|
||||
|
||||
process {
|
||||
foreach ($file in $filePath) {
|
||||
# Normalize path separators for cross-platform compatibility
|
||||
$file = $file -replace '\\', '/'
|
||||
Write-Verbose "Processing file: $file"
|
||||
$encodedFile = [System.Uri]::EscapeDataString($file)
|
||||
Write-Verbose "Encoded file: $encodedFile"
|
||||
@@ -85,7 +193,6 @@ Function Get-GiteaFileContent {
|
||||
$url += "/$encodedFile"
|
||||
$url += "?ref=$branch"
|
||||
Write-Verbose "URL: $url"
|
||||
|
||||
try {
|
||||
$fileContent = Invoke-RestMethod -Uri $url -Method Get -Headers $headers
|
||||
|
||||
@@ -123,6 +230,82 @@ Function Get-GiteaFileContent {
|
||||
}
|
||||
}
|
||||
|
||||
Function Get-GiteaLFSConfiguration {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Retrieves the LFS configuration for a Gitea repository.
|
||||
|
||||
.DESCRIPTION
|
||||
This function retrieves the LFS configuration 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.
|
||||
The function returns the LFS configuration details.
|
||||
#>
|
||||
[cmdletbinding()]
|
||||
param(
|
||||
[string]$giteaURL,
|
||||
[string]$repoOwner,
|
||||
[string]$repoName,
|
||||
[string]$token
|
||||
)
|
||||
|
||||
begin {
|
||||
# Use configuration if parameters aren't provided
|
||||
if (-not $PSBoundParameters.ContainsKey('giteaURL') -or
|
||||
-not $PSBoundParameters.ContainsKey('repoOwner') -or
|
||||
-not $PSBoundParameters.ContainsKey('repoName') -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('repoName')) { $repoName = $config.defaultRepo }
|
||||
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 $repoName) { $missingParams += "repoName" }
|
||||
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."
|
||||
}
|
||||
}
|
||||
|
||||
process {
|
||||
Write-Verbose "Parameters:"
|
||||
Write-Verbose "giteaURL: $giteaURL"
|
||||
Write-Verbose "repoOwner: $repoOwner"
|
||||
Write-Verbose "repoName: $repoName"
|
||||
Write-Debug "Token: $token"
|
||||
|
||||
$filePath = ".gitattributes"
|
||||
|
||||
try {
|
||||
$LFSConfig = (Get-GiteaFileContent -filePath $filePath -giteaURL $giteaURL -repoOwner $repoOwner -repoName $repoName -branch "main" -token $token -decode -ErrorAction SilentlyContinue).content
|
||||
$LFSConfig = $LFSConfig -replace "\r?\n", "`n" # Normalize line endings
|
||||
# Get the extensions of files which are tracked by LFS
|
||||
# Parse the LFS configuration file and extract extensions
|
||||
$lfsExtensions = @()
|
||||
foreach ($line in $LFSConfig.Split("`n")) {
|
||||
if ($line -match '^\*\.([^\s]+)\s+filter=lfs') {
|
||||
$extension = "." + $matches[1]
|
||||
$lfsExtensions += $extension
|
||||
}
|
||||
}
|
||||
$lfsExtensions = $lfsExtensions | Sort-Object -Unique
|
||||
return $lfsExtensions
|
||||
}
|
||||
catch {
|
||||
Write-Error "Failed to retrieve LFS configuration: $_"
|
||||
return @()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Function Invoke-GiteaFileDownload {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
@@ -131,6 +314,9 @@ Function Invoke-GiteaFileDownload {
|
||||
.DESCRIPTION
|
||||
This function downloads a file from a Gitea repository using a direct download URL. The function requires the download URL and a personal access token. You can optionally specify an output path and use the force switch to overwrite existing files.
|
||||
|
||||
.PARAMETER giteaURL
|
||||
The URL of the Gitea server.
|
||||
|
||||
.PARAMETER downloadURL
|
||||
The direct download URL for the file from the Gitea server.
|
||||
|
||||
@@ -138,58 +324,721 @@ Function Invoke-GiteaFileDownload {
|
||||
A personal access token for the Gitea server.
|
||||
|
||||
.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 outputName
|
||||
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
|
||||
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.
|
||||
Cannot be used with outputName.
|
||||
|
||||
.PARAMETER force
|
||||
A switch parameter to force overwriting of an existing file at the output path.
|
||||
|
||||
.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
|
||||
Invoke-GiteaFileDownload -downloadURL "https://gitea.example.com/api/v1/repos/owner/repo/raw/path/to/file.txt" -token "your_token"
|
||||
|
||||
.EXAMPLE
|
||||
# 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 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)]
|
||||
[Parameter(ValueFromPipelineByPropertyName)]
|
||||
[string]$downloadURL,
|
||||
[Parameter(Mandatory)]
|
||||
[string]$token,
|
||||
[Parameter(ValueFromPipelineByPropertyName)]
|
||||
[string]$outputPath,
|
||||
[switch]$force
|
||||
[Parameter(ValueFromPipelineByPropertyName)]
|
||||
[string]$outputName,
|
||||
[switch]$PreserveRelativePath,
|
||||
[switch]$force,
|
||||
[Parameter(ValueFromPipelineByPropertyName)]
|
||||
[string]$type
|
||||
)
|
||||
|
||||
# If the output path is not specified, use the current directory and the file name from the download URL (everything after the last '/' and before the last character after the last '.', inclusive of the extension)
|
||||
if (-not $outputPath) {
|
||||
# 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])
|
||||
begin {
|
||||
# Use configuration if parameters aren't provided
|
||||
if (-not $PSBoundParameters.ContainsKey('token')) {
|
||||
|
||||
# Append the outputFileName to the current location
|
||||
$outputPath = Join-Path -Path (Get-Location) -ChildPath $outputFileName
|
||||
$config = Get-GiteaConfiguration
|
||||
if ($config) {
|
||||
if (-not $PSBoundParameters.ContainsKey('token')) { $token = $config.token }
|
||||
}
|
||||
}
|
||||
|
||||
if((Test-Path -Path $outputPath -PathType Leaf) -and (-not $force)) {
|
||||
Write-Error "The file '$outputPath' already exists. Use the -Force switch to overwrite the file."
|
||||
return $false | Out-Null
|
||||
# Validate that we have the required token
|
||||
if (-not $token) {
|
||||
throw "Missing token parameter. Either provide it directly or set it with Set-GiteaConfiguration."
|
||||
}
|
||||
|
||||
Write-Verbose "Parameters:"
|
||||
Write-Debug "Token: $token (length: $(if($token){$token.Length}else{0}))"
|
||||
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
|
||||
$webClient = New-Object System.Net.WebClient
|
||||
$webClient.Headers.Add("Authorization", "token $token")
|
||||
}
|
||||
|
||||
process {
|
||||
# If type is not provided, check for any hints for cases such as LFS where a type can be inferred and special handling is needed
|
||||
If($downloadURL -match "/api/v1/repos/[^/]+/[^/]+/media/") {
|
||||
$type = "lfs"
|
||||
}
|
||||
|
||||
Write-Verbose "Type: $type"
|
||||
# Handle the type parameter
|
||||
|
||||
# Skip directories and provide a verbose message
|
||||
if ($type -eq "dir") {
|
||||
Write-Verbose "Skipping directory"
|
||||
return [PSCustomObject]@{
|
||||
SourceURL = $downloadURL
|
||||
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"
|
||||
Write-Error $errorMsg
|
||||
return [PSCustomObject]@{
|
||||
SourceURL = $downloadURL
|
||||
Type = $type
|
||||
Success = $false
|
||||
Error = $errorMsg
|
||||
Timestamp = Get-Date
|
||||
FileSize = $null
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# Parse the download URL to extract the file path and file name
|
||||
|
||||
# Separate the URL into its components: Gitea URL, Repo Owner, Repo Name, branch, file path, and file name
|
||||
$uri = New-Object System.Uri($downloadURL)
|
||||
$pathSegments = $uri.AbsolutePath.Trim('/').Split('/')
|
||||
If($type -eq "lfs") {
|
||||
# Everything after the media segment is considered the file path with the last segment being the file name
|
||||
$branchIndex = [Array]::IndexOf($pathSegments, "media") + 1
|
||||
$DownloadFilePath = $pathSegments[$branchIndex..($pathSegments.Length - 2)] -join '/'
|
||||
$DownloadFileName = $pathSegments[-1]
|
||||
}
|
||||
Else {
|
||||
# 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 $outputPath) {
|
||||
# If PreserveRelativePath is used, set up the directory structure
|
||||
Write-Verbose "Preserving relative path structure for: $DownloadFileName"
|
||||
|
||||
$baseDir = $outputPath
|
||||
Write-Verbose "Using base directory: $baseDir"
|
||||
|
||||
# Combine the base directory with the file path
|
||||
$fileOutputPath = Join-Path -Path $baseDir -ChildPath $DownloadFilePath
|
||||
# Combine with the file name
|
||||
$fileOutputPath = Join-Path -Path $fileOutputPath -ChildPath $DownloadFileName
|
||||
}
|
||||
elseif($PreserveRelativePath) {
|
||||
# If no outputPath is specified, use the current directory as base and the file name from the download URL
|
||||
$fileOutputPath = Join-Path -Path (Get-Location) -ChildPath $DownloadFilePath
|
||||
# Combine with the file name
|
||||
$fileOutputPath = Join-Path -Path $fileOutputPath -ChildPath $DownloadFileName
|
||||
}
|
||||
elseif ($outputPath -and $outputName) {
|
||||
# If both outputPath and outputName are specified, use outputPath as the base directory with the file name specified by outputName
|
||||
$fileOutputPath = Join-Path -Path $outputPath -ChildPath $outputName
|
||||
}
|
||||
elseif ($outputPath) {
|
||||
# If outputPath is specified, use it as the base directory with the file name from the download URL
|
||||
$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]@{
|
||||
Path = $fileOutputPath
|
||||
SourceURL = $downloadURL
|
||||
Type = "file"
|
||||
Success = $false
|
||||
Error = $null
|
||||
Timestamp = Get-Date
|
||||
FileSize = $null
|
||||
}
|
||||
|
||||
if((Test-Path -Path $fileOutputPath -PathType Leaf) -and (-not $force)) {
|
||||
$errorMsg = "The file '$fileOutputPath' 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 $fileOutputPath -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
|
||||
}
|
||||
|
||||
# Download the file
|
||||
Write-Verbose "Downloading from $downloadURL to $fileOutputPath"
|
||||
|
||||
# Use synchronous download
|
||||
$webClient.DownloadFile($downloadURL, $fileOutputPath)
|
||||
|
||||
Write-Verbose "File downloaded successfully to $fileOutputPath"
|
||||
|
||||
# Get file info for the result object
|
||||
if (Test-Path -Path $fileOutputPath -PathType Leaf) {
|
||||
$fileInfo = Get-Item -Path $fileOutputPath
|
||||
$result.FileSize = $fileInfo.Length
|
||||
}
|
||||
|
||||
$result.Success = $true
|
||||
return $result
|
||||
}
|
||||
catch {
|
||||
$errorMsg = "Failed to download file from Gitea: $_"
|
||||
Write-Error $errorMsg
|
||||
$result.Error = $errorMsg
|
||||
return $result
|
||||
}
|
||||
}
|
||||
|
||||
end {
|
||||
# Clean up resources
|
||||
if ($webClient) {
|
||||
$webClient.Dispose()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Function Get-GiteaChildItem {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Lists files and directories in a Gitea repository path.
|
||||
|
||||
.DESCRIPTION
|
||||
This function retrieves a list of files and directories from a specified path in a Gitea repository.
|
||||
The results can be directly piped to Get-GiteaFileContent to retrieve file contents.
|
||||
|
||||
.PARAMETER giteaURL
|
||||
The URL of the Gitea server.
|
||||
|
||||
.PARAMETER repoOwner
|
||||
The owner of the repository.
|
||||
|
||||
.PARAMETER repoName
|
||||
The name of the repository.
|
||||
|
||||
.PARAMETER Path
|
||||
The path to list items from. This parameter accepts pipeline input.
|
||||
|
||||
.PARAMETER branch
|
||||
The branch to retrieve the items from. The default value is 'main'.
|
||||
|
||||
.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.
|
||||
|
||||
.PARAMETER File
|
||||
Returns only files, omitting directories from the results.
|
||||
|
||||
.EXAMPLE
|
||||
# List items in the root directory of a repository
|
||||
Get-GiteaChildItem -repoOwner "owner" -repoName "repo" -Path "" -token "your_token"
|
||||
|
||||
.EXAMPLE
|
||||
# List items and pipe files to Get-GiteaFileContent to get their content
|
||||
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"
|
||||
|
||||
.EXAMPLE
|
||||
# List only files (no directories) in a repository
|
||||
Get-GiteaChildItem -repoOwner "owner" -repoName "repo" -Path "src" -File -token "your_token"
|
||||
#>
|
||||
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[string]$giteaURL,
|
||||
[Parameter(ValueFromPipelineByPropertyName)]
|
||||
[string]$repoOwner,
|
||||
[Parameter(ValueFromPipelineByPropertyName)]
|
||||
[string]$repoName,
|
||||
[Parameter(ValueFromPipeline, ValueFromPipelineByPropertyName)]
|
||||
[Alias('FullName')]
|
||||
[string[]]$Path,
|
||||
[Parameter(ValueFromPipelineByPropertyName)]
|
||||
[string]$branch = "main",
|
||||
[string]$token,
|
||||
[switch]$Recurse,
|
||||
[int]$Depth,
|
||||
[switch]$File
|
||||
)
|
||||
|
||||
begin {
|
||||
# Initialize results array
|
||||
$results = @()
|
||||
|
||||
# Use configuration if parameters aren't provided
|
||||
if (-not $PSBoundParameters.ContainsKey('giteaURL') -or
|
||||
-not $PSBoundParameters.ContainsKey('repoOwner') -or
|
||||
-not $PSBoundParameters.ContainsKey('repoName') -or
|
||||
-not $PSBoundParameters.ContainsKey('branch') -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('repoName')) { $repoName = $config.defaultRepo }
|
||||
if (-not $PSBoundParameters.ContainsKey('branch')) { $branch = $config.defaultBranch }
|
||||
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 $repoName) { $missingParams += "repoName" }
|
||||
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."
|
||||
}
|
||||
|
||||
Write-Verbose "Parameters:"
|
||||
Write-Verbose "giteaURL: $giteaURL"
|
||||
Write-Verbose "repoOwner: $repoOwner"
|
||||
Write-Verbose "repoName: $repoName"
|
||||
Write-Verbose "Path: $Path"
|
||||
Write-Debug "Token: $token"
|
||||
Write-Verbose "File: $File"
|
||||
|
||||
$headers = @{
|
||||
"Authorization" = "token $token"
|
||||
"Accept" = "application/json"
|
||||
}
|
||||
|
||||
# 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"
|
||||
# Normalize path separators for cross-platform compatibility
|
||||
$normalizedPath = $path -replace '\\', '/' -replace '/$', ''
|
||||
Write-Verbose "Normalized path: $normalizedPath"
|
||||
$encodedPath = [System.Uri]::EscapeDataString($normalizedPath)
|
||||
$url = "$giteaURL"
|
||||
$url += "/api/v1/repos"
|
||||
$url += "/$repoOwner"
|
||||
$url += "/$repoName"
|
||||
$url += "/contents"
|
||||
# Only add the path component if it's not empty
|
||||
if (-not [string]::IsNullOrWhiteSpace($normalizedPath)) {
|
||||
$url += "/$encodedPath"
|
||||
}
|
||||
$url += "?ref=$branch"
|
||||
Write-Verbose "URL: $url"
|
||||
|
||||
try {
|
||||
$response = Invoke-RestMethod -Uri $url -Method Get -Headers $headers
|
||||
|
||||
# Handle both single items and arrays
|
||||
$items = if ($response -is [array]) { $response } else { @($response) }
|
||||
|
||||
foreach ($item in $items) {
|
||||
# Check if the name ends with any of the LFS extensions
|
||||
$isLFS = $false
|
||||
foreach ($ext in $lfsExtensions) {
|
||||
if ($item.name.EndsWith($ext, [System.StringComparison]::InvariantCultureIgnoreCase)) {
|
||||
$isLFS = $true
|
||||
break
|
||||
}
|
||||
}
|
||||
if ($isLFS) {
|
||||
$item.type = "lfs"
|
||||
# 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]@{
|
||||
name = $item.name
|
||||
Path = $item.path
|
||||
type = $item.type
|
||||
size = $item.size
|
||||
sha = $item.sha
|
||||
downloadURL = $item.download_url
|
||||
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"
|
||||
# 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)"
|
||||
}
|
||||
|
||||
$subItemObj = [PSCustomObject]@{
|
||||
name = $subItem.name
|
||||
Path = $subItem.path
|
||||
type = $subItem.type
|
||||
size = $subItem.size
|
||||
sha = $subItem.sha
|
||||
downloadURL = $subItem.download_url
|
||||
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 {
|
||||
$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]@{
|
||||
Path = $normalizedPath
|
||||
repoOwner = $repoOwner
|
||||
repoName = $repoName
|
||||
giteaURL = $giteaURL
|
||||
branch = $branch
|
||||
type = $null
|
||||
name = $null
|
||||
size = $null
|
||||
sha = $null
|
||||
downloadURL = $null
|
||||
Success = $false
|
||||
Error = $_.Exception.Message
|
||||
Level = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
Invoke-RestMethod -Uri $downloadURL -Method Get -Headers $headers -OutFile $outputPath
|
||||
return $true | Out-Null
|
||||
}
|
||||
catch {
|
||||
Write-Error "Failed to download file from Gitea: $_"
|
||||
return $false | Out-Null
|
||||
$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
|
||||
}
|
||||
}
|
||||
|
||||
Export-ModuleMember -Function Get-GiteaFileContent, Invoke-GiteaFileDownload
|
||||
# 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
|
||||
173
Tests/Get-GiteaChildItem.Tests.ps1
Normal file
173
Tests/Get-GiteaChildItem.Tests.ps1
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
75
Tests/Get-GiteaConfiguration.tests.ps1
Normal file
75
Tests/Get-GiteaConfiguration.tests.ps1
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
119
Tests/Get-GiteaFileContent.Tests.ps1
Normal file
119
Tests/Get-GiteaFileContent.Tests.ps1
Normal 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!'
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
117
Tests/Get-GiteaReleases.Tests.ps1
Normal file
117
Tests/Get-GiteaReleases.Tests.ps1
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
152
Tests/Invoke-GiteaFileDownload.Tests.ps1
Normal file
152
Tests/Invoke-GiteaFileDownload.Tests.ps1
Normal 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'
|
||||
}
|
||||
}
|
||||
}
|
||||
64
Tests/Set-GiteaConfiguration.Tests.ps1
Normal file
64
Tests/Set-GiteaConfiguration.Tests.ps1
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user