Decky/scripts/Publish-ForgejoRelease.ps1

78 lines
3.5 KiB
PowerShell

#requires -Version 5.1
[CmdletBinding()]
param(
[Parameter(Mandatory)]
[ValidatePattern('^\d+\.\d+\.\d+(-[0-9A-Za-z.-]+)?$')]
[string]$Version,
[string]$Notes = "Decky $Version",
[string]$ForgejoBaseUrl = 'https://git.elijahkuntz.com',
[string]$Repository = 'Elijah/Decky',
[string]$Token = $env:FORGEJO_TOKEN
)
$ErrorActionPreference = 'Stop'
if (-not $Token) { throw 'FORGEJO_TOKEN is required. Store it as a Forgejo Actions secret or set it only for this process.' }
$repositoryRoot = Split-Path -Parent $PSScriptRoot
$releaseDirectory = Join-Path $repositoryRoot "artifacts\release\v$Version"
$latestJsonPath = Join-Path $releaseDirectory 'latest.json'
if (-not (Test-Path -LiteralPath $latestJsonPath)) { throw "Run Prepare-Release.ps1 first: $latestJsonPath is missing." }
$headers = @{ Authorization = "token $Token"; Accept = 'application/json' }
$apiRoot = "$($ForgejoBaseUrl.TrimEnd('/'))/api/v1/repos/$Repository"
$tag = "v$Version"
$releaseBody = @{
tag_name = $tag
target_commitish = 'main'
name = "Decky $Version"
body = $Notes
draft = $false
prerelease = $Version.Contains('-')
} | ConvertTo-Json
try {
$release = Invoke-RestMethod -Method Get -Uri "$apiRoot/releases/tags/$tag" -Headers $headers
} catch {
if ($_.Exception.Response.StatusCode.value__ -ne 404) { throw }
$release = Invoke-RestMethod -Method Post -Uri "$apiRoot/releases" -Headers $headers -ContentType 'application/json' -Body $releaseBody
}
$assets = Get-ChildItem -LiteralPath $releaseDirectory -File
foreach ($asset in $assets) {
$existing = @($release.assets | Where-Object name -eq $asset.Name)
if ($existing.Count) {
Write-Host "Release asset already exists, skipping: $($asset.Name)"
continue
}
Add-Type -AssemblyName System.Net.Http
$client = New-Object Net.Http.HttpClient
try {
$client.DefaultRequestHeaders.Authorization = New-Object Net.Http.Headers.AuthenticationHeaderValue('token', $Token)
$form = New-Object Net.Http.MultipartFormDataContent
$fileContent = New-Object Net.Http.ByteArrayContent(,[IO.File]::ReadAllBytes($asset.FullName))
$fileContent.Headers.ContentType = New-Object Net.Http.Headers.MediaTypeHeaderValue('application/octet-stream')
$form.Add($fileContent, 'attachment', $asset.Name)
$response = $client.PostAsync("$apiRoot/releases/$($release.id)/assets?name=$([Uri]::EscapeDataString($asset.Name))", $form).GetAwaiter().GetResult()
if (-not $response.IsSuccessStatusCode) { throw "Asset upload failed ($([int]$response.StatusCode)): $($response.Content.ReadAsStringAsync().GetAwaiter().GetResult())" }
} finally {
if ($form) { $form.Dispose() }
$client.Dispose()
}
Write-Host "Uploaded $($asset.Name)"
}
$encodedMetadata = [Convert]::ToBase64String([IO.File]::ReadAllBytes($latestJsonPath))
$contentsUri = "$apiRoot/contents/releases/latest.json?ref=main"
$sha = $null
try { $sha = (Invoke-RestMethod -Method Get -Uri $contentsUri -Headers $headers).sha } catch {
if ($_.Exception.Response.StatusCode.value__ -ne 404) { throw }
}
$contentBody = @{
branch = 'main'
message = "chore(release): publish Decky $Version updater metadata"
content = $encodedMetadata
}
if ($sha) { $contentBody.sha = $sha }
Invoke-RestMethod -Method Put -Uri "$apiRoot/contents/releases/latest.json" -Headers $headers -ContentType 'application/json' -Body ($contentBody | ConvertTo-Json) | Out-Null
Write-Host "Published Decky $Version and updated releases/latest.json."