Files
tessera-ctl/.gitea/scripts/publish-release.sh
T
schalli 6940bd05a1 ci(quick-260916-dcz): Gitea-Release je Freigabe-Tag aus CHANGELOG.md (publish-release.sh, idempotent, --dry-run), Schritt in ci.yml
- publish-release.sh: POSIX sh, Entscheidung anhand GITHUB_REF wie publish-images.sh, --tag/--dry-run, Abschnitt per awk, JSON nur per jq --arg, Token nur aus GITEA_TOKEN ueber Header-Datei, GET/tags -> PATCH oder POST, Exit 1 ohne Abschnitt
- ci.yml: vierter Schritt im Job publish mit GITEA_TOKEN aus secrets.REGISTRY_TOKEN ueber env
- Bewiesen: dry-run v1.0.0 (Body 2227 Zeichen), v9.9.9 Exit 1, main nichts zu tun, API-Basis aus GITHUB_SERVER_URL; Docker-Abbild traegt den Changelog nur im Server-Bundle (1/0); Release Tessera 1.0.0 (id 1) angelegt, zweiter Lauf PATCH, genau ein Release

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018N9CD3ebPKm1b32bPpBknY
2026-09-16 11:24:20 +02:00

156 lines
5.1 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/bin/sh
# publish-release.sh -- Gitea-Release je Freigabe-Tag aus CHANGELOG.md anlegen
# (quick-260916-dcz).
#
# Entscheidung wie publish-images.sh allein anhand GITHUB_REF:
# refs/tags/vX.Y.Z -> Abschnitt "## X.Y.Z" aus CHANGELOG.md schneiden und als
# Release "Tessera X.Y.Z" anlegen (bzw. aktualisieren, wenn
# der Release zum Tag schon existiert -- idempotent)
# alles andere -> nichts zu tun (Exit 0)
#
# Aufrufformen:
# sh .gitea/scripts/publish-release.sh # im CI, Tag aus GITHUB_REF
# sh .gitea/scripts/publish-release.sh --tag v1.0.0 # lokal, expliziter Tag
# sh .gitea/scripts/publish-release.sh --dry-run --tag v1.0.0 # nur JSON und Ziel zeigen
#
# Umgebung:
# GITEA_TOKEN Zugriffstoken (Pflicht im echten Lauf; im CI aus secrets.REGISTRY_TOKEN
# ueber `env`). Wird nie ausgegeben und nie als Argument uebergeben --
# der Authorization-Header kommt aus einer temporaeren Datei.
# GITEA_API API-Basis; sonst GITHUB_API_URL, sonst GITHUB_SERVER_URL/api/v1,
# sonst http://localhost:3002/api/v1 (nur lokal erreichbar).
# GITEA_REPO owner/repo; sonst GITHUB_REPOSITORY, sonst schalli/tessera-ctl.
# CHANGELOG_FILE Pfad zur Aenderungsliste; Vorgabe CHANGELOG.md.
#
# Fehlt der Abschnitt fuer die Version, endet das Skript mit Exit 1 -- es entsteht
# nie ein leerer Release. JSON wird ausschliesslich mit jq gebaut.
set -eu
usage() {
echo "Aufruf: publish-release.sh [--dry-run] [--tag vX.Y.Z]" >&2
}
DRY_RUN=0
TAG=""
while [ $# -gt 0 ]; do
case "$1" in
--dry-run) DRY_RUN=1 ;;
--tag)
[ $# -ge 2 ] || { usage; exit 2; }
TAG="$2"
shift
;;
*) usage; exit 2 ;;
esac
shift
done
if [ -z "$TAG" ]; then
REF="${GITHUB_REF:-}"
case "$REF" in
refs/tags/v*) TAG="${REF#refs/tags/}" ;;
*)
echo "Kein Freigabe-Tag (nur refs/tags/v*): nichts zu tun."
exit 0
;;
esac
fi
if ! echo "$TAG" | grep -Eq '^v[0-9]+\.[0-9]+\.[0-9]+$'; then
echo "Ungueltiger Tag '$TAG' (erwartet vX.Y.Z)." >&2
exit 1
fi
VERSION="${TAG#v}"
API="${GITEA_API:-${GITHUB_API_URL:-${GITHUB_SERVER_URL:+${GITHUB_SERVER_URL}/api/v1}}}"
API="${API:-http://localhost:3002/api/v1}"
API="${API%/}"
REPO="${GITEA_REPO:-${GITHUB_REPOSITORY:-schalli/tessera-ctl}}"
echo "Gitea-API: $API Repo: $REPO Tag: $TAG"
CHANGELOG="${CHANGELOG_FILE:-CHANGELOG.md}"
if [ ! -f "$CHANGELOG" ]; then
echo "$CHANGELOG nicht gefunden." >&2
exit 1
fi
command -v jq >/dev/null 2>&1 || { echo "jq fehlt." >&2; exit 1; }
# Abschnitt "## X.Y.Z" bis zur naechsten "## "-Ueberschrift, ohne die eigene
# Ueberschrift; danach fuehrende und abschliessende Leerzeilen entfernen.
BODY=$(awk -v ver="$VERSION" '
BEGIN { esc = ver; gsub(/\./, "\\.", esc); pat = "^## " esc "( |$)" }
$0 ~ pat { f = 1; next }
/^## / { if (f) exit }
f { print }
' "$CHANGELOG" | awk '
{ line[NR] = $0; if ($0 !~ /^[[:space:]]*$/) last = NR }
END { for (i = 1; i <= last; i++) print line[i] }
' | sed '1{/^$/d}')
if [ -z "$BODY" ]; then
echo "$CHANGELOG hat keinen Abschnitt fuer Version $VERSION (erwartet eine Zeile '## $VERSION – <Datum>'). Kein Release ohne Text." >&2
exit 1
fi
NAME="Tessera $VERSION"
CREATE_JSON=$(jq -n --arg tag "$TAG" --arg name "$NAME" --arg body "$BODY" \
'{tag_name: $tag, name: $name, body: $body, draft: false, prerelease: false}')
UPDATE_JSON=$(jq -n --arg name "$NAME" --arg body "$BODY" '{name: $name, body: $body}')
RELEASES_URL="$API/repos/$REPO/releases"
TAG_URL="$API/repos/$REPO/releases/tags/$TAG"
if [ "$DRY_RUN" -eq 1 ]; then
echo "Probelauf (kein Netzaufruf):"
echo " POST $RELEASES_URL"
echo " PATCH $RELEASES_URL/<id> (falls GET $TAG_URL bereits 200 liefert)"
printf '%s\n' "$CREATE_JSON"
exit 0
fi
if [ -z "${GITEA_TOKEN:-}" ]; then
echo "Kein Zugriffstoken in der Umgebung gesetzt (siehe Kopfkommentar)." >&2
exit 1
fi
umask 077
TMPDIR_REL=$(mktemp -d)
trap 'rm -rf "$TMPDIR_REL"' EXIT INT TERM
HDR="$TMPDIR_REL/headers"
RESP="$TMPDIR_REL/response.json"
JSONFILE="$TMPDIR_REL/payload.json"
printf 'Authorization: token %s\nContent-Type: application/json\n' "$GITEA_TOKEN" > "$HDR"
CODE=$(curl -sS --header @"$HDR" -o "$RESP" -w '%{http_code}' "$TAG_URL")
case "$CODE" in
200)
ID=$(jq -r .id "$RESP")
printf '%s' "$UPDATE_JSON" > "$JSONFILE"
CODE=$(curl -sS --header @"$HDR" -X PATCH --data @"$JSONFILE" -o "$RESP" -w '%{http_code}' "$RELEASES_URL/$ID")
if [ "$CODE" = "200" ]; then
echo "Release $TAG aktualisiert (id $ID)"
else
echo "PATCH $RELEASES_URL/$ID antwortete mit $CODE:" >&2
cat "$RESP" >&2
exit 1
fi
;;
404)
printf '%s' "$CREATE_JSON" > "$JSONFILE"
CODE=$(curl -sS --header @"$HDR" -X POST --data @"$JSONFILE" -o "$RESP" -w '%{http_code}' "$RELEASES_URL")
if [ "$CODE" = "201" ]; then
echo "Release $TAG angelegt (id $(jq -r .id "$RESP"))"
else
echo "POST $RELEASES_URL antwortete mit $CODE:" >&2
cat "$RESP" >&2
exit 1
fi
;;
*)
echo "GET $TAG_URL antwortete mit $CODE:" >&2
cat "$RESP" >&2
exit 1
;;
esac