feat(update): Testing/Stable-Update-Kanäle wie enconf (Suite=trixie, Komponente=Kanal)
Installer (EDGEGUARD_CHANNEL), Kanal-Lesen/-Schreiben ohne DB-State (sources.list ist Quelle der Wahrheit), Cluster-Endpoints für Kanalwechsel mit mTLS-Peer-Propagation + Drift-Erkennung, --allow-downgrades für testing→stable-Downgrades über den bestehenden sicheren Rolling-Update- Flow, Settings-UI mit Bestätigung, neues scripts/release.sh (Testing-Push datumsbasiert YYYY.MM.DD.NN, Stable-Promotion mit Verify-Gate + Git-Tag), publish.sh/cleanup-old.sh kanalfähig mit Stable-Tag-Schutz. Migriert Bestandsnodes automatisch von der alten "main"-Komponente auf "stable" (postinst, idempotent) — ohne das würden vor diesem Release installierte Nodes stillschweigend keine Updates mehr sehen, sobald main nicht mehr bespielt wird. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
181
scripts/release.sh
Executable file
181
scripts/release.sh
Executable file
@@ -0,0 +1,181 @@
|
||||
#!/usr/bin/env bash
|
||||
# EdgeGuard — Release erstellen (Testing-Push oder Stable-Promotion)
|
||||
#
|
||||
# Verwendung:
|
||||
# ./scripts/release.sh # Testing-Push: baut den aktuellen
|
||||
# # Stand, Version = datumsbasiert
|
||||
# # YYYY.MM.DD.NN, Upload → testing
|
||||
# ./scripts/release.sh stable # Stable-Promotion: baut den
|
||||
# # aktuellen Stand unter der
|
||||
# # nächsten Patch-Version (VERSION-
|
||||
# # Datei +1), Upload → stable,
|
||||
# # VERSION-Commit + Git-Tag v<version>
|
||||
# ./scripts/release.sh stable 1.4.0 # Stable mit expliziter Version
|
||||
#
|
||||
# Kanal-Modell 1:1 von enconf (netcell-webpanel) übernommen — Suite=trixie
|
||||
# fest, Komponente=Kanal (stable/testing). An EdgeGuard angepasst:
|
||||
# - Gates laufen über die bestehende Go-Quality-Baseline (`make deb` ruft
|
||||
# `release-check` + `management-ui`-tsc automatisch auf) statt eigener
|
||||
# Preflight-Schritte.
|
||||
# - Kein Docs-/Changelog-/Checksum-/Marketing-Site-Deploy — dafür
|
||||
# existiert bei EdgeGuard keine Infrastruktur (siehe CLAUDE.md).
|
||||
# - Testing-Versionen sind datumsbasiert (wie enconf) DAMIT sie (a) bei
|
||||
# apt immer über jeder Stable-Semver-Version sortieren und (b) nie mit
|
||||
# einer Stable-Versionsnummer kollidieren — Gitea dedupliziert
|
||||
# (Name,Version) global über alle Kanäle hinweg.
|
||||
#
|
||||
# Voraussetzung: GITEA_TOKEN (env oder ~/.gitea-token) mit Package-
|
||||
# Upload/Delete/Query-Scope.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
cd "$REPO_DIR"
|
||||
|
||||
OWNER="projekte"
|
||||
GITEA_URL="https://git.netcell-it.de"
|
||||
KEEP="${KEEP:-10}"
|
||||
|
||||
log() { echo "[release] $*"; }
|
||||
warn() { echo " ⚠ $*" >&2; }
|
||||
abort() { echo "🛑 ABBRUCH: $*" >&2; exit 1; }
|
||||
|
||||
if [ -z "${GITEA_TOKEN:-}" ]; then
|
||||
if [ -r "$HOME/.gitea-token" ]; then
|
||||
GITEA_TOKEN="$(tr -d '\n' < "$HOME/.gitea-token")"
|
||||
else
|
||||
abort "GITEA_TOKEN fehlt (env oder ~/.gitea-token)"
|
||||
fi
|
||||
fi
|
||||
export GITEA_TOKEN
|
||||
|
||||
# ─── Testing-Version: datumsbasiert YYYY.MM.DD.NN ──────────────────────
|
||||
# NN = laufende Nummer für den Tag, ermittelt aus den bereits im Registry
|
||||
# vorhandenen Versionen des Meta-Pakets (edgeguard) mit dem heutigen
|
||||
# Datumspräfix. Erster Release des Tages -> .01.
|
||||
next_testing_version() {
|
||||
local today
|
||||
today="$(date -u +%Y.%m.%d)"
|
||||
local raw
|
||||
raw="$(curl -fsS -H "Authorization: token $GITEA_TOKEN" \
|
||||
"$GITEA_URL/api/v1/packages/$OWNER?type=debian&q=edgeguard&limit=1000" 2>/dev/null || echo '[]')"
|
||||
local last_n
|
||||
last_n="$(printf '%s' "$raw" | jq -r --arg n "edgeguard" --arg pfx "${today}." \
|
||||
'.[] | select(.name==$n) | .version | select(startswith($pfx))' \
|
||||
| sed "s/^${today}\.//" | sort -n | tail -1)"
|
||||
printf '%s.%02d' "$today" "$(( ${last_n:-0} + 1 ))"
|
||||
}
|
||||
|
||||
# ─── Stable-Version: Patch-Bump der VERSION-Datei (Default) ────────────
|
||||
bump_patch() {
|
||||
local major minor patch
|
||||
IFS='.' read -r major minor patch <<< "$1"
|
||||
echo "${major}.${minor}.$((patch + 1))"
|
||||
}
|
||||
|
||||
# ─── Alte Version im Ziel-Kanal ersetzen — Upload derselben Nummer würde
|
||||
# sonst mit 409 den alten Inhalt behalten (Gitea dedupliziert (Name,
|
||||
# Version,Arch,Kanal-Pool) — best-effort, 404 ist der Normalfall). ──────
|
||||
delete_before_upload() {
|
||||
local channel="$1" version="$2" name="$3" arch="$4"
|
||||
local url="$GITEA_URL/api/packages/$OWNER/debian/pool/trixie/$channel/$name/$version/$arch"
|
||||
curl -sS -o /dev/null -w '' -X DELETE -H "Authorization: token $GITEA_TOKEN" "$url" || true
|
||||
}
|
||||
|
||||
replace_in_channel() {
|
||||
local channel="$1" version="$2"
|
||||
delete_before_upload "$channel" "$version" edgeguard-api amd64
|
||||
delete_before_upload "$channel" "$version" edgeguard-api arm64
|
||||
delete_before_upload "$channel" "$version" edgeguard-ui all
|
||||
delete_before_upload "$channel" "$version" edgeguard all
|
||||
}
|
||||
|
||||
# ─── verify_channel_debs — Health-Gate wie enconf verify_stable_debs ───
|
||||
# Holt den Packages-Index eines Kanals und prüft für JEDE dort
|
||||
# referenzierte .deb, ob sie unter ihrer Pool-URL wirklich mit HTTP 200
|
||||
# abrufbar ist (nicht nur ob der Index existiert — Gitea regeneriert den
|
||||
# Index nicht immer zuverlässig nach einem Delete).
|
||||
verify_channel_debs() {
|
||||
local channel="$1"
|
||||
log "verify_channel_debs($channel): jede indexierte .deb muss abrufbar sein..."
|
||||
local files f code bad=0 checked=0
|
||||
files="$(curl -sk -H "Authorization: token $GITEA_TOKEN" \
|
||||
"$GITEA_URL/api/packages/$OWNER/debian/dists/trixie/$channel/binary-amd64/Packages" 2>/dev/null \
|
||||
| awk '/^Filename:/{print $2}')"
|
||||
if [ -z "$files" ]; then
|
||||
warn "$channel: Packages-Index leer/nicht erreichbar — übersprungen"
|
||||
return 0
|
||||
fi
|
||||
while IFS= read -r f; do
|
||||
[ -z "$f" ] && continue
|
||||
checked=$((checked + 1))
|
||||
code="$(curl -sk -o /dev/null -w '%{http_code}' \
|
||||
-H "Authorization: token $GITEA_TOKEN" "$GITEA_URL/api/packages/$OWNER/debian/$f")"
|
||||
if [ "$code" != "200" ]; then
|
||||
warn "$channel: $f -> HTTP $code"
|
||||
bad=$((bad + 1))
|
||||
fi
|
||||
done <<< "$files"
|
||||
[ "$bad" -eq 0 ] || abort "$channel: $bad von $checked .deb(s) im Index nicht abrufbar — Registry inkonsistent."
|
||||
log "$channel: $checked .deb(s) verifiziert, alle abrufbar."
|
||||
}
|
||||
|
||||
# ─── Testing-Push ───────────────────────────────────────────────────────
|
||||
testing_push() {
|
||||
local new_version
|
||||
new_version="$(next_testing_version)"
|
||||
log "[testing 1/3] Gates + Build $new_version (make deb — release-check, amd64+arm64+ui)..."
|
||||
make VERSION="$new_version" deb
|
||||
|
||||
log "[testing 2/3] Alte Version im testing-Kanal ersetzen + hochladen..."
|
||||
replace_in_channel testing "$new_version"
|
||||
./scripts/apt-repo/publish.sh "$new_version" amd64 testing
|
||||
./scripts/apt-repo/publish.sh "$new_version" arm64 testing
|
||||
|
||||
log "[testing 3/3] Cleanup (keep last $KEEP) + Verify..."
|
||||
KEEP="$KEEP" ./scripts/apt-repo/cleanup-old.sh testing
|
||||
verify_channel_debs testing
|
||||
|
||||
log "✅ Testing-Release $new_version veröffentlicht."
|
||||
}
|
||||
|
||||
# ─── Stable-Promotion ───────────────────────────────────────────────────
|
||||
promote_stable() {
|
||||
local explicit_version="${1:-}"
|
||||
local current_version new_version
|
||||
current_version="$(cat "$REPO_DIR/VERSION")"
|
||||
new_version="${explicit_version:-$(bump_patch "$current_version")}"
|
||||
|
||||
git -C "$REPO_DIR" rev-parse "v$new_version" >/dev/null 2>&1 && \
|
||||
abort "Git-Tag v$new_version existiert bereits."
|
||||
[ -n "$(git -C "$REPO_DIR" status --porcelain)" ] && \
|
||||
abort "Working Tree nicht sauber — commit/stash erst, dann Stable-Release."
|
||||
|
||||
log "[stable 1/4] Gates + Build $new_version (make deb — release-check, amd64+arm64+ui)..."
|
||||
make VERSION="$new_version" deb
|
||||
|
||||
log "[stable 2/4] Alte Version im stable-Kanal ersetzen + hochladen..."
|
||||
replace_in_channel stable "$new_version"
|
||||
./scripts/apt-repo/publish.sh "$new_version" amd64 stable
|
||||
./scripts/apt-repo/publish.sh "$new_version" arm64 stable
|
||||
|
||||
log "[stable 3/4] VERSION-Datei setzen + Commit + Git-Tag..."
|
||||
printf '%s' "$new_version" > "$REPO_DIR/VERSION"
|
||||
git -C "$REPO_DIR" add VERSION
|
||||
git -C "$REPO_DIR" commit -m "chore(release): v$new_version stable"
|
||||
git -C "$REPO_DIR" tag "v$new_version"
|
||||
|
||||
log "[stable 4/4] Cleanup (keep last $KEEP) + Verify..."
|
||||
KEEP="$KEEP" ./scripts/apt-repo/cleanup-old.sh stable
|
||||
verify_channel_debs stable
|
||||
|
||||
log "✅ Stable-Release v$new_version fertig."
|
||||
log " Push nicht vergessen: git push origin main --tags"
|
||||
}
|
||||
|
||||
case "${1:-}" in
|
||||
stable) promote_stable "${2:-}" ;;
|
||||
"") testing_push ;;
|
||||
*) abort "unbekanntes Argument '$1' (erwartet: 'stable' oder kein Argument)" ;;
|
||||
esac
|
||||
Reference in New Issue
Block a user