#!/usr/bin/env bash # ============================================================================= # streambox — installation / mise à jour en une ligne # # Rapide (image déjà présente sur la machine, ex. après un `docker load`) : # curl -fsSL https://git.les2ter.fr/gitea/hamriou/streambox-install/raw/branch/main/install.sh | sudo bash # # Depuis une archive : # curl -fsSL .../install.sh | sudo bash -s -- --tar /tmp/streambox-aio-v49.tar --tag v49 # # Depuis une autre machine du LAN (docker save | docker load, sans registry) : # curl -fsSL .../install.sh | sudo bash -s -- --from-ssh lab --tag v49 # # Aucun registry, aucun token, aucun secret. Le compose vient de ce dépôt public. # ============================================================================= set -euo pipefail BASE_URL="${BASE_URL:-https://git.les2ter.fr/gitea/hamriou/streambox-install/raw/branch/main}" INSTALL_DIR="${INSTALL_DIR:-$HOME/aio}" IMAGE_TAG="${IMAGE_TAG:-latest}" IMAGE_REPO="${IMAGE_REPO:-streambox-aio}" UI_PORT="${UI_PORT:-3005}" SRC_TAR="" SRC_REF="" SRC_SSH="" DRY=0 WAIT="${WAIT:-90}" log() { printf '\033[1;34m==>\033[0m %s\n' "$*"; } ok() { printf '\033[1;32m ok\033[0m %s\n' "$*"; } warn() { printf '\033[1;33m !!\033[0m %s\n' "$*" >&2; } die() { printf '\033[1;31mERREUR:\033[0m %s\n' "$*" >&2; exit 1; } usage() { cat <<'EOF' streambox — installeur --dir DIR répertoire d'installation (défaut: ~/aio) --tag TAG tag de l'image (défaut: latest) --image REPO:TAG tirer l'image depuis un registry --tar FICHIER charger l'image depuis une archive (docker load), chemin ou URL --from-ssh HOST charger l'image depuis HOST (ssh HOST 'docker save ...' | docker load) --wait SECONDES attente max du health check (défaut: 90) --dry-run afficher les actions sans rien modifier -h, --help cette aide Sans --image / --tar / --from-ssh, l'installeur utilise l'image locale streambox-aio: si elle existe déjà (cas d'une mise à jour). EOF } while [ $# -gt 0 ]; do case "$1" in --dir) INSTALL_DIR="${2:?}"; shift 2 ;; --tag) IMAGE_TAG="${2:?}"; shift 2 ;; --image) SRC_REF="${2:?}"; shift 2 ;; --tar) SRC_TAR="${2:?}"; shift 2 ;; --from-ssh) SRC_SSH="${2:?}"; shift 2 ;; --wait) WAIT="${2:?}"; shift 2 ;; --dry-run) DRY=1; shift ;; -h|--help) usage; exit 0 ;; *) die "argument inconnu: $1 (voir --help)" ;; esac done FULL_IMAGE="$IMAGE_REPO:$IMAGE_TAG" COMPOSE="$INSTALL_DIR/docker-compose.yml" # ----------------------------------------------------------------- 1. prérequis log "Prérequis" command -v docker >/dev/null || die "docker absent" command -v curl >/dev/null || die "curl absent" if [ "$DRY" = 0 ]; then docker info >/dev/null 2>&1 || die "docker injoignable (droits ? lance avec sudo)" docker compose version >/dev/null 2>&1 || die "docker compose v2 absent" fi ok "docker + compose v2" # --------------------------------------------------------- 2. répertoire + compose log "Répertoire $INSTALL_DIR" if [ "$DRY" = 0 ]; then mkdir -p "$INSTALL_DIR"/{config,data,log} tmp="$(mktemp)" curl -fsSL "$BASE_URL/docker-compose.yml" -o "$tmp" || die "compose introuvable sur $BASE_URL" grep -q 'streambox-aio' "$tmp" || die "compose téléchargé invalide" if [ -f "$COMPOSE" ] && ! cmp -s "$tmp" "$COMPOSE"; then cp "$COMPOSE" "$COMPOSE.bak.$(date +%Y%m%d%H%M%S)" warn "compose précédent sauvegardé (.bak)" fi mv "$tmp" "$COMPOSE" ok "compose à jour" else printf ' [dry-run] mkdir -p %s/{config,data,log}\n' "$INSTALL_DIR" printf ' [dry-run] curl %s/docker-compose.yml -> %s\n' "$BASE_URL" "$COMPOSE" fi # ------------------------------------------------------------------- 3. l'image log "Image $FULL_IMAGE" have_local() { [ "$DRY" = 1 ] && return 1 docker image inspect "$FULL_IMAGE" >/dev/null 2>&1 } if [ -n "$SRC_TAR" ]; then tar_path="$SRC_TAR" if printf '%s' "$SRC_TAR" | grep -qE '^https?://'; then tar_path="/tmp/$(basename "${SRC_TAR%%\?*}")" log "Téléchargement de l'archive" [ "$DRY" = 1 ] && printf ' [dry-run] curl -fL %s -o %s\n' "$SRC_TAR" "$tar_path" \ || curl -fL --retry 2 "$SRC_TAR" -o "$tar_path" fi [ "$DRY" = 1 ] && printf ' [dry-run] docker load -i %s\n' "$tar_path" \ || { docker load -i "$tar_path" || die "docker load a échoué"; } ok "image chargée depuis $tar_path" elif [ -n "$SRC_SSH" ]; then log "Transfert depuis $SRC_SSH (docker save | docker load)" if [ "$DRY" = 1 ]; then printf ' [dry-run] ssh %s "sudo docker save %s" | docker load\n' "$SRC_SSH" "$FULL_IMAGE" else ssh "$SRC_SSH" "sudo docker save $FULL_IMAGE" | docker load \ || die "transfert depuis $SRC_SSH échoué (image absente là-bas, ou clé SSH ?)" ok "image transférée" fi elif [ -n "$SRC_REF" ]; then [ "$DRY" = 1 ] && printf ' [dry-run] docker pull %s\n' "$SRC_REF" \ || { docker pull "$SRC_REF" || die "pull de $SRC_REF échoué"; } SRC_REF_ID="$(docker image inspect -f '{{.Id}}' "$SRC_REF" 2>/dev/null || true)" if [ "$DRY" = 0 ] && [ -n "$SRC_REF_ID" ]; then docker tag "$SRC_REF" "$FULL_IMAGE" fi ok "image tirée" elif have_local; then ok "image locale déjà présente" elif [ "$DRY" = 1 ]; then printf ' [dry-run] (image locale %s supposée présente)\n' "$FULL_IMAGE" else die "aucune image $FULL_IMAGE en local. Utilise --tar, --from-ssh, ou --image REPO:TAG" fi # ----------------------------------------------------------------- 4. démarrage log "Démarrage" if [ "$DRY" = 1 ]; then printf ' [dry-run] cd %s && IMAGE_TAG=%s docker compose up -d\n' "$INSTALL_DIR" "$IMAGE_TAG" else ( cd "$INSTALL_DIR" && IMAGE_TAG="$IMAGE_TAG" docker compose up -d ) || die "docker compose up a échoué" fi # --------------------------------------------------------------- 5. health check log "Health check (max ${WAIT}s)" if [ "$DRY" = 1 ]; then printf ' [dry-run] curl http://127.0.0.1:%s/ jusqu à 200\n' "$UI_PORT" exit 0 fi state="" for _ in $(seq 1 "$WAIT"); do state="$(docker inspect -f '{{.State.Status}}' streambox-aio 2>/dev/null || echo absente)" if [ "$state" = "running" ] && curl -fsS -o /dev/null -m 2 "http://127.0.0.1:$UI_PORT/" 2>/dev/null; then echo ok "conteneur running, HTTP 200 sur le port $UI_PORT" printf '\n État : %s\n' "$state" printf ' Image : %s\n' "$FULL_IMAGE" printf ' Dossier: %s\n' "$INSTALL_DIR" printf ' UI : http://%s:%s\n' "$(hostname -I 2>/dev/null | awk '{print $1}')" "$UI_PORT" printf ' Logs : cd %s && docker compose logs -f\n' "$INSTALL_DIR" printf ' Rollback: IMAGE_TAG= docker compose up -d\n' exit 0 fi sleep 1 done echo warn "conteneur '$state' mais pas de 200 sur :$UI_PORT après ${WAIT}s" docker inspect -f '{{.State.Status}} {{if .State.Health}}{{.State.Health.Status}}{{end}}' streambox-aio 2>/dev/null || true die "démarrage incomplet — voir: cd $INSTALL_DIR && docker compose logs --tail 50"