diff --git a/README.md b/README.md index 88e5910..bab99c2 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ Aktualisiert komplette Docker-Compose Stacks strukturiert und kontrolliert Zeigt an, was passieren würde, ohne Änderungen durchzuführen - 📲 ntfy Benachrichtigungen Push-Notifications über ntfy bei Updates, Fehlern oder Status -- ⏭️ Exclude-Liste +- ⚙️ Service-Modi (per Label steuerbar) Einzelne Container oder komplette Stacks gezielt vom Update ausschließen - 🗑️ Prune Funktion Entfernt nicht mehr benötigte Images/Container automatisch @@ -65,6 +65,91 @@ Entfernt nicht mehr benötigte Images/Container automatisch --- +## ⚙️ Service-Modi (per Label steuerbar) + +Du kannst das Verhalten einzelner Services oder ganzer Stacks über Labels steuern: + +```yml +labels: + - composeupdater.mode=update +``` + +### 📊 Übersicht + +| Mode | Pull | Compare | Update | ntfy | +| ----------- | ---- | ------- | ------ | ---- | +| update | ✅ | ✅ | ✅ | ✅ | +| notify-only | ✅ | ✅ | ❌ | ✅ | +| ignore | ❌ | ❌ | ❌ | ❌ | + + +### 🧠 Erklärung der Modi + +🔄 `update` (**Standard**) +- Images werden gepullt +- Lokales Image wird mit dem Container verglichen +- Bei Änderungen wird der Service bzw. Stack aktualisiert +- ntfy-Benachrichtigung wird gesendet + + +🔔 `notify-only` +- Image wird gepullt (für Vergleich notwendig) +- Es wird geprüft, ob ein Update verfügbar ist +- Kein Container-Update / kein Restart +- ntfy informiert über verfügbare Updates + + +🚫 `ignore` +- Service wird komplett ignoriert +- Kein Pull +- Kein Vergleich +- Kein Update +- Keine Benachrichtigung + + +### 🧩 Beispiele + +#### Service ausschließen (komplett ignorieren) + +```yml +services: + db: + image: postgres:15 + labels: + - composeupdater.mode=ignore +``` + +#### Nur Benachrichtigung, kein automatisches Update + +```yml +services: + app: + image: myapp:latest + labels: + - composeupdater.mode=notify-only +``` + +#### Explizit Standardverhalten setzen + +```yml +services: + web: + image: nginx:latest + labels: + - composeupdater.mode=update +``` + +#### 📦 Stack-Level Label (optional) + +Du kannst das Verhalten auch für den gesamten Stack setzen: + +```yml +labels: + - composeupdater.mode=ignore +``` + +--- + ## 🗑️ Prune / Cleanup Nach Abschluss der Updates kann optional ein Docker Cleanup durchgeführt werden. @@ -161,23 +246,6 @@ REDEPLOY_WAIT_HEALTHY=true REDEPLOY_WAIT_HEALTHY_TIMEOUT=60 -# ---------------------------------------------------------- -# EXCLUDES -# ---------------------------------------------------------- - -# Stacks vom Update ausschließen [ Array ] -EXCLUDE_STACKS=( - "example_stack_1" - "example_stack_2" -) - -# Services vom Update ausschließen [ Array ] -EXCLUDE_SERVICES=( - "example_container_1" - "example_container_2" -) - - # ---------------------------------------------------------- # NTFY SETTINGS # ---------------------------------------------------------- diff --git a/config.conf b/config.conf index 243c735..f021535 100644 --- a/config.conf +++ b/config.conf @@ -53,23 +53,6 @@ REDEPLOY_WAIT_HEALTHY=true REDEPLOY_WAIT_HEALTHY_TIMEOUT=60 -# ---------------------------------------------------------- -# EXCLUDES -# ---------------------------------------------------------- - -# Stacks vom Update ausschließen [ Array ] -EXCLUDE_STACKS=( - "example_stack_1" - "example_stack_2" -) - -# Services vom Update ausschließen [ Array ] -EXCLUDE_SERVICES=( - "example_container_1" - "example_container_2" -) - - # ---------------------------------------------------------- # NTFY SETTINGS # ---------------------------------------------------------- diff --git a/shell_docker_compose_update.sh b/shell_docker_compose_update.sh index 591aca4..e638c6f 100644 --- a/shell_docker_compose_update.sh +++ b/shell_docker_compose_update.sh @@ -42,6 +42,30 @@ fi # Helper # ============================= + +get_service_mode() { + local svc="$1" + + echo "$compose_json" \ + | jq -r --arg svc "$svc" ' + .services[$svc].labels // [] + | map(select(startswith("composeupdater.mode="))) + | .[0] // "composeupdater.mode=update" + | split("=")[1] + ' +} + + +get_stack_mode() { + echo "$compose_json" \ + | jq -r ' + .labels // [] + | map(select(startswith("composeupdater.mode="))) + | .[0] // "composeupdater.mode=update" + | split("=")[1] + ' +} + is_excluded() { local svc="$1" for ex in "${EXCLUDE_SERVICES[@]}"; do @@ -58,6 +82,21 @@ is_stack_excluded() { return 1 } +is_label_excluded() { + local svc="$1" + + echo "$compose_json" \ + | jq -r --arg svc "$svc" ' + .services[$svc].labels // {} + | (if type=="array" + then map(split("=") | {(.[0]): .[1]}) | add + else . + end) + | .["composeupdater.enable"] // empty + ' \ + | grep -qi "^false$" +} + get_image() { local svc="$1" @@ -241,7 +280,6 @@ stack_tree=() cd "$PATH_COMPOSE_DIR" - while IFS= read -r -d '' file; do declare -A pulled_images @@ -249,11 +287,6 @@ while IFS= read -r -d '' file; do dir=$(dirname "$file") stack=$(basename "$dir") - if is_stack_excluded "$stack"; then - log INFO "→ Stack $stack übersprungen (excluded)" - continue - fi - log INFO "" log INFO "→ Prüfe Stack: $stack" @@ -263,6 +296,14 @@ while IFS= read -r -d '' file; do compose_json=$(docker compose config --format json) + stack_mode=$(get_stack_mode) + + if [ "$stack_mode" = "ignore" ]; then + log INFO "→ Stack $stack übersprungen (label=ignore)" + cd "$PATH_COMPOSE_DIR" + continue + fi + mapfile -t services < <(docker compose config --services) total_services=${#services[@]} current_index=0 @@ -280,7 +321,6 @@ while IFS= read -r -d '' file; do for svc in "${services[@]}"; do update_needed=false - current_index=$((current_index + 1)) if [ "$current_index" -eq "$total_services" ]; then @@ -296,12 +336,47 @@ while IFS= read -r -d '' file; do continue fi - if is_excluded "$svc"; then - pull_with_retry "$image" || true - notify_excluded_updates+=("$stack/$svc") - log INFO " $prefix $svc (excluded)" - continue - fi + mode=$(get_service_mode "$svc") + + case "$mode" in + + ignore) + log INFO " $prefix $svc (ignore)" + continue + ;; + + notify-only) + log INFO " $prefix $svc (notify-only)" + + before_id=$(get_container_image_id "$svc") + + # 👉 ohne Container kein Vergleich sinnvoll + if [ -z "$before_id" ]; then + continue + fi + + if [ -z "${pulled_images[$image]:-}" ]; then + pull_with_retry "$image" || true + pulled_images[$image]=1 + fi + + after_id=$(get_local_image_id "$image") + + if [ "$before_id" != "$after_id" ]; then + notify_excluded_updates+=("$stack/$svc") + fi + + continue + ;; + + update) + # normal weiterlaufen lassen + ;; + + *) + log WARN " $prefix $svc (unbekannter mode: $mode → fallback=update)" + ;; + esac log INFO " $prefix $svc ($image)" @@ -314,7 +389,7 @@ while IFS= read -r -d '' file; do fi # ============================= - # Pull + Vergleich (NEU) + # Pull + Vergleich # =============================