Private
Public Access
addet test set
This commit is contained in:
1 parent
d370f992ce
commit
b0f35bf3cc
21 files changed
+1796
-183
No files matched your search
@@ -0,0 +1,172 @@
|
||||
#!/usr/bin/env bash
|
||||
# Runs on the Pi, after rsync has completed. No secrets or device defaults.
|
||||
set -euo pipefail
|
||||
privileged=false
|
||||
if [[ ${1:-} == --privileged ]]; then privileged=true; shift; fi
|
||||
root=${1:?Basisordner fehlt}
|
||||
release=${2:?Version fehlt}
|
||||
bind_host=${3:-}
|
||||
[[ $root =~ ^/[a-zA-Z0-9_./-]+$ && $root != / && /$root/ != *'/../'* && /$root/ != *'/./'* ]] || exit 2
|
||||
[[ $release =~ ^[a-zA-Z0-9_-]+$ ]] || exit 2
|
||||
[[ -z $bind_host || $bind_host == 127.0.0.1 || $bind_host == 0.0.0.0 ]] || exit 2
|
||||
candidate="$root/releases/$release"
|
||||
service=halloween-grab.service
|
||||
unit=/etc/systemd/system/$service
|
||||
if ! $privileged; then
|
||||
exec 9>"$root/.update.lock"
|
||||
flock -n 9 || { echo 'Ein anderes Update läuft.' >&2; exit 2; }
|
||||
else
|
||||
[[ $(id -u) == 0 && ${SUDO_USER:-} =~ ^[a-zA-Z_][a-zA-Z0-9_-]*$ && $SUDO_USER != root ]] || {
|
||||
echo 'Privilegierter Teil darf nur vom Upload über sudo gestartet werden.' >&2; exit 2;
|
||||
}
|
||||
fi
|
||||
[[ -f $candidate/run.py && ! -L $candidate ]] || exit 2
|
||||
[[ ! -e $root/current || -L $root/current ]] || { echo 'current muss ein Symlink sein.' >&2; exit 2; }
|
||||
previous=
|
||||
if [[ -L $root/current ]]; then
|
||||
previous=$(readlink -f "$root/current")
|
||||
[[ -d $previous && ( $previous == "$root" || $previous == "$root/releases/"* ) ]] || {
|
||||
echo 'Ungültiges Ziel von current.' >&2; exit 2;
|
||||
}
|
||||
fi
|
||||
if [[ -z $previous && -f $root/run.py && -x $root/.venv/bin/python ]]; then
|
||||
previous=$root # Existing flat installation remains untouched as rollback target.
|
||||
fi
|
||||
[[ ! -L $root/data ]] || { echo 'data darf kein Symlink sein.' >&2; exit 2; }
|
||||
mkdir -p "$root/data" "$root/backups"
|
||||
chmod 700 "$root/backups"
|
||||
backup="$root/backups/$release"
|
||||
if [[ -f $root/service-host ]]; then old_host=$(cat "$root/service-host"); else old_host=127.0.0.1; fi
|
||||
bind_host=${bind_host:-$old_host}
|
||||
[[ $bind_host == 127.0.0.1 || $bind_host == 0.0.0.0 ]] || exit 2
|
||||
if ! $privileged; then
|
||||
[[ $(id -un) != root ]] || { echo 'Update als normalen Pi-Benutzer starten.' >&2; exit 2; }
|
||||
# Prepare dependencies as the Pi user, before the single sudo invocation.
|
||||
# The parent retains the update lock while sudo and its child are running.
|
||||
python3 -m venv --system-site-packages "$candidate/.venv"
|
||||
"$candidate/.venv/bin/python" -m pip install --only-binary=:all: -r "$candidate/requirements.txt"
|
||||
"$candidate/.venv/bin/python" -c 'import fastapi, uvicorn, PIL, picamera2'
|
||||
"$candidate/.venv/bin/python" -m compileall -q "$candidate/halloween" "$candidate/run.py"
|
||||
sudo bash "$candidate/tools/activate_pi_release.sh" --privileged "$root" "$release" "$bind_host"
|
||||
exit $?
|
||||
fi
|
||||
|
||||
# All systemd/root filesystem work, including rollback, runs inside this one
|
||||
# sudo process. No sudo timestamp, keepalive, password pipe or sudoers exception.
|
||||
command -v runuser >/dev/null || { echo 'runuser fehlt auf dem Pi (util-linux).' >&2; exit 2; }
|
||||
|
||||
as_user() {
|
||||
runuser -u "$SUDO_USER" -- "$@"
|
||||
}
|
||||
mkdir "$backup"
|
||||
chown "$SUDO_UID:$SUDO_GID" "$backup"
|
||||
had_unit=false
|
||||
was_enabled=false
|
||||
if test -e "$unit"; then
|
||||
grep -qF '# Managed by Halloween project: tools/install_pi_service.sh' "$unit" || {
|
||||
echo 'Fremde systemd-Unit wird nicht verändert.' >&2; exit 2;
|
||||
}
|
||||
had_unit=true
|
||||
cat "$unit" > "$backup/service.unit"
|
||||
chmod 600 "$backup/service.unit"
|
||||
chown "$SUDO_UID:$SUDO_GID" "$backup/service.unit"
|
||||
if systemctl is-enabled --quiet "$service"; then was_enabled=true; fi
|
||||
fi
|
||||
|
||||
install_service() {
|
||||
local host=$1 temporary
|
||||
temporary=$(mktemp) || return 1
|
||||
# Render the unit and inspect user-owned project files without root privileges.
|
||||
if ! as_user bash "$candidate/tools/install_pi_service.sh" --project-dir "$root" --host "$host" --print-unit > "$temporary"; then
|
||||
rm -f "$temporary"
|
||||
return 1
|
||||
fi
|
||||
if ! install -m 0644 "$temporary" "$unit"; then
|
||||
rm -f "$temporary"
|
||||
return 1
|
||||
fi
|
||||
rm -f "$temporary" || return 1
|
||||
systemctl daemon-reload && systemctl enable "$service" &&
|
||||
systemctl restart "$service" && systemctl is-active --quiet "$service"
|
||||
}
|
||||
|
||||
switch_link() {
|
||||
ln -s "$1" "$root/.current-$release"
|
||||
mv -Tf "$root/.current-$release" "$root/current" &&
|
||||
chown -h "$SUDO_UID:$SUDO_GID" "$root/current"
|
||||
}
|
||||
check_health() {
|
||||
as_user "$candidate/.venv/bin/python" - <<'PY' || return 1
|
||||
import json, time, urllib.request
|
||||
for attempt in range(30):
|
||||
try:
|
||||
with urllib.request.urlopen('http://127.0.0.1:8000/api/status', timeout=1) as response:
|
||||
value = json.load(response)
|
||||
if isinstance(value, dict) and isinstance(value.get('controller'), dict) and isinstance(value.get('camera'), dict):
|
||||
break
|
||||
except Exception:
|
||||
pass
|
||||
time.sleep(1)
|
||||
else:
|
||||
raise SystemExit('WebUI antwortet nicht innerhalb der Startfrist.')
|
||||
PY
|
||||
systemctl is-active --quiet "$service"
|
||||
}
|
||||
transaction=false
|
||||
data_saved=false
|
||||
rollback() {
|
||||
status=$?
|
||||
trap - EXIT INT TERM
|
||||
if $transaction; then
|
||||
set +e
|
||||
echo 'Update fehlgeschlagen: vorherigen Stand wiederherstellen.' >&2
|
||||
rollback_ok=true
|
||||
if ! systemctl stop "$service"; then
|
||||
echo "Dienst lässt sich nicht stoppen; Daten nicht zurückgespielt. Sicherung: $backup" >&2
|
||||
exit 1
|
||||
fi
|
||||
if $data_saved; then
|
||||
mv "$root/data" "$backup/failed-data" && cp -a "$backup/data" "$root/data" || rollback_ok=false
|
||||
fi
|
||||
if [[ -n $previous ]]; then switch_link "$previous" || rollback_ok=false; else rm -f "$root/current"; fi
|
||||
printf '%s\n' "$old_host" > "$root/service-host" &&
|
||||
chown "$SUDO_UID:$SUDO_GID" "$root/service-host" || rollback_ok=false
|
||||
if $had_unit; then
|
||||
install -m 0644 "$backup/service.unit" "$unit" || rollback_ok=false
|
||||
else
|
||||
systemctl disable "$service" || rollback_ok=false
|
||||
rm -f "$unit" || rollback_ok=false
|
||||
fi
|
||||
systemctl daemon-reload || rollback_ok=false
|
||||
if $had_unit; then
|
||||
if ! $was_enabled; then systemctl disable "$service" || rollback_ok=false; fi
|
||||
systemctl restart "$service" && check_health || rollback_ok=false
|
||||
elif [[ -n $previous ]]; then
|
||||
install_service "$old_host" && check_health || rollback_ok=false
|
||||
fi
|
||||
if $rollback_ok; then echo 'Rückfall abgeschlossen.' >&2; else echo "Rückfall unvollständig! Dienst und Sicherung $backup prüfen." >&2; fi
|
||||
exit 1
|
||||
fi
|
||||
exit "$status"
|
||||
}
|
||||
trap rollback EXIT
|
||||
trap 'exit 130' INT
|
||||
trap 'exit 143' TERM
|
||||
if $had_unit; then transaction=true; systemctl stop "$service"; fi
|
||||
# Refuse an unmanaged server, rather than claiming its response as update success.
|
||||
as_user "$candidate/.venv/bin/python" - <<'PY'
|
||||
import socket
|
||||
with socket.socket() as sock:
|
||||
if sock.connect_ex(('127.0.0.1', 8000)) == 0:
|
||||
raise SystemExit('Port 8000 ist noch belegt. Manuell gestartete WebUI zuerst beenden.')
|
||||
PY
|
||||
cp -a "$root/data" "$backup/data"
|
||||
data_saved=true
|
||||
transaction=true
|
||||
switch_link "$candidate"
|
||||
printf '%s\n' "$bind_host" > "$root/service-host"
|
||||
chown "$SUDO_UID:$SUDO_GID" "$root/service-host"
|
||||
install_service "$bind_host"
|
||||
check_health
|
||||
transaction=false
|
||||
printf 'Version %s aktiv; WebUI-Autostart eingeschaltet. Sicherung: %s\n' "$release" "$backup"
|
||||
@@ -5,15 +5,19 @@ project_dir=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd -P)
|
||||
service_name=halloween-grab.service
|
||||
unit_path="/etc/systemd/system/$service_name"
|
||||
apply=false
|
||||
print_unit=false
|
||||
bind_host=127.0.0.1
|
||||
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
Richtet die Halloween-WebUI als systemd-Dienst auf dem Raspberry Pi ein.
|
||||
|
||||
Aufruf: bash tools/install_pi_service.sh [--apply]
|
||||
Aufruf: bash tools/install_pi_service.sh [--project-dir BASISORDNER] [--host 127.0.0.1|0.0.0.0] [--apply]
|
||||
|
||||
Ohne --apply wird nur die geplante Unit gezeigt. --apply installiert sie mit
|
||||
sudo, aktiviert den Autostart und startet die WebUI sofort neu.
|
||||
--print-unit gibt nur die Unit auf stdout aus; interner Renderer für den Upload,
|
||||
ohne Installation, Paketprüfung oder sudo.
|
||||
EOF
|
||||
}
|
||||
|
||||
@@ -24,6 +28,11 @@ die() {
|
||||
|
||||
while (($#)); do
|
||||
case $1 in
|
||||
--project-dir|--host)
|
||||
(($# >= 2)) || die "Wert für $1 fehlt"
|
||||
if [[ $1 == --project-dir ]]; then project_dir=$2; else bind_host=$2; fi
|
||||
shift 2 ;;
|
||||
--print-unit) print_unit=true; shift ;;
|
||||
--apply) apply=true; shift ;;
|
||||
-h|--help) usage; exit 0 ;;
|
||||
*) die "Unbekanntes Argument: $1" ;;
|
||||
@@ -33,9 +42,13 @@ done
|
||||
service_user=$(id -un)
|
||||
[[ $service_user =~ ^[a-zA-Z_][a-zA-Z0-9_-]*$ ]] || die 'Ungeeigneter Benutzername für systemd'
|
||||
[[ $service_user != root ]] || die 'Skript als normaler Pi-Benutzer ausführen, nicht als root'
|
||||
[[ $project_dir =~ ^/[a-zA-Z0-9_./-]+$ ]] || die 'Projektpfad enthält für die Unit ungeeignete Zeichen'
|
||||
[[ $project_dir =~ ^/[a-zA-Z0-9_./-]+$ && $project_dir != / ]] || die 'Projektpfad enthält für die Unit ungeeignete Zeichen'
|
||||
[[ /$project_dir/ != *'/../'* && /$project_dir/ != *'/./'* ]] || die 'Projektpfad enthält . oder .. Segmente'
|
||||
|
||||
[[ $bind_host == 127.0.0.1 || $bind_host == 0.0.0.0 ]] || die 'Ungültige Bind-Adresse'
|
||||
code_dir=$project_dir
|
||||
if [[ -L $project_dir/current ]]; then code_dir=$project_dir/current; fi
|
||||
|
||||
unit_contents=$(cat <<EOF
|
||||
# Managed by Halloween project: tools/install_pi_service.sh
|
||||
[Unit]
|
||||
@@ -45,8 +58,9 @@ After=network.target
|
||||
[Service]
|
||||
Type=simple
|
||||
User=$service_user
|
||||
WorkingDirectory=$project_dir
|
||||
ExecStart=$project_dir/.venv/bin/python $project_dir/run.py
|
||||
WorkingDirectory=$code_dir
|
||||
ExecStart=$code_dir/.venv/bin/python -m uvicorn halloween.web:create_app --factory --host $bind_host --port 8000
|
||||
Environment=HALLOWEEN_STATE_DIR=$project_dir/data
|
||||
Environment=PYTHONUNBUFFERED=1
|
||||
Restart=on-failure
|
||||
RestartSec=3
|
||||
@@ -56,6 +70,12 @@ WantedBy=multi-user.target
|
||||
EOF
|
||||
)
|
||||
|
||||
if $print_unit; then
|
||||
$apply && die '--print-unit kann nicht mit --apply kombiniert werden'
|
||||
printf '%s\n' "$unit_contents"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
printf 'Dienst: %s\nBenutzer: %s\nProjekt: %s\n' "$service_name" "$service_user" "$project_dir"
|
||||
if ! $apply; then
|
||||
printf '\nVorschau für %s:\n%s\n' "$unit_path" "$unit_contents"
|
||||
@@ -63,9 +83,9 @@ if ! $apply; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
[[ -f "$project_dir/run.py" ]] || die 'run.py fehlt im Projektordner'
|
||||
[[ -x "$project_dir/.venv/bin/python" ]] || die '.venv fehlt; zuerst SOFTWARE.md: Pi vorbereiten ausführen'
|
||||
"$project_dir/.venv/bin/python" -c 'import fastapi, uvicorn, PIL' \
|
||||
[[ -f "$code_dir/run.py" ]] || die 'run.py fehlt im Projektordner'
|
||||
[[ -x "$code_dir/.venv/bin/python" ]] || die '.venv fehlt; zuerst SOFTWARE.md: Pi vorbereiten ausführen'
|
||||
"$code_dir/.venv/bin/python" -c 'import fastapi, uvicorn, PIL' \
|
||||
|| die 'Python-Abhängigkeiten fehlen; zuerst requirements.txt installieren'
|
||||
command -v sudo >/dev/null || die 'sudo fehlt'
|
||||
command -v systemctl >/dev/null || die 'systemctl fehlt'
|
||||
|
||||
+23
-7
@@ -2,13 +2,17 @@
|
||||
|
||||
Hier liegen Pi-Transfer/Einrichtung und die reproduzierbaren Blender-Werkzeuge.
|
||||
|
||||
- `upload_to_pi.sh` zeigt ohne `--apply` nur eine Vorschau. Beim Übertragen
|
||||
keine Dateien auf dem Pi löschen; `.git`, `.venv`, `data`, Vorschauen,
|
||||
Referenzen und Schlüsseldateien ausgeschlossen lassen. SSH-Hostschlüssel
|
||||
weiterhin prüfen.
|
||||
- `install_pi_service.sh` richtet den optionalen systemd-Autostart ein.
|
||||
Die Python-Umgebung muss vorher auf dem Pi eingerichtet sein. Der Dienst
|
||||
bindet über `run.py` nur an `127.0.0.1`; `sudo` nur bei `--apply`.
|
||||
- `upload_to_pi.sh` zeigt ohne `--apply` nur eine lokale Vorschau. Jede Version
|
||||
kommt in einen neuen releases/-Ordner; niemals data/ oder bestehende Dateien
|
||||
überschreiben/löschen. SSH-Hostschlüssel weiterhin prüfen.
|
||||
- `activate_pi_release.sh` läuft auf dem Pi: flock-Sperre, eigene venv vorbereiten,
|
||||
verwalteten Dienst stoppen, data/ sichern, current atomar wechseln. Bei Fehlern
|
||||
Code, Unit und Daten wiederherstellen und alten Dienst prüfen. Abhängigkeiten
|
||||
nur als Wheels installieren. Fremde Units und manuelle Server ablehnen.
|
||||
- `install_pi_service.sh` richtet systemd-Autostart ein, beim Upload automatisch.
|
||||
Gemeinsames HALLOWEEN_STATE_DIR; Code/venv über current, wo vorhanden.
|
||||
Default Loopback; direkter Heimnetzzugriff nur mit ausdrücklich gewähltem Host.
|
||||
sudo nur bei --apply; keine Browser-Automation. Alle Versionen/Backups behalten.
|
||||
- Render-, Ton- und Prüfscripte unter `tools/` erzeugen Dateien in
|
||||
`preview/`. Bearbeitbare `.blend`-Szenen werden versioniert, gerenderte
|
||||
Medien und `.blend1`-Backups bleiben lokal.
|
||||
@@ -28,3 +32,15 @@ Schritte; Reihenfolge im zugehörigen Vorschau-README dokumentieren.
|
||||
fixierte NanoDet-Modell unter `data/models/`. Größe und SHA-256 vor atomarem
|
||||
Ersetzen prüfen, TLS-Prüfung beibehalten. `--state-dir` bzw.
|
||||
`HALLOWEEN_STATE_DIR` muss zum Server passen. Keine Modellgewichte hochladen.
|
||||
|
||||
- Versionierte Updates verwenden genau einen sudo-Aufruf nach unprivilegierter
|
||||
Paketvorbereitung. activate_pi_release.sh --privileged ist der interne Teil:
|
||||
Unitprüfung, Sicherung, Umschaltung, systemd und Rückfall im selben sudo-Prozess.
|
||||
Normale Einstiegsshell hält flock über dessen Laufzeit. Nicht direkt aufrufen.
|
||||
- Python-/Projektprüfungen und Unit-Renderer im privilegierten Teil mit runuser
|
||||
als SUDO_USER ausführen. Keine Pakete als root, kein Passwortspeicher/Keepalive
|
||||
und keine sudoers-/Zeitstempeländerung. install_pi_service.sh --print-unit
|
||||
rendert ohne sudo; standalone --apply ist vom Updateaufruf getrennt.
|
||||
- Neu erzeugte Backupordner, Unit-Sicherung, current-Link und service-host
|
||||
dem ursprünglichen Pi-Benutzer zuordnen; bestehende Datenbesitzer beim Kopieren
|
||||
bewahren. Abgelehnte sudo-Anmeldung darf keine Umschaltung/Dienstpause auslösen.
|
||||
+30
-23
@@ -7,22 +7,26 @@ pi_user=
|
||||
pi_host=
|
||||
remote_dir=
|
||||
apply=false
|
||||
install_service=false
|
||||
bind_host=
|
||||
release="$(date -u +%Y%m%dT%H%M%SZ)-$(python3 -c 'import secrets; print(secrets.token_hex(4))')"
|
||||
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
Projektdateien per SSH/rsync auf einen Raspberry Pi kopieren.
|
||||
Versioniertes Pi-Update per SSH/rsync mit Autostart und automatischem Rückfall.
|
||||
|
||||
Aufruf:
|
||||
bash tools/upload_to_pi.sh --user BENUTZER --host HOST \
|
||||
--remote-dir /home/BENUTZER/halloween [--source ORDNER] [--install-service] [--apply]
|
||||
--remote-dir /home/BENUTZER/halloween [--source ORDNER] \
|
||||
[--web-host 127.0.0.1|0.0.0.0] [--apply]
|
||||
|
||||
Ohne --apply nur Vorschau, keine Verbindung. Mit --apply neue Version unter
|
||||
releases/ vorbereiten, Daten sichern, Dienst umschalten und WebUI prüfen.
|
||||
Autostart wird immer eingerichtet (sudo auf dem Pi erforderlich).
|
||||
--web-host wählt SSH-Tunnel oder direkten Heimnetzzugriff; ohne Angabe bleibt
|
||||
die gespeicherte Wahl erhalten, erstmalig 127.0.0.1.
|
||||
--install-service wird als bisheriges Argument weiterhin akzeptiert.
|
||||
Daten, Einstellungen und Modelle unter data/ bleiben erhalten.
|
||||
|
||||
Ohne --apply wird nur der geplante Aufruf angezeigt; es gibt keine Verbindung
|
||||
zum Pi. --apply legt den Zielordner an und kopiert die Dateien. Alte Dateien
|
||||
auf dem Pi werden nicht gelöscht.
|
||||
--install-service richtet nach dem Upload einen systemd-Autostart ein. Dazu
|
||||
muss die Python-Umgebung auf dem Pi bereits vorbereitet sein; sudo wird dort
|
||||
benötigt. Ohne --apply wird auch der Dienst nicht installiert.
|
||||
EOF
|
||||
}
|
||||
|
||||
@@ -33,18 +37,19 @@ die() {
|
||||
|
||||
while (($#)); do
|
||||
case $1 in
|
||||
--user|--host|--remote-dir|--source)
|
||||
--user|--host|--remote-dir|--source|--web-host)
|
||||
(($# >= 2)) || die "Wert für $1 fehlt"
|
||||
case $1 in
|
||||
--user) pi_user=$2 ;;
|
||||
--host) pi_host=$2 ;;
|
||||
--remote-dir) remote_dir=$2 ;;
|
||||
--source) source_dir=$2 ;;
|
||||
--web-host) bind_host=$2 ;;
|
||||
esac
|
||||
shift 2
|
||||
;;
|
||||
--apply) apply=true; shift ;;
|
||||
--install-service) install_service=true; shift ;;
|
||||
--install-service) shift ;; # Compatibility: service installation is now automatic.
|
||||
-h|--help) usage; exit 0 ;;
|
||||
*) die "Unbekanntes Argument: $1" ;;
|
||||
esac
|
||||
@@ -56,15 +61,21 @@ done
|
||||
[[ /$remote_dir/ != *'/../'* && /$remote_dir/ != *'/./'* ]] || die 'Zielpfad darf keine . oder .. Segmente enthalten'
|
||||
[[ -d $source_dir ]] || die "Quellordner fehlt: $source_dir"
|
||||
source_dir=$(cd -- "$source_dir" && pwd -P)
|
||||
if $install_service; then
|
||||
[[ -f "$source_dir/tools/install_pi_service.sh" ]] || die 'tools/install_pi_service.sh fehlt in der Quelle'
|
||||
fi
|
||||
[[ -z $bind_host || $bind_host == 127.0.0.1 || $bind_host == 0.0.0.0 ]] || die 'Ungültiger --web-host'
|
||||
for required in run.py requirements.txt tools/install_pi_service.sh tools/activate_pi_release.sh; do
|
||||
[[ -f "$source_dir/$required" ]] || die "Quelle unvollständig: $required fehlt"
|
||||
done
|
||||
|
||||
command -v ssh >/dev/null || die 'ssh fehlt lokal'
|
||||
command -v rsync >/dev/null || die 'rsync fehlt lokal'
|
||||
|
||||
target="$pi_user@$pi_host"
|
||||
excludes=(
|
||||
--exclude=releases/
|
||||
--exclude=backups/
|
||||
--exclude=current
|
||||
--exclude=service-host
|
||||
--exclude=.update.lock
|
||||
--exclude=.git/
|
||||
--exclude=.venv/
|
||||
--exclude=__pycache__/
|
||||
@@ -77,14 +88,14 @@ excludes=(
|
||||
--exclude='*.p12'
|
||||
--exclude='*.pfx'
|
||||
)
|
||||
ssh_cmd=(ssh -o StrictHostKeyChecking=ask "$target" "mkdir -p -- '$remote_dir'")
|
||||
ssh_cmd=(ssh -o StrictHostKeyChecking=ask "$target" "mkdir -p -- '$remote_dir/releases' && mkdir -- '$remote_dir/releases/$release'")
|
||||
rsync_cmd=(
|
||||
rsync -a --delay-updates --human-readable --info=stats2
|
||||
"${excludes[@]}"
|
||||
-e 'ssh -o StrictHostKeyChecking=ask'
|
||||
-- "$source_dir/" "$target:$remote_dir/"
|
||||
-- "$source_dir/" "$target:$remote_dir/releases/$release/"
|
||||
)
|
||||
service_cmd=(ssh -tt -o StrictHostKeyChecking=ask "$target" "cd -- '$remote_dir' && bash tools/install_pi_service.sh --apply")
|
||||
service_cmd=(ssh -tt -o StrictHostKeyChecking=ask "$target" "bash '$remote_dir/releases/$release/tools/activate_pi_release.sh' '$remote_dir' '$release' '$bind_host'")
|
||||
|
||||
printf 'Quelle: %s\nZiel: %s:%s/\n' "$source_dir" "$target" "$remote_dir"
|
||||
printf 'Ausgelassen: .git, .venv, data, preview, references, __pycache__, .env, Schlüsseldateien\n'
|
||||
@@ -92,9 +103,7 @@ if ! $apply; then
|
||||
printf '\nVorschau, noch keine Verbindung und keine Übertragung:\n'
|
||||
printf ' %q' "${ssh_cmd[@]}"; printf '\n'
|
||||
printf ' %q' "${rsync_cmd[@]}"; printf '\n'
|
||||
if $install_service; then
|
||||
printf ' %q' "${service_cmd[@]}"; printf '\n'
|
||||
fi
|
||||
printf ' %q' "${service_cmd[@]}"; printf '\n'
|
||||
printf '\nMit --apply ausführen.\n'
|
||||
exit 0
|
||||
fi
|
||||
@@ -102,6 +111,4 @@ fi
|
||||
"${ssh_cmd[@]}"
|
||||
"${rsync_cmd[@]}"
|
||||
printf 'Übertragung abgeschlossen.\n'
|
||||
if $install_service; then
|
||||
"${service_cmd[@]}"
|
||||
fi
|
||||
"${service_cmd[@]}"
|
||||
Reference in new issue
Block a user