Files
edgeguard-native/scripts/apt-repo/cleanup-old.sh
noroot bab82f8d5b 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>
2026-09-02 16:31:52 +02:00

133 lines
4.6 KiB
Bash
Executable File

#!/bin/bash
# Cleanup-old: löscht alle EdgeGuard-Versionen im Gitea Package
# Registry außer den letzten N (default 10) — pro Kanal (Komponente).
#
# Wird vom Makefile/release.sh direkt nach erfolgreichem Upload
# aufgerufen. Reihenfolge ist wichtig: ERST der neue Build hochladen,
# DANN die ältesten wegschmeißen — sonst riskieren wir bei Cleanup-vor-
# Upload eine Lücke.
#
# Stable-Schutz: Versionen, die als Git-Tag `v<version>` im Repo stehen,
# werden im stable-Kanal NIE gelöscht (analog enconf STABLE_PROTECT) —
# ein Kunde, der genau diese Version installiert hat, muss sie über
# apt-get weiterhin ziehen können.
#
# Voraussetzungen:
# - ~/.gitea-token mit write-Package-Scope
# - jq + curl
#
# Aufruf:
# ./cleanup-old.sh # Kanal stable, KEEP=10 (default)
# ./cleanup-old.sh testing # Kanal testing
# KEEP=20 ./cleanup-old.sh stable # mehr behalten
set -euo pipefail
KEEP="${KEEP:-10}"
OWNER="projekte"
DIST="trixie"
COMPONENT="${1:-stable}"
case "$COMPONENT" in stable|testing) ;; *) echo "cleanup-old: unknown channel '$COMPONENT' (expected stable or testing)" >&2; exit 2 ;; esac
BASE="https://git.netcell-it.de"
# Geschützte Versionen (nur relevant im stable-Kanal): jeder vorhandene
# Git-Tag `v*` im Repo-Root. Leer wenn nicht im Repo ausgeführt oder im
# testing-Kanal (dort gibt es keine Tags/keinen Schutz).
PROTECTED_VERSIONS=""
if [ "$COMPONENT" = "stable" ]; then
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
PROTECTED_VERSIONS="$(git -C "$REPO_ROOT" tag --list 'v*' 2>/dev/null | sed 's/^v//' || true)"
fi
is_protected() {
local v="$1"
[ -z "$PROTECTED_VERSIONS" ] && return 1
printf '%s\n' "$PROTECTED_VERSIONS" | grep -qx "$v"
}
if [ -z "${GITEA_TOKEN:-}" ]; then
if [ -r "$HOME/.gitea-token" ]; then
GITEA_TOKEN="$(tr -d '\n' < "$HOME/.gitea-token")"
else
echo "cleanup-old: missing ~/.gitea-token and GITEA_TOKEN env" >&2
exit 1
fi
fi
TOK="$GITEA_TOKEN"
# Pkg-Spec: "name:arch". arch="all" für noarch-Pakete (meta + UI),
# "amd64" für edgeguard-api. Mehrere arch je name = mehrere Zeilen.
PKGS=(
"edgeguard:all"
"edgeguard-api:amd64"
"edgeguard-api:arm64"
"edgeguard-ui:all"
)
cleanup_pkg() {
local pkg="$1"
local arch="$2"
# Versionen sammeln. Gitea Package-API liefert flache Liste über ALLE
# Kanäle hinweg: /api/v1/packages/{owner}?type=debian&q={name}
# Wir filtern client-seitig auf name (exact-match — Gitea-Query ist
# leider substring-fuzzy) UND auf das Versionsformat des aktuellen
# Kanals — stable=Semver (X.Y.Z), testing=datumsbasiert (YYYY.MM.DD.NN).
# Die beiden Formate überlappen nie, das hält testing- und stable-
# Versionen sauber getrennt, obwohl Gitea (Name,Version) global dedupliziert.
local raw
raw="$(curl -fsS -H "Authorization: token $TOK" \
"$BASE/api/v1/packages/$OWNER?type=debian&q=$pkg&limit=1000")"
local ver_pattern
if [ "$COMPONENT" = "stable" ]; then
ver_pattern='^[0-9]+\.[0-9]+\.[0-9]+$'
else
ver_pattern='^[0-9]{4}\.[0-9]{2}\.[0-9]{2}\.[0-9]+$'
fi
local versions
versions="$(printf '%s' "$raw" | jq -r --arg n "$pkg" \
'.[] | select(.name==$n) | .version' | grep -E "$ver_pattern" | sort -V -r | awk '!seen[$0]++')"
if [ -z "$versions" ]; then
echo " $pkg ($arch): no $COMPONENT versions found, skip"
return 0
fi
local count
count="$(printf '%s\n' "$versions" | wc -l)"
if [ "$count" -le "$KEEP" ]; then
echo " $pkg ($arch): $count $COMPONENT versions, ≤ keep=$KEEP, nothing to delete"
return 0
fi
local to_delete
to_delete="$(printf '%s\n' "$versions" | tail -n +$((KEEP+1)))"
local kept_count=$((count - $(printf '%s\n' "$to_delete" | wc -l)))
echo " $pkg ($arch): $count $COMPONENT versions total, keeping $kept_count newest, considering $(printf '%s\n' "$to_delete" | wc -l) for deletion"
while IFS= read -r v; do
[ -z "$v" ] && continue
if is_protected "$v"; then
echo " skip $pkg $v $arch (protected — git tag v$v exists)"
continue
fi
# DELETE-Endpoint für Debian-Pakete:
# /api/packages/{owner}/debian/pool/{dist}/{comp}/{name}/{version}/{arch}
local url="$BASE/api/packages/$OWNER/debian/pool/$DIST/$COMPONENT/$pkg/$v/$arch"
local http
http="$(curl -sS -o /dev/null -w '%{http_code}' \
-X DELETE -H "Authorization: token $TOK" "$url")"
case "$http" in
204|404) echo " delete $pkg $v $arch -> $http" ;;
*) echo " delete $pkg $v $arch -> $http (failed)" ;;
esac
done <<< "$to_delete"
}
for spec in "${PKGS[@]}"; do
IFS=':' read -r pkg arch <<< "$spec"
cleanup_pkg "$pkg" "$arch"
done