Auf dem Rechner des Nutzers liegt im PATH ein ssh, das sich anders verhaelt als das System-ssh - die Skripte sollen sich darauf nicht verlassen. setup_pi.sh und deploy.sh riefen ssh, scp und rsync bisher ohne vollen Pfad auf. Beide Skripte nutzen jetzt SSH_BIN/SCP_BIN mit Default /usr/bin/ssh bzw. /usr/bin/scp, ueberschreibbar per NINLIGHT_SSH_BIN/NINLIGHT_SCP_BIN - dasselbe Prinzip wie NINLIGHT_STATION_ROOT weiter unten in deploy.sh, das Tests ohne echten Pi ermoeglicht. rsync bekommt "-e $SSH_BIN", damit auch dessen Transport ueber /usr/bin/ssh laeuft. Die Beispielaufrufe in setup_pi.sh's Abschlussmeldung und in der README bleiben bewusst bei schlichtem "ssh" - dort geht es um das, was der Nutzer selbst eintippt. test: tools/station/tests/test_deploy.sh auf den neuen Override umgestellt (NINLIGHT_SSH_BIN zeigt auf den ssh-Stub) und um ein Szenario ergaenzt, das den /usr/bin/ssh- bzw. /usr/bin/scp-Default in beiden Skripten absichert. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
102 lines
4.4 KiB
Bash
Executable File
102 lines
4.4 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Liefert Firmware-Images und den Stationscode an den Pi aus.
|
|
#
|
|
# Aufruf: tools/station/deploy.sh <pi-host> [target ...]
|
|
# Ohne target-Argumente werden alle in build/*/ gefundenen _ota.img uebertragen.
|
|
set -euo pipefail
|
|
HOST=${1:?Aufruf: tools/station/deploy.sh <pi-host> [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."
|