#!/usr/bin/env bash # Liefert Firmware-Images und den Stationscode an den Pi aus. # # Aufruf: tools/station/deploy.sh [target ...] # Ohne target-Argumente werden alle in build/*/ gefundenen _ota.img uebertragen. set -euo pipefail HOST=${1:?Aufruf: tools/station/deploy.sh [target ...]} shift || true HERE=$(cd "$(dirname "$0")" && pwd) ROOT=$(cd "$HERE/../.." && pwd) MKIMAGE="$ROOT/tools/mkimage/nl_mkimage.py" # /usr/bin/ssh statt einem ssh im PATH, das sich anders verhaelt (siehe A3-Notiz im # Abnahmebericht). Ueberschreibbar, damit tests/test_deploy.sh gegen einen Stub statt # einen echten Pi laufen kann - dasselbe Prinzip wie bei NINLIGHT_STATION_ROOT unten. SSH_BIN=${NINLIGHT_SSH_BIN:-/usr/bin/ssh} declare -a FILES=() if [ $# -gt 0 ]; then # Gezielter Deploy: nur die genannten Geraetetypen werden ersetzt, alle # anderen Images im Zielverzeichnis bleiben unangetastet (siehe FULL_SYNC # unten). FULL_SYNC=0 for t in "$@"; do f="$ROOT/build/$t/ninlight_${t}_ota.img" [ -f "$f" ] || { echo "Fehler: $f fehlt - erst bauen" >&2; exit 1; } FILES+=("$f") done else FULL_SYNC=1 while IFS= read -r f; do FILES+=("$f"); done < <(find "$ROOT/build" -maxdepth 2 -name 'ninlight_*_ota.img' | sort) [ ${#FILES[@]} -gt 0 ] || { echo "Fehler: keine Images unter $ROOT/build gefunden" >&2; exit 1; } fi echo "== Images pruefen ==" STAGE=$(mktemp -d) trap 'rm -rf "$STAGE"' EXIT printf '%-26s %-10s %s\n' GERAET VERSION BYTES for f in "${FILES[@]}"; do base=$(basename "$f") target=${base#ninlight_}; target=${target%_ota.img} # Ein Image, das die Pruefung nicht besteht, wuerde jede Lampe ablehnen - # das faellt sonst erst am Geraet auf. out=$(python3 "$MKIMAGE" --verify "$f" --expect-device "$target") || { echo "Fehler: $base besteht die Trailer-Pruefung nicht, nichts uebertragen" >&2; exit 1; } # shellcheck disable=SC2086 set -- $out printf '%-26s %-10s %s\n' "$1" "$2" "$3" cp "$f" "$STAGE/$base" done echo "== uebertragen ==" # -c (Inhaltsvergleich per Checksumme statt Groesse+Zeitstempel): ein neues # Image kann zufaellig dieselbe Byte-Groesse wie das alte haben, und bei # schnell aufeinanderfolgenden Deploys reicht rsyncs Sekundenaufloesung fuer # den Zeitstempel nicht, um das als Aenderung zu erkennen. Ohne -c wuerde # rsync ein solches Update dann stillschweigend ueberspringen. # # /tmp/ninlight-images auf dem Pi ist nur eine fluechtige Ablage fuer diesen # Lauf - hier darf --delete unbedingt ran, das betrifft nicht das live # ausgelieferte Verzeichnis. # -e: rsync soll fuer den Transport zum Pi denselben ssh wie oben benutzen. rsync -a -c --delete -e "$SSH_BIN" "$STAGE/" "$HOST:/tmp/ninlight-images/" rsync -a -e "$SSH_BIN" "$HERE/server.py" "$HERE/ninlight-station" "$HERE/ninlight-station.service" "$HOST:/tmp/" # Pfade als Variablen mit Default: so kann ein Test die Installation gegen # ein Sandbox-Verzeichnis statt gegen echte Systempfade laufen lassen. "$SSH_BIN" "$HOST" bash -s -- "$FULL_SYNC" <<'REMOTE' set -e FULL_SYNC=$1 ROOT_DIR=${NINLIGHT_STATION_ROOT:-/opt/ninlight-station} BIN_PATH=${NINLIGHT_STATION_BIN:-/usr/local/bin/ninlight-station} UNIT_PATH=${NINLIGHT_STATION_UNIT:-/etc/systemd/system/ninlight-station.service} sudo install -d -m 0755 "$ROOT_DIR/images" if [ "$FULL_SYNC" -eq 1 ]; then # Vollabgleich: alle Images ausserhalb der aktuellen Auslieferung entfernen. echo " voller Abgleich - Images ausserhalb der Auswahl werden entfernt" sudo rsync -a -c --delete /tmp/ninlight-images/ "$ROOT_DIR/images/" else # Gezielter Deploy: nur die uebertragenen Dateien ersetzen/ergaenzen, alle # anderen Geraetetypen im Zielverzeichnis bleiben erhalten (kein --delete). echo " gezielter Deploy - andere Geraetetypen im Zielverzeichnis bleiben erhalten" sudo rsync -a -c /tmp/ninlight-images/ "$ROOT_DIR/images/" fi sudo install -m 0755 /tmp/server.py "$ROOT_DIR/server.py" sudo install -m 0755 /tmp/ninlight-station "$BIN_PATH" sudo install -m 0644 /tmp/ninlight-station.service "$UNIT_PATH" rm -rf /tmp/ninlight-images /tmp/server.py /tmp/ninlight-station /tmp/ninlight-station.service sudo systemctl daemon-reload if systemctl is-active --quiet ninlight-station; then if sudo systemctl restart ninlight-station; then echo " Dienst neu gestartet" else echo " Fehler: Dienst lief, Neustart nach dem Deploy ist aber fehlgeschlagen" >&2 exit 1 fi else echo " Dienst laeuft nicht (Station ist aus)" fi REMOTE echo "fertig."