#!/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}"