5 Commits
1.0.0 ... 1.1.0

3 changed files with 666 additions and 35 deletions

View File

@@ -22,9 +22,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
}
@@ -56,7 +56,7 @@ jobs:
Write-Host "Module version: $moduleVersion"
$module = get-childItem -path . -Directory -Exclude ".*" | 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

View File

@@ -12,7 +12,7 @@
RootModule = 'PS-GiteaUtilities.psm1'
# Version number of this module.
ModuleVersion = '1.0.0'
ModuleVersion = '1.1.0'
# 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'
# 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 = '*'

View File

@@ -1,3 +1,49 @@
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 $env:USERPROFILE -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()
$configPath = Join-Path -Path $env:USERPROFILE -ChildPath ".giteautils\config.xml"
if (Test-Path -Path $configPath) {
return Import-Clixml -Path $configPath
}
else {
Write-Warning "Gitea configuration not found. Use Set-GiteaConfiguration to set up."
return $null
}
}
Function Get-GiteaFileContent {
<#
@@ -42,21 +88,51 @@ 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"
@@ -123,6 +199,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-Verbose "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 +283,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.
@@ -143,6 +298,15 @@ Function Invoke-GiteaFileDownload {
.PARAMETER force
A switch parameter to force overwriting of an existing file at the output path.
.PARAMETER PreserveRelativePath
A switch parameter to preserve the relative path structure from the repository.
If used without specifying an outputPath, creates the directory structure in the current location.
If used with outputPath, treats the outputPath as the base directory to append the relative path to.
.PARAMETER type
The type of the item to download (file, dir, lfs). This parameter is typically used with pipeline input from Get-GiteaChildItem.
Directories will be skipped with a verbose message.
.EXAMPLE
# Example 1: Download a file to the current directory
Invoke-GiteaFileDownload -downloadURL "https://gitea.example.com/api/v1/repos/owner/repo/raw/path/to/file.txt" -token "your_token"
@@ -150,46 +314,513 @@ Function Invoke-GiteaFileDownload {
.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
.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
[switch]$force,
[Parameter(ValueFromPipelineByPropertyName)]
[string]$filePath,
[switch]$PreserveRelativePath,
[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
begin {
# Use configuration if parameters aren't provided
if (-not $PSBoundParameters.ContainsKey('token')) {
$config = Get-GiteaConfiguration
if ($config) {
if (-not $PSBoundParameters.ContainsKey('token')) { $token = $config.token }
}
}
# 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-Verbose "token: $token (length: $(if($token){$token.Length}else{0}))"
Write-Verbose "PreserveRelativePath: $PreserveRelativePath"
# Create a WebClient to be reused
$webClient = New-Object System.Net.WebClient
$webClient.Headers.Add("Authorization", "token $token")
}
process {
Write-Verbose "File path: $filePath"
Write-Verbose "Type: $type"
# Skip directories and provide a verbose message
if ($type -eq "dir") {
Write-Verbose "Skipping directory: $filePath"
return [PSCustomObject]@{
Path = $filePath
SourceURL = $downloadURL
FilePath = $filePath
Type = $type
Success = $true
Error = "Skipped - item is a directory"
Timestamp = Get-Date
FileSize = $null
}
}
# Handle invalid entry types
if ([string]::IsNullOrEmpty($type) -and [string]::IsNullOrEmpty($downloadURL)) {
$errorMsg = "Invalid item type or missing download URL for path: $filePath"
Write-Error $errorMsg
return [PSCustomObject]@{
Path = $filePath
SourceURL = $downloadURL
FilePath = $filePath
Type = $type
Success = $false
Error = $errorMsg
Timestamp = Get-Date
FileSize = $null
}
}
Write-Verbose "Processing download URL: $downloadURL"
# Set the output path based on parameters
$currentOutputPath = ""
# Get the file name from the download URL or file path
$outputFileName = $downloadURL.Substring($downloadURL.LastIndexOf("/") + 1)
# Clean the file name by removing any query string parameters and HTML encoded characters
$outputFileName = [System.Uri]::UnescapeDataString($outputFileName.Split("?")[0])
if ($PreserveRelativePath -and $filePath) {
# If PreserveRelativePath is used, set up the directory structure
Write-Verbose "Preserving relative path structure for: $filePath"
if ($outputPath) {
# If outputPath is specified, use it as the base directory
$baseDir = $outputPath
Write-Verbose "Using base directory: $baseDir"
# Combine the base directory with the file path
$currentOutputPath = Join-Path -Path $baseDir -ChildPath $filePath
} else {
# If no outputPath is specified, use the current directory as base
$currentOutputPath = Join-Path -Path (Get-Location) -ChildPath $filePath
}
# Normalize path separators
$currentOutputPath = $currentOutputPath.Replace("/", [System.IO.Path]::DirectorySeparatorChar)
Write-Verbose "Output path with preserved structure: $currentOutputPath"
} else {
# Standard path handling (unchanged from original)
if ($outputPath) {
$currentOutputPath = $outputPath
} else {
# Append the outputFileName to the current location
$outputPath = Join-Path -Path (Get-Location) -ChildPath $outputFileName
$currentOutputPath = Join-Path -Path (Get-Location) -ChildPath $outputFileName
}
Write-Verbose "Output path: $currentOutputPath"
}
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
$result = [PSCustomObject]@{
Path = $currentOutputPath
SourceURL = $downloadURL
FilePath = $filePath
Type = $type
Success = $false
Error = $null
Timestamp = Get-Date
FileSize = $null
}
$headers = @{
"Authorization" = "token $token"
if((Test-Path -Path $currentOutputPath -PathType Leaf) -and (-not $force)) {
$errorMsg = "The file '$currentOutputPath' already exists. Use the -Force switch to overwrite the file."
Write-Error $errorMsg
$result.Error = $errorMsg
return $result
}
try {
Invoke-RestMethod -Uri $downloadURL -Method Get -Headers $headers -OutFile $outputPath
return $true | Out-Null
# Create the directory structure if it doesn't exist
$directory = Split-Path -Path $currentOutputPath -Parent
if (-not (Test-Path -Path $directory -PathType Container) -and $directory) {
Write-Verbose "Creating directory structure: $directory"
New-Item -Path $directory -ItemType Directory -Force | Out-Null
}
# Download the file
Write-Verbose "Downloading from $downloadURL to $currentOutputPath"
# Use synchronous download
$webClient.DownloadFile($downloadURL, $currentOutputPath)
Write-Verbose "File downloaded successfully to $currentOutputPath"
# Get file info for the result object
if (Test-Path -Path $currentOutputPath -PathType Leaf) {
$fileInfo = Get-Item -Path $currentOutputPath
$result.FileSize = $fileInfo.Length
}
$result.Success = $true
return $result
}
catch {
Write-Error "Failed to download file from Gitea: $_"
return $false | Out-Null
$errorMsg = "Failed to download file from Gitea: $_"
Write-Error $errorMsg
$result.Error = $errorMsg
return $result
}
}
Export-ModuleMember -Function Get-GiteaFileContent, Invoke-GiteaFileDownload
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-Verbose "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 the path format - replace backslashes with forward slashes and trim trailing slashes
$normalizedPath = $path -replace '\\', '/' -replace '/$', ''
$encodedPath = [System.Uri]::EscapeDataString($normalizedPath)
Write-Verbose "Normalized path: $normalizedPath"
Write-Verbose "Encoded path: $encodedPath"
$url = "$giteaURL"
$url += "/api/v1/repos"
$url += "/$repoOwner"
$url += "/$repoName"
$url += "/contents"
# 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"
$item.download_url = "$giteaURL/api/v1/repos/$repoOwner/$repoName/media/$($item.path)"
}
$itemObj = [PSCustomObject]@{
filePath = $item.path
Path = $item.path
repoOwner = $repoOwner
repoName = $repoName
giteaURL = $giteaURL
downloadURL = $item.download_url
branch = $branch
type = $item.type
name = $item.name
size = $item.size
sha = $item.sha
Success = $true
Error = $null
Level = 0
}
$results += $itemObj
# If the item is a directory and we're recursing, add it to the queue
if ($Recurse -and $item.type -eq 'dir') {
$directoryQueue.Enqueue(@{
Path = $item.path
Level = 1
})
}
}
# Process the directory queue if recursion is enabled
if ($Recurse) {
while ($directoryQueue.Count -gt 0) {
$currentDir = $directoryQueue.Dequeue()
$currentPath = $currentDir.Path
$currentLevel = $currentDir.Level
# Check depth limit if specified
if ($PSBoundParameters.ContainsKey('Depth') -and $currentLevel -gt $Depth) {
continue
}
Write-Verbose "Recursing into: $currentPath (Level: $currentLevel)"
# Normalize the path format for consistent handling
$normalizedSubPath = $currentPath -replace '\\', '/' -replace '/$', ''
$encodedSubPath = [System.Uri]::EscapeDataString($normalizedSubPath)
Write-Verbose "Normalized sub path: $normalizedSubPath"
Write-Verbose "Encoded sub path: $encodedSubPath"
# Fix the URL construction for subdirectories
$subUrl = "$giteaURL/api/v1/repos/$repoOwner/$repoName/contents/$encodedSubPath"
# Add the branch reference separately to avoid issues
$subUrl += "?ref=$branch"
Write-Verbose "Sub URL: $subUrl"
try {
$subResponse = Invoke-RestMethod -Uri $subUrl -Method Get -Headers $headers -ErrorAction Stop
$subItems = if ($subResponse -is [array]) { $subResponse } else { @($subResponse) }
foreach ($subItem in $subItems) {
# Check if the name ends with any of the LFS extensions
$isLFS = $false
foreach ($ext in $lfsExtensions) {
if ($subItem.name.EndsWith($ext, [System.StringComparison]::InvariantCultureIgnoreCase)) {
$isLFS = $true
break
}
}
if ($isLFS) {
$subItem.type = "lfs"
$subItem.download_url = "$giteaURL/api/v1/repos/$repoOwner/$repoName/media/$($subItem.path)"
}
$subItemObj = [PSCustomObject]@{
filePath = $subItem.path
Path = $subItem.path
repoOwner = $repoOwner
repoName = $repoName
giteaURL = $giteaURL
downloadURL = $subItem.download_url
branch = $branch
type = $subItem.type
name = $subItem.name
size = $subItem.size
sha = $subItem.sha
Success = $true
Error = $null
Level = $currentLevel
}
$results += $subItemObj
# If the subitem is a directory, add it to the queue for further processing
if ($subItem.type -eq 'dir') {
$nextLevel = $currentLevel + 1
# Only add to queue if we haven't hit our depth limit
if (-not $PSBoundParameters.ContainsKey('Depth') -or $nextLevel -le $Depth) {
$directoryQueue.Enqueue(@{
Path = $subItem.path
Level = $nextLevel
})
}
}
}
}
catch {
$errorDetails = if ($_.ErrorDetails) {
try { $_.ErrorDetails.Message | ConvertFrom-Json -ErrorAction SilentlyContinue } catch { $_.ErrorDetails.Message }
} else { $null }
Write-Error "Failed to recursively retrieve path '$normalizedSubPath' from Gitea: $($_.Exception.Message)`n$($errorDetails | ConvertTo-Json -Depth 1 -Compress)"
}
}
}
}
catch {
$errorDetails = if ($_.ErrorDetails) {
try { $_.ErrorDetails.Message | ConvertFrom-Json -ErrorAction SilentlyContinue } catch { $_.ErrorDetails.Message }
} else { $null }
Write-Error "Failed to retrieve '$normalizedPath' from Gitea: $($_.Exception.Message)`n$($errorDetails | ConvertTo-Json -Depth 1 -Compress)"
$results += [PSCustomObject]@{
filePath = $normalizedPath
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
}
}
Export-ModuleMember -Function Set-GiteaConfiguration, Get-GiteaConfiguration, Get-GiteaFileContent, Invoke-GiteaFileDownload, Get-GiteaChildItem