diff --git a/PS-GiteaUtilities/PS-GiteaUtilities.psm1 b/PS-GiteaUtilities/PS-GiteaUtilities.psm1 index 1447807..955b1cb 100644 --- a/PS-GiteaUtilities/PS-GiteaUtilities.psm1 +++ b/PS-GiteaUtilities/PS-GiteaUtilities.psm1 @@ -872,4 +872,142 @@ Function Get-GiteaChildItem { } } -Export-ModuleMember -Function Set-GiteaConfiguration, Get-GiteaConfiguration, Get-GiteaFileContent, Invoke-GiteaFileDownload, Get-GiteaChildItem, Get-GiteaLFSFile \ No newline at end of file +function Get-GiteaReleases { + <# + .SYNOPSIS + Retrieves the releases for a Gitea repository. + + .DESCRIPTION + This function retrieves the releases for a Gitea repository using the Gitea API. The function requires the URL of the Gitea server, the owner of the repository, the name of the repository, and a personal access token. + #> + [cmdletbinding()] + param( + [Parameter(ValueFromPipelineByPropertyName)] + [string]$giteaURL = "https://gitea.norwichct.tech", + [Parameter(ValueFromPipelineByPropertyName)] + [string]$repoOwner, + [Parameter(ValueFromPipelineByPropertyName)] + [string]$repoName, + [int]$page, + [int]$limit, + [switch]$includeDrafts, + [switch]$includePreReleases, + [string]$token + ) + + begin { + # Use configuration if parameters aren't provided + if (-not $PSBoundParameters.ContainsKey('giteaURL') -or + -not $PSBoundParameters.ContainsKey('repoOwner') -or + -not $PSBoundParameters.ContainsKey('token')) { + + $config = Get-GiteaConfiguration + if ($config) { + if (-not $PSBoundParameters.ContainsKey('giteaURL')) { $giteaURL = $config.giteaURL } + if (-not $PSBoundParameters.ContainsKey('repoOwner')) { $repoOwner = $config.defaultOwner } + if (-not $PSBoundParameters.ContainsKey('token')) { $token = $config.token } + } + } + + # Validate that we have all required parameters + $missingParams = @() + if (-not $giteaURL) { $missingParams += "giteaURL" } + if (-not $repoOwner) { $missingParams += "repoOwner" } + if (-not $token) { $missingParams += "token" } + + if ($missingParams.Count -gt 0) { + throw "Missing required parameters: $($missingParams -join ', '). Either provide them directly or set them with Set-GiteaConfiguration." + } + + $headers = @{ + "Authorization" = "token $token" + "Accept" = "application/json" + } + } + + process { + Write-Verbose "Parameters:" + Write-Verbose "giteaURL: $giteaURL" + Write-Verbose "repoOwner: $repoOwner" + Write-Verbose "repoName: $repoName" + Write-Verbose "page: $page" + Write-Verbose "limit: $limit" + Write-Verbose "includeDrafts: $includeDrafts" + Write-Verbose "includePreReleases: $includePreReleases" + Write-Debug "Token: $token" + + Write-Verbose "Processing releases for repository: $repoName" + $url = "$giteaURL" + $url += "/api/v1/repos" + $url += "/$repoOwner" + $url += "/$repoName" + $url += "/releases" + if ($includeDrafts -eq $true) { + $url += "?draft=true" + } + else { + $url += "?draft=false" + } + if ($includePreReleases -eq $true) { + $url += "&pre-release=true" + } + if ($page -gt 1) { + $url += "&page=$page" + } + if ($limit -gt 0) { + $url += "&limit=$limit" + } + Write-Verbose "URL: $url" + + try { + $result = Invoke-RestMethod -Uri $url -Method Get -Headers $headers + $releases = foreach ($release in $result) { + # Process each asset into its own object + $assets = foreach ($asset in $release.assets) { + [PSCustomObject]@{ + Id = $asset.id + Name = $asset.name + Size = $asset.size + DownloadCount = $asset.download_count + CreatedAt = [DateTime]$asset.created_at + UUID = $asset.uuid + DownloadURL = $asset.browser_download_url + } + } + + # Create the release object with nested author and assets + [PSCustomObject]@{ + Id = $release.id + TagName = $release.tag_name + TargetCommit = $release.target_commitish + Name = $release.name + Description = $release.body + URL = $release.url + HTMLURL = $release.html_url + TarballURL = $release.tarball_url + ZipballURL = $release.zipball_url + UploadURL = $release.upload_url + IsDraft = $release.draft + IsPrerelease = $release.prerelease + CreatedAt = [DateTime]$release.created_at + PublishedAt = [DateTime]$release.published_at + Author = [PSCustomObject]@{ + Id = $release.author.id + Username = $release.author.username + Email = $release.author.email + AvatarURL = $release.author.avatar_url + HTMLURL = $release.author.html_url + } + Assets = $assets + } + } + + return $releases + } + catch { + Write-Error "Failed to retrieve file '$file' from Gitea: $_" + } + } +} + +Export-ModuleMember -Function Set-GiteaConfiguration, Get-GiteaConfiguration, Get-GiteaFileContent, Invoke-GiteaFileDownload, Get-GiteaChildItem, Get-GiteaLFSFile, Get-GiteaReleases \ No newline at end of file diff --git a/PS-GiteaUtilities/Tests/Get-GiteaReleases.Tests.ps1 b/PS-GiteaUtilities/Tests/Get-GiteaReleases.Tests.ps1 new file mode 100644 index 0000000..b072bdd --- /dev/null +++ b/PS-GiteaUtilities/Tests/Get-GiteaReleases.Tests.ps1 @@ -0,0 +1,117 @@ +# Get-GiteaReleases.Tests.ps1 + +# Import the module +$modulePath = Join-Path -Path $PSScriptRoot -ChildPath '..\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 + } + } +}