Files
Flaro/Tools/image-veroeffentlichen.sh
ScarriffleandClaude Opus 5 e1b3625809 Das Würfeln der Build-Kennung brach den ganzen Lauf ab
`tr -dc … < /dev/urandom | head -c 6`: head schliesst die Pipe nach sechs
Zeichen, tr bekommt SIGPIPE, und mit `set -o pipefail` gilt das als
Fehlschlag. Das Skript endete mit 141, bevor es irgendetwas gebaut hatte
— dieselbe Falle, vor der dreissig Zeilen weiter oben eine Anmerkung
wegen `grep -q` steht.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-04 15:58:18 +02:00

149 lines
5.5 KiB
Bash
Executable File

#!/bin/bash
# Baut das Flaro-Image für beide Architekturen und legt es in die Registry.
#
# **Der Weg von Hand** — für den Fall, dass kein Gitea-Runner läuft. Sonst
# macht das `.gitea/workflows/image.yml` bei jedem Push von selbst.
#
# Tools/image-veroeffentlichen.sh → :latest
# Tools/image-veroeffentlichen.sh 0.2.0 → :latest und :0.2.0
#
# Nur eine Architektur — deutlich schneller, weil die fremde sonst unter
# Emulation läuft:
#
# FLARO_PLATTFORMEN=linux/amd64 Tools/image-veroeffentlichen.sh
#
# Vorher einmalig anmelden — das Passwort tippst du selbst ein:
#
# docker login git.scarriffle.com
set -euo pipefail
REGISTRY="${FLARO_REGISTRY:-git.scarriffle.com}"
PFAD="${FLARO_IMAGE_PATH:-scarriffle/flaro}"
BILD="$REGISTRY/$PFAD"
FASSUNG="${1:-}"
PLATTFORMEN="${FLARO_PLATTFORMEN:-linux/amd64,linux/arm64}"
cd "$(dirname "$0")/.."
command -v docker >/dev/null || { echo "docker fehlt."; exit 1; }
# **Ohne Anmeldung bricht der Push erst nach dem ganzen Bau ab.** Zwei
# Minuten Arbeit für eine Fehlermeldung, die am Anfang gestanden hätte.
# Kein `docker info | grep -q`: `grep -q` beendet die Pipe beim ersten
# Treffer, Docker bekommt SIGPIPE, und mit `set -o pipefail` gilt das als
# Fehlschlag. Die Prüfung meldete dann „Docker läuft nicht", während es lief.
if ! docker info >/dev/null 2>&1; then
echo "Docker läuft nicht."; exit 1
fi
if ! grep -q "$REGISTRY" ~/.docker/config.json 2>/dev/null; then
echo "Nicht an $REGISTRY angemeldet. Erst:"
echo
echo " docker login $REGISTRY"
exit 1
fi
# **Vor dem Bauen, nicht danach.** Laufen App und Browser farblich
# auseinander, soll das auffallen, bevor ein Image daraus wird.
if command -v python3 >/dev/null && [ -f Tools/tokens-pruefen.py ]; then
python3 Tools/tokens-pruefen.py || {
echo
echo "Abgebrochen. Mit FLARO_DRIFT_EGAL=1 trotzdem bauen."
[ "${FLARO_DRIFT_EGAL:-}" = "1" ] || exit 1
}
fi
# Fragt die Registry, welcher Stand dort unter `latest` liegt.
#
# Anonym — das Paket ist öffentlich lesbar. Schlägt es fehl, ist die Antwort
# leer, und der Aufrufer entscheidet, was das bedeutet.
registry_digest() {
local token
token=$(curl -fsS --max-time 20 \
"https://$REGISTRY/v2/token?scope=repository:$PFAD:pull&service=container_registry" \
| python3 -c 'import json,sys;print(json.load(sys.stdin).get("token",""))' 2>/dev/null) || return 1
[ -n "$token" ] || return 1
curl -fsS --max-time 20 -D- -o /dev/null \
-H "Authorization: Bearer $token" \
-H "Accept: application/vnd.oci.image.index.v1+json" \
"https://$REGISTRY/v2/$PFAD/manifests/latest" 2>/dev/null \
| awk 'BEGIN{IGNORECASE=1} /^docker-content-digest:/ {print $2}' | tr -d '\r'
}
MARKEN=(-t "$BILD:latest")
[ -n "$FASSUNG" ] && MARKEN+=(-t "$BILD:$FASSUNG")
# **Eine Kennung je Bau.**
#
# Sechs Zeichen, jedes Mal neu — auch bei unverändertem Quelltext. Sie landet
# im Abbild und steht danach in der Oberfläche unter „Einstellungen" sowie
# unter `/health`. Damit ist die Frage „läuft auf dem NAS wirklich der Bau von
# eben?" in zwei Sekunden beantwortet statt in einem halben Tag.
#
# Ohne I, O, 0 und 1: diese Kennung wird vorgelesen und abgetippt.
#
# **Nicht `tr < /dev/urandom | head -c 6`.** `head` schliesst die Pipe nach
# sechs Zeichen, `tr` bekommt SIGPIPE, und mit `set -o pipefail` gilt das als
# Fehlschlag — das Skript brach mit 141 ab, bevor es irgendetwas gebaut hatte.
# Dieselbe Falle wie oben bei `grep -q`, und sie hat hier zweimal zugeschlagen.
BUILD=$(python3 -c "import secrets; print(''.join(secrets.choice('ABCDEFGHJKLMNPQRSTUVWXYZ23456789') for _ in range(6)))")
echo "Build-Kennung dieses Laufs: $BUILD"
# **Ein eigener Builder.** Der Vorgabe-Builder von Docker Desktop kann kein
# Manifest mit zwei Architekturen bauen; `docker buildx create` schon.
docker buildx inspect flaro-bauer >/dev/null 2>&1 \
|| docker buildx create --name flaro-bauer --use >/dev/null
# **Den Digest festhalten, bevor gebaut wird.** Nur so lässt sich hinterher
# sagen, ob wirklich etwas Neues angekommen ist.
vorher=$(registry_digest || true)
echo "Baue $BILD für $PLATTFORMEN."
if ! docker buildx build \
--builder flaro-bauer \
--platform "$PLATTFORMEN" \
-f server/Dockerfile \
--build-arg "FLARO_BUILD=$BUILD" \
"${MARKEN[@]}" \
--push . ; then
echo
echo "Der Bau oder das Hochladen ist gescheitert. In der Registry liegt weiter:"
echo " ${vorher:-nichts}"
exit 1
fi
# **Nachsehen, statt zu glauben.**
#
# `docker buildx build --push` hat bei einem abgebrochenen Upload schon mit
# Rückgabewert 0 beendet, nachdem es „ERROR: failed to build" gedruckt hatte —
# ein TLS-Zeitlimit mitten im Hochladen. Das Skript meldete Erfolg, in der
# Registry lag das alte Image, und auf dem NAS kam eine Fassung an, die
# niemand gebaut hatte. Seitdem wird gefragt, nicht vertraut.
nachher=$(registry_digest || true)
if [ -z "$nachher" ]; then
echo
echo "Die Registry antwortet nicht auf $BILD:latest — hochgeladen wurde offenbar nichts."
exit 1
fi
if [ -n "$vorher" ] && [ "$vorher" = "$nachher" ]; then
echo
echo "In der Registry liegt noch derselbe Stand wie vorher:"
echo " $nachher"
echo "Das Hochladen ist also nicht durchgekommen. Noch einmal versuchen."
exit 1
fi
echo
echo "Fertig. In der Registry liegt jetzt:"
echo " $nachher"
echo
echo "Build-Kennung: $BUILD"
echo " Dieselbe muss nach dem Ziehen unter Einstellungen stehen."
echo " Steht dort eine andere, läuft auf dem NAS ein älteres Abbild."
echo
echo "Auf dem NAS:"
echo " image: $BILD:${FASSUNG:-latest}"