# Baut das ZIP eines Skills und legt damit ein Gitea-Release an. # Kein Actions-Runner. Der Tag /v muss lokal und auf dem Server liegen. # Token: Umgebungsvariable GITEA_TOKEN (persoenliches Zugangs-Token, nicht ins Repo). # Aufruf: scripts\release.ps1 -Name param([Parameter(Mandatory = $true)][string]$Name) . (Join-Path $PSScriptRoot 'lib.ps1') $Token = $env:GITEA_TOKEN if (-not $Token) { Write-Host 'GITEA_TOKEN fehlt. Persoenliches Zugangs-Token mit Schreibrecht (write:repository), nur in der Umgebung.' exit 1 } $dir = Join-Path $SkillsDir $Name if (-not (Test-Path $dir -PathType Container)) { Write-Host "Ordner skills/$Name existiert nicht." exit 1 } $ver = Get-SkillVersion $dir if (-not $ver) { Write-Host "CHANGELOG von $Name hat keine Version '## x.y.z (YYYY-MM-DD)'." exit 1 } $tag = "$Name/v$ver" git -C $Root rev-parse --verify --quiet "refs/tags/$tag" | Out-Null if ($LASTEXITCODE -ne 0) { Write-Host "Tag $tag fehlt lokal. Setzen, nicht verschieben: git tag $tag" exit 1 } & (Join-Path $PSScriptRoot 'package.ps1') -Name $Name if ($LASTEXITCODE -ne 0) { exit 1 } $zipName = "$Name-v$ver.zip" $zipPath = Join-Path $DistDir $zipName if (-not (Test-Path $zipPath)) { Write-Host "ZIP fehlt: dist/$zipName" exit 1 } $on = $false $acc = New-Object System.Collections.Generic.List[string] foreach ($l in ((Read-Text (Join-Path $dir 'CHANGELOG.md')) -split "`n")) { if (-not $on) { if ($l.StartsWith("## $ver ")) { $on = $true } continue } if ($l.StartsWith('## ')) { break } $acc.Add($l) } $notes = (($acc -join "`n").Trim()) $api = 'https://git.gundel.dev/api/v1/repos/dg/claude-skills' [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 function Invoke-Gitea([string]$Method, [string]$Url, [byte[]]$Body, [string]$ContentType) { $req = [Net.HttpWebRequest]::Create($Url) $req.Method = $Method $req.Headers.Add('Authorization', "token $Token") $req.Accept = 'application/json' if ($null -ne $Body) { $req.ContentType = $ContentType $req.ContentLength = $Body.LongLength $stream = $req.GetRequestStream() $stream.Write($Body, 0, $Body.Length) $stream.Close() } try { $resp = $req.GetResponse() } catch [System.Net.WebException] { $errResp = $_.Exception.Response $detail = '' if ($errResp) { $reader = New-Object IO.StreamReader($errResp.GetResponseStream()) $detail = $reader.ReadToEnd() $reader.Close() } throw "API-Fehler $Url : $detail" } $reader = New-Object IO.StreamReader($resp.GetResponseStream(), $Utf8NoBom) $text = $reader.ReadToEnd() $reader.Close() $resp.Close() return $text } $payload = @{ tag_name = $tag name = "$Name $ver" body = $notes } | ConvertTo-Json -Compress try { $created = Invoke-Gitea 'POST' "$api/releases" $Utf8NoBom.GetBytes($payload) 'application/json; charset=utf-8' | ConvertFrom-Json } catch { Write-Host $_ Write-Host "Tag auf dem Server? git push origin $tag" exit 1 } $assetUrl = "$api/releases/$($created.id)/assets?name=$([Uri]::EscapeDataString($zipName))" $asset = Invoke-Gitea 'POST' $assetUrl ([IO.File]::ReadAllBytes($zipPath)) 'application/octet-stream' | ConvertFrom-Json if ($created.html_url) { Write-Host $created.html_url } if ($asset.browser_download_url) { Write-Host $asset.browser_download_url } Write-Host "Release $tag angelegt."