Add Get-GiteaChildItem, Config management, LFS detection. Increase usability with pipes.

This commit is contained in:
2025-04-24 15:41:46 -04:00
parent e9d6aa255f
commit 305ec3c2fb

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 { Function Get-GiteaFileContent {
<# <#
@@ -42,21 +88,51 @@ Function Get-GiteaFileContent {
[CmdletBinding()] [CmdletBinding()]
param( param(
[Parameter(ValueFromPipelineByPropertyName)]
[string]$giteaURL = "https://gitea.norwichct.tech", [string]$giteaURL = "https://gitea.norwichct.tech",
[Parameter(Mandatory)] [Parameter(ValueFromPipelineByPropertyName)]
[string]$repoOwner, [string]$repoOwner,
[Parameter(Mandatory)] [Parameter(ValueFromPipelineByPropertyName)]
[string]$repoName, [string]$repoName,
[Parameter(ValueFromPipelineByPropertyName)]
[string]$branch = "main", [string]$branch = "main",
[Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)] [Parameter(ValueFromPipeline, ValueFromPipelineByPropertyName)]
[Alias('FullName', 'Path')] [Alias('FullName', 'Path')]
[string[]]$filePath, [string[]]$filePath,
[Parameter(Mandatory)]
[string]$token, [string]$token,
[switch]$decode [switch]$decode
) )
begin { 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 = @() $results = @()
Write-Verbose "Parameters:" Write-Verbose "Parameters:"
Write-Verbose "giteaURL: $giteaURL" 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 { Function Invoke-GiteaFileDownload {
<# <#
.SYNOPSIS .SYNOPSIS
@@ -131,6 +283,9 @@ Function Invoke-GiteaFileDownload {
.DESCRIPTION .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. 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 .PARAMETER downloadURL
The direct download URL for the file from the Gitea server. The direct download URL for the file from the Gitea server.
@@ -150,46 +305,279 @@ Function Invoke-GiteaFileDownload {
.EXAMPLE .EXAMPLE
# Example 2: Download a file to a specific location with force overwrite # Example 2: Download a file to a specific location with force overwrite
Invoke-GiteaFileDownload -downloadURL "https://gitea.example.com/api/v1/repos/owner/repo/raw/path/to/file.txt" -token "your_token" -outputPath "C:\Downloads\file.txt" -force Invoke-GiteaFileDownload -downloadURL "https://gitea.example.com/api/v1/repos/owner/repo/raw/path/to/file.txt" -token "your_token" -outputPath "C:\Downloads\file.txt" -force
.EXAMPLE
# Example 3: Download files in pipeline from Get-GiteaChildItem
Get-GiteaChildItem -path "docs" | Where-Object { $_.type -eq 'file' } | Invoke-GiteaFileDownload
#> #>
[cmdletbinding()] [cmdletbinding()]
param( param(
[Parameter(Mandatory)] [Parameter(Mandatory, ValueFromPipelineByPropertyName)]
[string]$downloadURL, [string]$downloadURL,
[Parameter(Mandatory)]
[string]$token, [string]$token,
[Parameter(ValueFromPipelineByPropertyName)]
[string]$outputPath, [string]$outputPath,
[switch]$force [switch]$force
) )
# 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) begin {
if (-not $outputPath) { # Use configuration if parameters aren't provided
# Get the file name from the download URL if (-not $PSBoundParameters.ContainsKey('token')) {
$outputFileName = $downloadURL.Substring($downloadURL.LastIndexOf("/") + 1)
# Clean the file name by removing any query string parameters and HTML encoded characters $config = Get-GiteaConfiguration
$outputFileName = [System.Uri]::UnescapeDataString($outputFileName.Split("?")[0]) 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}))"
# Append the outputFileName to the current location # Create a WebClient to be reused
$outputPath = Join-Path -Path (Get-Location) -ChildPath $outputFileName $webClient = New-Object System.Net.WebClient
$webClient.Headers.Add("Authorization", "token $token")
} }
if((Test-Path -Path $outputPath -PathType Leaf) -and (-not $force)) { process {
Write-Error "The file '$outputPath' already exists. Use the -Force switch to overwrite the file." Write-Verbose "Processing download URL: $downloadURL"
return $false | Out-Null
# If the output path is not specified, use the current directory and the file name from the download URL
$currentOutputPath = $outputPath
if (-not $currentOutputPath) {
# Get the file name from the download URL
$outputFileName = $downloadURL.Substring($downloadURL.LastIndexOf("/") + 1)
# Clean the file name by removing any query string parameters and HTML encoded characters
$outputFileName = [System.Uri]::UnescapeDataString($outputFileName.Split("?")[0])
# Append the outputFileName to the current location
$currentOutputPath = Join-Path -Path (Get-Location) -ChildPath $outputFileName
}
Write-Verbose "Output path: $currentOutputPath"
if((Test-Path -Path $currentOutputPath -PathType Leaf) -and (-not $force)) {
Write-Error "The file '$currentOutputPath' already exists. Use the -Force switch to overwrite the file."
return $false
}
try {
# Create the directory structure if it doesn't exist
$directory = Split-Path -Path $currentOutputPath -Parent
if (-not (Test-Path -Path $directory -PathType Container) -and $directory) {
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"
return $true
}
catch {
Write-Error "Failed to download file from Gitea: $_"
return $false
}
} }
$headers = @{ end {
"Authorization" = "token $token" # Clean up resources
} if ($webClient) {
$webClient.Dispose()
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
} }
} }
Export-ModuleMember -Function Get-GiteaFileContent, Invoke-GiteaFileDownload 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.
.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
#>
[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
)
begin {
# Initialize results array
# This is used to store the results of the API calls later
# It is initialized here to avoid re-initializing it in the process block
$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"
$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
}
process {
$paths = $path
foreach ($path in $paths) {
Write-Verbose "Processing path: $path"
$encodedPath = [System.Uri]::EscapeDataString($path)
Write-Verbose "Encoded path: $encodedPath"
$url = "$giteaURL"
$url += "/api/v1/repos"
$url += "/$repoOwner"
$url += "/$repoName"
$url += "/contents"
$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)"
}
$results += [PSCustomObject]@{
# Properties for direct pipeline binding with other functions in this module
filePath = $item.path # Maps to -filePath parameter
Path = $item.path # Also include original name (alias)
repoOwner = $repoOwner # Maps to -repoOwner parameter
repoName = $repoName # Maps to -repoName parameter
giteaURL = $giteaURL # Maps to -giteaURL parameter
downloadURL = $item.download_url # Maps to -downloadURL parameter
branch = $branch # Maps to -branch parameter
# Additional useful properties
type = $item.type # 'file' or 'dir'
name = $item.name
size = $item.size
sha = $item.sha
Success = $true
Error = $null
}
}
}
catch {
Write-Error "Failed to retrieve '$path' from Gitea: $_"
$results += [PSCustomObject]@{
filePath = $path
Path = $path
repoOwner = $repoOwner
repoName = $repoName
giteaURL = $giteaURL
branch = $branch
type = $null
name = $null
size = $null
sha = $null
downloadURL = $null
Success = $false
Error = $_.Exception.Message
}
}
}
}
end {
return $results
}
}
Export-ModuleMember -Function Set-GiteaConfiguration, Get-GiteaConfiguration, Get-GiteaFileContent, Invoke-GiteaFileDownload, Get-GiteaChildItem