diff --git a/.gitignore b/.gitignore index 5de824d..0388b17 100644 --- a/.gitignore +++ b/.gitignore @@ -14,6 +14,9 @@ data/ .env .env.local +# per-operator federation host list (SSH targets are sensitive) +scripts/hosts + # editors .vscode/ .idea/ diff --git a/scripts/deploy-all.sh b/scripts/deploy-all.sh new file mode 100755 index 0000000..d614bad --- /dev/null +++ b/scripts/deploy-all.sh @@ -0,0 +1,65 @@ +#!/usr/bin/env bash +# Deploy the current main commit to every federation host listed in +# scripts/hosts (one node per line: LABEL SSH_TARGET REMOTE_PATH PUBLIC_URL). +# Loops scripts/deploy.sh against each. Bails on first failure unless --keep-going. +set -euo pipefail + +HOSTS_FILE="${PSYC_HOSTS_FILE:-$(dirname "$0")/hosts}" +KEEP_GOING=0 +for arg in "$@"; do + case "$arg" in + --keep-going) KEEP_GOING=1 ;; + -h|--help) + echo "usage: $0 [--keep-going]" + echo " reads $HOSTS_FILE (override with PSYC_HOSTS_FILE=...)" + exit 0 + ;; + esac +done + +if [[ ! -f "$HOSTS_FILE" ]]; then + echo "no hosts file at $HOSTS_FILE — copy scripts/hosts.example to scripts/hosts and edit" >&2 + exit 2 +fi + +declare -a OK=() FAIL=() +while IFS= read -r line; do + # skip blanks + comments + [[ -z "${line// /}" || "${line# }" == \#* ]] && continue + # shellcheck disable=SC2206 + parts=($line) + if [[ ${#parts[@]} -lt 4 ]]; then + echo "[deploy-all] skipping malformed line: $line" >&2 + continue + fi + LABEL="${parts[0]}" + SSH_TARGET="${parts[1]}" + REMOTE_PATH="${parts[2]}" + PUBLIC_URL="${parts[3]}" + + echo + echo "════════════════════════════════════════════════════════════════" + echo " deploying → $LABEL ($SSH_TARGET:$REMOTE_PATH → $PUBLIC_URL)" + echo "════════════════════════════════════════════════════════════════" + + if PSYC_PROD_HOST="$SSH_TARGET" \ + PSYC_PROD_PATH="$REMOTE_PATH" \ + PSYC_PROD_URL="$PUBLIC_URL" \ + bash "$(dirname "$0")/deploy.sh"; then + OK+=("$LABEL") + else + FAIL+=("$LABEL") + if [[ $KEEP_GOING -ne 1 ]]; then + echo "[deploy-all] $LABEL failed — stopping. pass --keep-going to continue past failures." >&2 + break + fi + fi +done < "$HOSTS_FILE" + +echo +echo "════════════════════════════════════════════════════════════════" +echo " summary" +echo "════════════════════════════════════════════════════════════════" +echo " ok: ${OK[*]:-(none)}" +echo " failed: ${FAIL[*]:-(none)}" +[[ ${#FAIL[@]} -eq 0 ]] diff --git a/scripts/hosts.example b/scripts/hosts.example new file mode 100644 index 0000000..985e79c --- /dev/null +++ b/scripts/hosts.example @@ -0,0 +1,6 @@ +# Federation hosts — one node per line. +# Format: LABEL SSH_TARGET REMOTE_PATH PUBLIC_URL +# Lines starting with # are ignored. Copy to scripts/hosts and edit. +prod neuronetz@cloud.neuronetz.ai /home/neuronetz/docker-public/neuro-psyc https://psyc.neuronetz.ai +sto user@sto-host.example /path/to/neuro-psyc https://psyc.maschinen-stockert.de +bittomine user@bittomine-host.example /path/to/neuro-psyc https://psyc.bittomine.com