forked from unom/punktfunk
Release notes now live in the repo at docs/releases/vX.Y.Z.md, authored during the version bump (before the tag). ensure_release (gitea-release.sh + .ps1 twin) seeds the Gitea release body from that file in the create POST, so the release is born WITH its notes instead of being created empty and PATCHed afterward. canary/rc have no file -> empty body, unchanged. New manual `announce` workflow (workflow_dispatch, tag input) re-syncs the notes file over the live release and posts a violet embed (title, notes lead-in, release-page link) to the Discord #releases channel via the DISCORD_RELEASE_WEBHOOK secret. Stable-only: a -rc tag is refused unless allow_prerelease=true. Pressing "go" is the quality gate, so a half-built or failed release is never announced. docs/releases/README.md documents the ritual; TEMPLATE.md is the skeleton. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
83 lines
4.1 KiB
PowerShell
83 lines
4.1 KiB
PowerShell
# Shared Gitea Release helpers for the punktfunk Windows CI workflows (pwsh / PowerShell 7).
|
|
#
|
|
# Dot-source it, then call Ensure-GiteaRelease / Upsert-GiteaAsset:
|
|
# . scripts/ci/gitea-release.ps1
|
|
# Mirrors scripts/ci/gitea-release.sh; parses JSON with ConvertFrom-Json (the Windows runner
|
|
# has no python). Same idempotent semantics: Upsert-GiteaAsset deletes an existing asset of
|
|
# the same name before uploading, so re-runs / rolling canary uploads don't 409.
|
|
#
|
|
# Env (Gitea Actions sets the first two automatically):
|
|
# GITHUB_SERVER_URL e.g. https://git.unom.io
|
|
# GITHUB_REPOSITORY e.g. unom/punktfunk
|
|
# GITEA_TOKEN a PAT with repository (release) write scope — set from secrets.REGISTRY_TOKEN
|
|
# (must carry write:repository, not only write:package)
|
|
|
|
$ErrorActionPreference = 'Stop'
|
|
|
|
function _GiteaApi {
|
|
if (-not $env:GITHUB_SERVER_URL) { throw 'GITHUB_SERVER_URL unset' }
|
|
if (-not $env:GITHUB_REPOSITORY) { throw 'GITHUB_REPOSITORY unset' }
|
|
"$($env:GITHUB_SERVER_URL)/api/v1/repos/$($env:GITHUB_REPOSITORY)"
|
|
}
|
|
|
|
function _GiteaHeaders {
|
|
if (-not $env:GITEA_TOKEN) { throw 'GITEA_TOKEN unset' }
|
|
@{ Authorization = "token $($env:GITEA_TOKEN)" }
|
|
}
|
|
|
|
# Ensure-GiteaRelease TAG NAME PRERELEASE [TARGETCOMMITISH] -> release id
|
|
# Idempotently create or fetch the release for TAG. Prerelease is 'true', 'false', or 'auto'
|
|
# (auto marks it a prerelease iff TAG carries a `-` pre-release suffix, e.g. v0.2.0-rc1, so an
|
|
# rc never becomes "Latest"). TargetCommitish (optional) creates the tag if missing.
|
|
function Ensure-GiteaRelease {
|
|
param(
|
|
[Parameter(Mandatory)] [string]$Tag,
|
|
[Parameter(Mandatory)] [string]$Name,
|
|
[Parameter(Mandatory)] [string]$Prerelease,
|
|
[string]$TargetCommitish
|
|
)
|
|
$pre = if ($Prerelease -eq 'auto') { [bool]($Tag -match '-') } else { [System.Convert]::ToBoolean($Prerelease) }
|
|
$api = _GiteaApi; $h = _GiteaHeaders
|
|
$payload = @{ tag_name = $Tag; name = $Name; prerelease = $pre }
|
|
if ($TargetCommitish) { $payload.target_commitish = $TargetCommitish }
|
|
# Seed the body from docs/releases/<Tag>.md (the source of truth) so a Windows job winning the
|
|
# create race is born WITH its notes, exactly like the bash twin. Missing file (canary/rc) -> no
|
|
# body key. $PSScriptRoot is scripts/ci; walk up two levels to the repo root.
|
|
$notes = Join-Path (Split-Path (Split-Path $PSScriptRoot -Parent) -Parent) "docs/releases/$Tag.md"
|
|
if (Test-Path $notes) { $payload.body = Get-Content -Raw -Encoding utf8 $notes }
|
|
try {
|
|
$r = Invoke-RestMethod -Method Post -Uri "$api/releases" -Headers $h `
|
|
-ContentType 'application/json' -Body ($payload | ConvertTo-Json -Compress)
|
|
return $r.id
|
|
} catch {
|
|
# Almost always: the release already exists. Fetch it by tag; error if that fails too.
|
|
$r = Invoke-RestMethod -Method Get -Uri "$api/releases/tags/$Tag" -Headers $h
|
|
if (-not $r.id) { throw "gitea-release: could not create or find a release for tag '$Tag'" }
|
|
return $r.id
|
|
}
|
|
}
|
|
|
|
# Upsert-GiteaAsset RELEASEID FILE [NAME]
|
|
# Attach FILE, replacing any existing asset of the same name first (idempotent).
|
|
function Upsert-GiteaAsset {
|
|
param(
|
|
[Parameter(Mandatory)] [string]$ReleaseId,
|
|
[Parameter(Mandatory)] [string]$File,
|
|
[string]$Name
|
|
)
|
|
if (-not (Test-Path $File)) { throw "gitea-release: asset file not found: $File" }
|
|
if (-not $Name) { $Name = Split-Path $File -Leaf }
|
|
$api = _GiteaApi; $h = _GiteaHeaders
|
|
$assets = Invoke-RestMethod -Method Get -Uri "$api/releases/$ReleaseId/assets" -Headers $h
|
|
$existing = $assets | Where-Object { $_.name -eq $Name } | Select-Object -First 1
|
|
if ($existing) {
|
|
Invoke-RestMethod -Method Delete -Uri "$api/releases/$ReleaseId/assets/$($existing.id)" -Headers $h | Out-Null
|
|
}
|
|
$enc = [uri]::EscapeDataString($Name)
|
|
# curl.exe for the multipart upload — matches the rest of the windows workflows.
|
|
curl.exe -fsS -H "Authorization: token $($env:GITEA_TOKEN)" -o NUL `
|
|
-X POST "$api/releases/$ReleaseId/assets?name=$enc" -F "attachment=@$File"
|
|
if ($LASTEXITCODE -ne 0) { throw "gitea-release: asset upload failed ($LASTEXITCODE): $Name" }
|
|
Write-Output "gitea-release: uploaded '$Name' -> release $ReleaseId"
|
|
}
|