Add install.sh, HA config files, translate scripts to English, fix apt parsing

This commit is contained in:
GuiPoM
2026-06-01 23:30:00 +02:00
parent a05c54f7b1
commit 12717233af
14 changed files with 530 additions and 371 deletions
+147
View File
@@ -0,0 +1,147 @@
# nas-ops
Scripts de maintenance et de mise à jour pour NAS sous Debian / OpenMediaVault 8.
Gère les mises à jour système (`apt`) et Docker (`docker compose`), avec deux modes :
- **Terminal interactif** : affichage coloré, confirmations
- **Non-interactif** (Home Assistant, cron) : output JSON
## Installation
```bash
bash <(curl -fsSL https://raw.githubusercontent.com/GuiPoM/nas-ops/main/install.sh)
```
Les scripts sont installés dans `/usr/local/bin/` et disponibles directement en ligne de commande.
## Configuration sudo (requis pour Home Assistant)
Lors des appels SSH depuis Home Assistant, sudo demande un mot de passe. Il faut autoriser ces commandes sans mot de passe sur le NAS :
```bash
echo "<user> ALL=(ALL) NOPASSWD: /usr/local/bin/nas-system-update, /usr/local/bin/nas-system-upgrade, /usr/local/bin/nas-docker-pull, /usr/local/bin/nas-docker-up, /usr/local/bin/nas-docker-prune" > /etc/sudoers.d/nas-ops
chmod 440 /etc/sudoers.d/nas-ops
```
Remplace `<user>` par l'utilisateur SSH utilisé dans ta config Home Assistant (celui défini dans `/config/.ssh/config`).
## Scripts
### `nas-update`
Script racine interactif. Orchestre toutes les étapes dans l'ordre :
1. Analyse système (apt)
2. Pull et détection des mises à jour Docker
3. Upgrade système (confirmation)
4. Upgrade Docker (tout ou conteneur par conteneur)
5. Nettoyage des images orphelines
```bash
nas-update
```
---
### `nas-system-update`
Vérifie les mises à jour système disponibles via apt. Ne modifie rien.
- Mode terminal : affichage coloré des paquets upgradables
- Mode non-interactif (HA) : output JSON
```bash
nas-system-update
```
```json
{"count":2,"reboot_required":false,"packages":[{"name":"curl","current":"7.88.0","available":"7.88.1"}]}
```
---
### `nas-system-upgrade`
Applique les mises à jour système (`apt full-upgrade`).
- Mode terminal : affiche le bilan + confirmation avant d'appliquer
- Mode non-interactif (HA) : applique directement
```bash
nas-system-upgrade
```
---
### `nas-docker-pull`
Pull toutes les images Docker des conteneurs actifs et détecte les mises à jour disponibles. **Ne recrée pas les conteneurs.**
Idempotent : tant que `nas-docker-up` n'a pas recréé les conteneurs, le check détecte toujours l'écart.
- Mode terminal : affichage coloré
- Mode non-interactif (HA) : output JSON
```bash
nas-docker-pull
```
```json
{"count":1,"containers":[{"name":"jellyfin","image":"jellyfin/jellyfin:latest","compose_dir":"/opt/stacks/jellyfin","current":"10.9.0","available":"disponible"}]}
```
---
### `nas-docker-up`
Recrée les conteneurs sur la nouvelle image via `docker compose up -d --remove-orphans`.
- Sans argument : propose la mise à jour de tous les conteneurs ayant une image plus récente
- Avec argument : cible une stack spécifique
- Mode terminal : confirmation par stack ou tout d'un coup
- Mode non-interactif (HA) : applique directement
```bash
nas-docker-up # toutes les stacks
nas-docker-up jellyfin # stack spécifique
```
---
### `nas-docker-prune`
Nettoie les images Docker orphelines (dangling). À appeler après `nas-docker-up`.
```bash
nas-docker-prune
```
---
## Intégration Home Assistant
Deux fichiers prêts à l'emploi sont fournis :
**`ha-shell-command.yaml`** — à inclure dans `configuration.yaml` :
```yaml
shell_command: !include ha-shell-command.yaml
```
**`ha-command-line.yaml`** — à inclure dans `configuration.yaml` :
```yaml
command_line: !include ha-command-line.yaml
```
Les sensors `command_line` exposent :
- `sensor.omv_system_updates` → nombre de paquets apt upgradables
- `sensor.omv_docker_updates` → nombre de conteneurs Docker à mettre à jour
- `reboot_required` et `packages` comme attributs du sensor système
- `containers` comme attribut du sensor Docker
## Flux typique depuis HA
```
nas_docker_pull → détecte les mises à jour dispo (idempotent)
nas_docker_up → applique les mises à jour
nas_docker_prune → nettoie les anciennes images
```
+65 -48
View File
@@ -1,30 +1,41 @@
# nas-ops # nas-ops
Scripts de maintenance et de mise à jour pour NAS sous Debian / OpenMediaVault 8. Maintenance and update scripts for NAS running Debian / OpenMediaVault 8.
Gère les mises à jour système (`apt`) et Docker (`docker compose`), avec deux modes : Handles system updates (`apt`) and Docker updates (`docker compose`), with two modes:
- **Terminal interactif** : affichage coloré, confirmations - **Interactive terminal**: colored output, confirmations
- **Non-interactif** (Home Assistant, cron) : output JSON - **Non-interactive** (Home Assistant, cron): JSON output
## Installation ## Installation
```bash ```bash
curl -fsSL https://raw.githubusercontent.com/GuiPoM/nas-ops/main/install.sh | bash bash <(curl -fsSL https://raw.githubusercontent.com/GuiPoM/nas-ops/main/install.sh)
``` ```
Les scripts sont installés dans `/usr/local/bin/` et disponibles directement en ligne de commande. Scripts are installed in `/usr/local/bin/` and available directly from the command line.
## Sudo configuration (required for Home Assistant)
When calling scripts via SSH from Home Assistant, sudo requires passwordless access. Add the following rule on the NAS:
```bash
echo "<user> ALL=(ALL) NOPASSWD: /usr/local/bin/nas-system-update, /usr/local/bin/nas-system-upgrade, /usr/local/bin/nas-docker-pull, /usr/local/bin/nas-docker-up, /usr/local/bin/nas-docker-prune" > /etc/sudoers.d/nas-ops
chmod 440 /etc/sudoers.d/nas-ops
```
Replace `<user>` with the SSH user used in your Home Assistant config (e.g. the user in `/config/.ssh/config`).
## Scripts ## Scripts
### `nas-update` ### `nas-update`
Script racine interactif. Orchestre toutes les étapes dans l'ordre : Interactive root script. Orchestrates all steps in order:
1. Analyse système (apt) 1. System analysis (apt)
2. Pull et détection des mises à jour Docker 2. Docker pull and update detection
3. Upgrade système (confirmation) 3. System upgrade (with confirmation)
4. Upgrade Docker (tout ou conteneur par conteneur) 4. Docker upgrade (all at once or container by container)
5. Nettoyage des images orphelines 5. Orphaned image cleanup
```bash ```bash
nas-update nas-update
@@ -32,15 +43,15 @@ nas-update
--- ---
### `nas-update-system` ### `nas-system-update`
Vérifie les mises à jour système disponibles via apt. Ne modifie rien. Checks available system updates via apt. Does not modify anything.
- Mode terminal : affichage coloré des paquets upgradables - Terminal mode: colored output of upgradable packages
- Mode non-interactif (HA) : output JSON - Non-interactive mode (HA): JSON output
```bash ```bash
nas-update-system nas-system-update
``` ```
```json ```json
@@ -49,57 +60,57 @@ nas-update-system
--- ---
### `nas-upgrade-system` ### `nas-system-upgrade`
Applique les mises à jour système (`apt full-upgrade`). Applies system updates (`apt full-upgrade`).
- Mode terminal : affiche le bilan + confirmation avant d'appliquer - Terminal mode: shows summary + confirmation before applying
- Mode non-interactif (HA) : applique directement - Non-interactive mode (HA): applies directly
```bash ```bash
nas-upgrade-system nas-system-upgrade
``` ```
--- ---
### `nas-docker-pull` ### `nas-docker-pull`
Pull toutes les images Docker des conteneurs actifs et détecte les mises à jour disponibles. **Ne recrée pas les conteneurs.** Pulls all Docker images for active containers and detects available updates. **Does not recreate containers.**
Idempotent : tant que `nas-docker-up` n'a pas recréé les conteneurs, le check tecte toujours l'écart. Idempotent: as long as `nas-docker-up` has not recreated the containers, the check always detects the gap.
- Mode terminal : affichage coloré - Terminal mode: colored output
- Mode non-interactif (HA) : output JSON - Non-interactive mode (HA): JSON output
```bash ```bash
nas-docker-pull nas-docker-pull
``` ```
```json ```json
{"count":1,"containers":[{"name":"jellyfin","image":"jellyfin/jellyfin:latest","compose_dir":"/opt/stacks/jellyfin","current":"10.9.0","available":"disponible"}]} {"count":1,"containers":[{"name":"jellyfin","image":"jellyfin/jellyfin:latest","compose_dir":"/opt/stacks/jellyfin","current":"10.9.0","available":"available"}]}
``` ```
--- ---
### `nas-docker-up` ### `nas-docker-up`
Recrée les conteneurs sur la nouvelle image via `docker compose up -d --remove-orphans`. Recreates containers on the new image via `docker compose up -d --remove-orphans`.
- Sans argument : propose la mise à jour de tous les conteneurs ayant une image plus récente - No argument: offers to update all containers with a newer image
- Avec argument : cible une stack spécifique - With argument: targets a specific stack
- Mode terminal : confirmation par stack ou tout d'un coup - Terminal mode: confirmation per stack or all at once
- Mode non-interactif (HA) : applique directement - Non-interactive mode (HA): applies directly
```bash ```bash
nas-docker-up # toutes les stacks nas-docker-up # all stacks
nas-docker-up jellyfin # stack spécifique nas-docker-up jellyfin # specific stack
``` ```
--- ---
### `nas-docker-prune` ### `nas-docker-prune`
Nettoie les images Docker orphelines (dangling). À appeler après `nas-docker-up`. Removes orphaned (dangling) Docker images. Call after `nas-docker-up`.
```bash ```bash
nas-docker-prune nas-docker-prune
@@ -107,24 +118,30 @@ nas-docker-prune
--- ---
## Intégration Home Assistant ## Home Assistant Integration
Voir `nas-ha-config.yaml` pour la configuration complète. Two ready-to-use files are provided:
**`ha-shell-command.yaml`** — include in `configuration.yaml` as:
```yaml ```yaml
shell_command: shell_command: !include ha-shell-command.yaml
nas_update_system: "ssh -F /config/.ssh/config omv 'sudo nas-update-system'"
nas_upgrade_system: "ssh -F /config/.ssh/config omv 'sudo nas-upgrade-system'"
nas_docker_pull: "ssh -F /config/.ssh/config omv 'sudo nas-docker-pull'"
nas_docker_up: "ssh -F /config/.ssh/config omv 'sudo nas-docker-up'"
nas_docker_up_stack: "ssh -F /config/.ssh/config omv 'sudo nas-docker-up {{ stack }}'"
nas_docker_prune: "ssh -F /config/.ssh/config omv 'sudo nas-docker-prune'"
``` ```
## Flux typique depuis HA **`ha-command-line.yaml`** — include in `configuration.yaml` as:
```yaml
command_line: !include ha-command-line.yaml
```
The `command_line` sensors expose:
- `sensor.omv_system_updates` → number of upgradable apt packages
- `sensor.omv_docker_updates` → number of Docker containers to update
- `reboot_required` and `packages` as attributes on the system sensor
- `containers` as attribute on the Docker sensor
## Typical workflow from HA
``` ```
nas_docker_pull → détecte les mises à jour dispo (idempotent) nas_docker_pull → detect available updates (idempotent)
nas_docker_up → applique les mises à jour nas_docker_up → apply updates
nas_docker_prune → nettoie les anciennes images nas_docker_prune → clean up old images
``` ```
+22
View File
@@ -0,0 +1,22 @@
- sensor:
name: OMV System Updates
unique_id: omv_system_updates
icon: mdi:debian
command: "ssh -F /config/.ssh/config omv 'sudo nas-system-update'"
command_timeout: 60
scan_interval: 3600
value_template: "{{ value_json.count | default(0) }}"
json_attributes:
- packages
- reboot_required
- sensor:
name: OMV Docker Updates
unique_id: omv_docker_updates
icon: mdi:docker
command: "ssh -F /config/.ssh/config omv 'sudo nas-docker-pull'"
command_timeout: 300
scan_interval: 3600
value_template: "{{ value_json.count | default(0) }}"
json_attributes:
- containers
+6
View File
@@ -0,0 +1,6 @@
nas_system_update: "ssh -F /config/.ssh/config omv 'sudo nas-system-update'"
nas_system_upgrade: "ssh -F /config/.ssh/config omv 'sudo nas-system-upgrade'"
nas_docker_pull: "ssh -F /config/.ssh/config omv 'sudo nas-docker-pull'"
nas_docker_up: "ssh -F /config/.ssh/config omv 'sudo nas-docker-up'"
nas_docker_up_stack: "ssh -F /config/.ssh/config omv 'sudo nas-docker-up {{ stack }}'"
nas_docker_prune: "ssh -F /config/.ssh/config omv 'sudo nas-docker-prune'"
+10 -8
View File
@@ -1,17 +1,20 @@
#!/bin/bash #!/bin/bash
# install.sh — Install nas-ops scripts to /usr/local/bin # install.sh — Install nas-ops scripts to /usr/local/bin
# Usage : curl -fsSL https://raw.githubusercontent.com/GuiPoM/nas-ops/main/install.sh | bash # Usage: bash <(curl -fsSL https://raw.githubusercontent.com/GuiPoM/nas-ops/main/install.sh)
set -euo pipefail set -euo pipefail
# Redirect stdin from /dev/tty to avoid issues when piped from curl
exec < /dev/tty
REPO="GuiPoM/nas-ops" REPO="GuiPoM/nas-ops"
BRANCH="main" BRANCH="main"
BASE_URL="https://raw.githubusercontent.com/${REPO}/${BRANCH}" BASE_URL="https://raw.githubusercontent.com/${REPO}/${BRANCH}"
INSTALL_DIR="/usr/local/bin" INSTALL_DIR="/usr/local/bin"
SCRIPTS=( SCRIPTS=(
"nas-update-system" "nas-system-update"
"nas-upgrade-system" "nas-system-upgrade"
"nas-docker-pull" "nas-docker-pull"
"nas-docker-up" "nas-docker-up"
"nas-docker-prune" "nas-docker-prune"
@@ -39,8 +42,8 @@ fi
# Check dependencies # Check dependencies
echo -e "${CYAN}Checking dependencies...${RESET}" echo -e "${CYAN}Checking dependencies...${RESET}"
for cmd in curl docker; do for cmd in curl docker apt-get; do
if command -v "$cmd" > /dev/null 2>&1; then if type "$cmd" > /dev/null 2>&1 || [ -x "/usr/bin/$cmd" ] || [ -x "/bin/$cmd" ]; then
echo -e " ${GREEN}${cmd}${RESET}" echo -e " ${GREEN}${cmd}${RESET}"
else else
echo -e " ${YELLOW}${cmd} not found — some scripts may not work${RESET}" echo -e " ${YELLOW}${cmd} not found — some scripts may not work${RESET}"
@@ -51,12 +54,11 @@ echo ""
# Download and install scripts # Download and install scripts
echo -e "${CYAN}Installing scripts to ${INSTALL_DIR}...${RESET}" echo -e "${CYAN}Installing scripts to ${INSTALL_DIR}...${RESET}"
for script in "${SCRIPTS[@]}"; do for script in "${SCRIPTS[@]}"; do
echo -ne " Downloading ${script}... "
if curl -fsSL "${BASE_URL}/${script}" -o "${INSTALL_DIR}/${script}"; then if curl -fsSL "${BASE_URL}/${script}" -o "${INSTALL_DIR}/${script}"; then
chmod +x "${INSTALL_DIR}/${script}" chmod +x "${INSTALL_DIR}/${script}"
echo -e "${GREEN}${RESET}" echo -e " ${GREEN} ${script}${RESET}"
else else
echo -e "${RED}❌ Failed${RESET}" echo -e " ${RED} ${script} Failed${RESET}"
exit 1 exit 1
fi fi
done done
+5 -5
View File
@@ -1,7 +1,7 @@
#!/bin/bash #!/bin/bash
# nas-docker-prune — Nettoie les images Docker orphelines (dangling) # nas-docker-prune — Remove orphaned (dangling) Docker images
# Usage : nas-docker-prune # Usage: nas-docker-prune
# À appeler après nas-docker-up pour supprimer les anciennes images remplacées # Call after nas-docker-up to clean up replaced images
set -euo pipefail set -euo pipefail
@@ -12,11 +12,11 @@ if $INTERACTIVE; then
GREEN='\033[0;32m' GREEN='\033[0;32m'
BOLD='\033[1m' BOLD='\033[1m'
RESET='\033[0m' RESET='\033[0m'
echo -e "${CYAN}🧹 Nettoyage des images orphelines...${RESET}" echo -e "${CYAN}🧹 Removing orphaned images...${RESET}"
fi fi
docker image prune -f docker image prune -f
if $INTERACTIVE; then if $INTERACTIVE; then
echo -e "${GREEN}✅ Nettoyage terminé.${RESET}" echo -e "${GREEN}✅ Cleanup complete.${RESET}"
fi fi
+20 -19
View File
@@ -1,15 +1,16 @@
#!/bin/bash #!/bin/bash
# nas-docker-pull — Pull les images Docker et détecte les mises à jour disponibles # nas-docker-pull — Pull Docker images and detect available updates
# Usage : nas-docker-pull # Usage: nas-docker-pull
# Output : JSON (mode non-interactif) ou texte coloré (mode terminal) # Output: JSON (non-interactive mode) or colored text (terminal mode)
# Idempotent : tant que nas-docker-up n'a pas recréé les conteneurs, # Idempotent: as long as nas-docker-up has not recreated the containers,
# le conteneur tourne sur l'ancien image ID → l'écart est toujours détecté # the container runs on the old image ID — the gap is always detected
# Also writes JSON to /tmp/nas-docker-pull.json for use by nas-update
set -euo pipefail set -euo pipefail
if [ -t 1 ]; then INTERACTIVE=true; else INTERACTIVE=false; fi if [ -t 1 ]; then INTERACTIVE=true; else INTERACTIVE=false; fi
# Couleurs (mode terminal uniquement) # Colors (terminal mode only)
if $INTERACTIVE; then if $INTERACTIVE; then
RED='\033[0;31m' RED='\033[0;31m'
GREEN='\033[0;32m' GREEN='\033[0;32m'
@@ -25,14 +26,14 @@ containers_json=""
count=0 count=0
if $INTERACTIVE; then if $INTERACTIVE; then
echo -e "${BOLD}--- Vérification des images Docker ---${RESET}" echo -e "${BOLD}--- Checking Docker images ---${RESET}"
fi fi
while IFS=: read -r container_id container_name; do while IFS=: read -r container_id container_name; do
# Récupérer le compose_dir via le label # Get compose_dir from container label
compose_dir=$(docker inspect --format='{{index .Config.Labels "com.docker.compose.project.working_dir"}}' "$container_id" 2>/dev/null | xargs) compose_dir=$(docker inspect --format='{{index .Config.Labels "com.docker.compose.project.working_dir"}}' "$container_id" 2>/dev/null | xargs)
# Ignorer les conteneurs hors compose # Skip containers not managed by compose
if [ -z "$compose_dir" ] || [ ! -d "$compose_dir" ]; then if [ -z "$compose_dir" ] || [ ! -d "$compose_dir" ]; then
continue continue
fi fi
@@ -40,14 +41,14 @@ while IFS=: read -r container_id container_name; do
image_name=$(docker inspect --format='{{.Config.Image}}' "$container_id") image_name=$(docker inspect --format='{{.Config.Image}}' "$container_id")
old_image_id=$(docker inspect --format='{{.Image}}' "$container_id") old_image_id=$(docker inspect --format='{{.Image}}' "$container_id")
old_ver=$(docker inspect --format='{{index .Config.Labels "org.opencontainers.image.version"}}' "$container_id" 2>/dev/null || echo "") old_ver=$(docker inspect --format='{{index .Config.Labels "org.opencontainers.image.version"}}' "$container_id" 2>/dev/null || echo "")
[ -z "$old_ver" ] && old_ver="inconnue" [ -z "$old_ver" ] && old_ver="unknown"
if $INTERACTIVE; then if $INTERACTIVE; then
echo -ne " Vérification de ${CYAN}${container_name}${RESET}... " echo -ne " Checking ${CYAN}${container_name}${RESET}... "
fi fi
if ! docker pull "$image_name" > /dev/null 2>&1; then if ! docker pull "$image_name" > /dev/null 2>&1; then
if $INTERACTIVE; then echo -e "${YELLOW}⚠ Erreur de pull (ignoré)${RESET}"; fi if $INTERACTIVE; then echo -e "${YELLOW}⚠ Pull error (skipped)${RESET}"; fi
continue continue
fi fi
@@ -55,10 +56,10 @@ while IFS=: read -r container_id container_name; do
if [ "$old_image_id" != "$new_image_id" ]; then if [ "$old_image_id" != "$new_image_id" ]; then
new_ver=$(docker inspect --format='{{index .Config.Labels "org.opencontainers.image.version"}}' "$image_name" 2>/dev/null || echo "") new_ver=$(docker inspect --format='{{index .Config.Labels "org.opencontainers.image.version"}}' "$image_name" 2>/dev/null || echo "")
[ -z "$new_ver" ] && new_ver="disponible" [ -z "$new_ver" ] && new_ver="available"
if $INTERACTIVE; then if $INTERACTIVE; then
echo -e "${YELLOW}MAJ DISPONIBLE${RESET} : ${YELLOW}${old_ver}${RESET} → ${GREEN}${new_ver}${RESET}" echo -e "${YELLOW}UPDATE AVAILABLE${RESET}: ${YELLOW}${old_ver}${RESET} → ${GREEN}${new_ver}${RESET}"
fi fi
entry="{\"name\":\"${container_name}\",\"image\":\"${image_name}\",\"compose_dir\":\"${compose_dir}\",\"current\":\"${old_ver}\",\"available\":\"${new_ver}\"}" entry="{\"name\":\"${container_name}\",\"image\":\"${image_name}\",\"compose_dir\":\"${compose_dir}\",\"current\":\"${old_ver}\",\"available\":\"${new_ver}\"}"
@@ -69,21 +70,21 @@ while IFS=: read -r container_id container_name; do
fi fi
count=$((count + 1)) count=$((count + 1))
else else
if $INTERACTIVE; then echo -e "${GREEN}✅ À jour${RESET}"; fi if $INTERACTIVE; then echo -e "${GREEN}✅ Up to date${RESET}"; fi
fi fi
done < <(docker ps --format "{{.ID}}:{{.Names}}") done < <(docker ps --format "{{.ID}}:{{.Names}}")
# Toujours écrire le JSON dans /tmp pour nas-update # Always write JSON to /tmp for use by nas-update
printf '{"count":%d,"containers":[%s]}\n' "$count" "$containers_json" > /tmp/nas-docker-pull.json printf '{"count":%d,"containers":[%s]}\n' "$count" "$containers_json" > /tmp/nas-docker-pull.json
if $INTERACTIVE; then if $INTERACTIVE; then
echo "" echo ""
echo -e "${BOLD}--- Bilan ---${RESET}" echo -e "${BOLD}--- Summary ---${RESET}"
if [ $count -eq 0 ]; then if [ $count -eq 0 ]; then
echo -e "${GREEN}✅ Tous les conteneurs sont à jour.${RESET}" echo -e "${GREEN}✅ All containers are up to date.${RESET}"
else else
echo -e "${YELLOW}🐳 ${count} conteneur(s) à mettre à jour.${RESET}" echo -e "${YELLOW}🐳 ${count} container(s) available for update.${RESET}"
fi fi
else else
cat /tmp/nas-docker-pull.json cat /tmp/nas-docker-pull.json
+38 -40
View File
@@ -1,10 +1,10 @@
#!/bin/bash #!/bin/bash
# nas-docker-up — Applique les mises à jour Docker via docker compose up -d # nas-docker-up — Apply Docker updates via docker compose up -d
# Usage : nas-docker-up [stack_name] # Usage: nas-docker-up [stack_name]
# Sans argument : met à jour tous les conteneurs ayant une image plus récente # No argument: updates all containers with a newer image available
# Avec argument : met à jour uniquement la stack spécifiée # With argument: updates only the specified stack
# Mode HA (non-interactif) : applique directement, pas de prompt # Non-interactive mode (HA): applies directly, no prompt
# Mode terminal : confirmation par stack (sauf si stack spécifiée en argument) # Terminal mode: confirmation per stack (unless a specific stack is given)
set -euo pipefail set -euo pipefail
@@ -12,7 +12,7 @@ if [ -t 1 ]; then INTERACTIVE=true; else INTERACTIVE=false; fi
TARGET_STACK="${1:-}" TARGET_STACK="${1:-}"
# Couleurs (mode terminal uniquement) # Colors (terminal mode only)
if $INTERACTIVE; then if $INTERACTIVE; then
RED='\033[0;31m' RED='\033[0;31m'
GREEN='\033[0;32m' GREEN='\033[0;32m'
@@ -24,7 +24,7 @@ else
RED='' GREEN='' YELLOW='' CYAN='' BOLD='' RESET='' RED='' GREEN='' YELLOW='' CYAN='' BOLD='' RESET=''
fi fi
# --- Détection de l'env global OMV --- # --- Detect global OMV env file ---
OMV_GLOBAL_ENV="" OMV_GLOBAL_ENV=""
if [ -f "/etc/omv-compose.env" ]; then if [ -f "/etc/omv-compose.env" ]; then
OMV_GLOBAL_ENV="/etc/omv-compose.env" OMV_GLOBAL_ENV="/etc/omv-compose.env"
@@ -32,7 +32,7 @@ elif [ -f "/srv/omv-compose.env" ]; then
OMV_GLOBAL_ENV="/srv/omv-compose.env" OMV_GLOBAL_ENV="/srv/omv-compose.env"
fi fi
# --- Collecte des conteneurs à mettre à jour --- # --- Collect containers to update ---
containers_to_update=() containers_to_update=()
images_to_update=() images_to_update=()
compose_dirs=() compose_dirs=()
@@ -41,11 +41,11 @@ old_versions=()
new_versions=() new_versions=()
if $INTERACTIVE; then if $INTERACTIVE; then
echo -e "${BOLD}--- Phase 1 : Recherche des mises à jour ---${RESET}" echo -e "${BOLD}--- Phase 1: Detecting updates ---${RESET}"
fi fi
while IFS=: read -r container_id container_name; do while IFS=: read -r container_id container_name; do
# Filtrer par stack si argument fourni # Filter by stack if argument provided
if [ -n "$TARGET_STACK" ] && [ "$container_name" != "$TARGET_STACK" ]; then if [ -n "$TARGET_STACK" ] && [ "$container_name" != "$TARGET_STACK" ]; then
stack_label=$(docker inspect --format='{{index .Config.Labels "com.docker.compose.project"}}' "$container_id" 2>/dev/null | xargs) stack_label=$(docker inspect --format='{{index .Config.Labels "com.docker.compose.project"}}' "$container_id" 2>/dev/null | xargs)
if [ "$stack_label" != "$TARGET_STACK" ]; then if [ "$stack_label" != "$TARGET_STACK" ]; then
@@ -62,14 +62,14 @@ while IFS=: read -r container_id container_name; do
image_name=$(docker inspect --format='{{.Config.Image}}' "$container_id") image_name=$(docker inspect --format='{{.Config.Image}}' "$container_id")
old_image_id=$(docker inspect --format='{{.Image}}' "$container_id") old_image_id=$(docker inspect --format='{{.Image}}' "$container_id")
old_ver=$(docker inspect --format='{{index .Config.Labels "org.opencontainers.image.version"}}' "$container_id" 2>/dev/null || echo "") old_ver=$(docker inspect --format='{{index .Config.Labels "org.opencontainers.image.version"}}' "$container_id" 2>/dev/null || echo "")
[ -z "$old_ver" ] && old_ver="inconnue" [ -z "$old_ver" ] && old_ver="unknown"
if $INTERACTIVE; then if $INTERACTIVE; then
echo -ne " Vérification de ${CYAN}${container_name}${RESET}... " echo -ne " Checking ${CYAN}${container_name}${RESET}... "
fi fi
if ! docker pull "$image_name" > /dev/null 2>&1; then if ! docker pull "$image_name" > /dev/null 2>&1; then
if $INTERACTIVE; then echo -e "${YELLOW}⚠ Erreur de pull (ignoré)${RESET}"; fi if $INTERACTIVE; then echo -e "${YELLOW}⚠ Pull error (skipped)${RESET}"; fi
continue continue
fi fi
@@ -77,10 +77,10 @@ while IFS=: read -r container_id container_name; do
if [ "$old_image_id" != "$new_image_id" ]; then if [ "$old_image_id" != "$new_image_id" ]; then
new_ver=$(docker inspect --format='{{index .Config.Labels "org.opencontainers.image.version"}}' "$image_name" 2>/dev/null || echo "") new_ver=$(docker inspect --format='{{index .Config.Labels "org.opencontainers.image.version"}}' "$image_name" 2>/dev/null || echo "")
[ -z "$new_ver" ] && new_ver="disponible" [ -z "$new_ver" ] && new_ver="available"
if $INTERACTIVE; then if $INTERACTIVE; then
echo -e "${YELLOW}MAJ DISPONIBLE${RESET} : ${YELLOW}${old_ver}${RESET} → ${GREEN}${new_ver}${RESET}" echo -e "${YELLOW}UPDATE AVAILABLE${RESET}: ${YELLOW}${old_ver}${RESET} → ${GREEN}${new_ver}${RESET}"
fi fi
containers_to_update+=("$container_name") containers_to_update+=("$container_name")
@@ -90,7 +90,7 @@ while IFS=: read -r container_id container_name; do
old_versions+=("$old_ver") old_versions+=("$old_ver")
new_versions+=("$new_ver") new_versions+=("$new_ver")
else else
if $INTERACTIVE; then echo -e "${GREEN}✅ À jour${RESET}"; fi if $INTERACTIVE; then echo -e "${GREEN}✅ Up to date${RESET}"; fi
fi fi
done < <(docker ps --format "{{.ID}}:{{.Names}}") done < <(docker ps --format "{{.ID}}:{{.Names}}")
@@ -98,30 +98,28 @@ done < <(docker ps --format "{{.ID}}:{{.Names}}")
if [ ${#containers_to_update[@]} -eq 0 ]; then if [ ${#containers_to_update[@]} -eq 0 ]; then
if $INTERACTIVE; then if $INTERACTIVE; then
echo "" echo ""
echo -e "${GREEN}✅ Aucun conteneur à mettre à jour.${RESET}" echo -e "${GREEN}✅ No containers to update.${RESET}"
echo -e "${CYAN}🧹 Nettoyage des images orphelines...${RESET}"
fi fi
docker image prune -f > /dev/null
exit 0 exit 0
fi fi
if $INTERACTIVE; then if $INTERACTIVE; then
echo "" echo ""
echo -e "${BOLD}--- Phase 2 : Bilan des mises à jour disponibles ---${RESET}" echo -e "${BOLD}--- Phase 2: Available updates ---${RESET}"
for i in "${!containers_to_update[@]}"; do for i in "${!containers_to_update[@]}"; do
echo -e " • ${CYAN}${containers_to_update[$i]}${RESET} : ${YELLOW}${old_versions[$i]}${RESET} → ${GREEN}${new_versions[$i]}${RESET}" echo -e " • ${CYAN}${containers_to_update[$i]}${RESET}: ${YELLOW}${old_versions[$i]}${RESET} → ${GREEN}${new_versions[$i]}${RESET}"
done done
echo "" echo ""
# Si stack spécifique : pas de question sur le mode, on applique directement # If a specific stack was given, apply directly without asking
if [ -z "$TARGET_STACK" ]; then if [ -z "$TARGET_STACK" ]; then
UPDATE_ALL=false UPDATE_ALL=false
while true; do while true; do
read -p "Mettre à jour TOUS ces conteneurs ? [y]es (Tout) / [n]o (Choisir par conteneur) : " global_choice read -p "Update ALL containers? [y]es (All) / [n]o (Choose per container): " global_choice
case "$global_choice" in case "$global_choice" in
[yY]*) UPDATE_ALL=true; break ;; [yY]*) UPDATE_ALL=true; break ;;
[nN]*) UPDATE_ALL=false; break ;; [nN]*) UPDATE_ALL=false; break ;;
*) echo "Veuillez répondre par y ou n." ;; *) echo "Please answer y or n." ;;
esac esac
done done
else else
@@ -129,10 +127,10 @@ if $INTERACTIVE; then
fi fi
fi fi
# --- Phase 3 : Application --- # --- Phase 3: Apply ---
if $INTERACTIVE; then if $INTERACTIVE; then
echo "" echo ""
echo -e "${BOLD}--- Phase 3 : Application des mises à jour ---${RESET}" echo -e "${BOLD}--- Phase 3: Applying updates ---${RESET}"
fi fi
START_DIR=$(pwd) START_DIR=$(pwd)
@@ -148,34 +146,34 @@ for i in "${!containers_to_update[@]}"; do
DO_UPDATE=false DO_UPDATE=false
if ! $INTERACTIVE; then if ! $INTERACTIVE; then
# Mode HA : toujours appliquer # HA mode: always apply
DO_UPDATE=true DO_UPDATE=true
elif [ "$UPDATE_ALL" = true ]; then elif [ "$UPDATE_ALL" = true ]; then
DO_UPDATE=true DO_UPDATE=true
else else
while true; do while true; do
read -p "Mettre à jour '${c_name}' (${c_old_ver} → ${c_new_ver}) ? [y/n] : " choice read -p " Update '${c_name}' (${c_old_ver} → ${c_new_ver})? [y/n]: " choice
case "$choice" in case "$choice" in
[yY]*) DO_UPDATE=true; break ;; [yY]*) DO_UPDATE=true; break ;;
[nN]*) DO_UPDATE=false; break ;; [nN]*) DO_UPDATE=false; break ;;
*) echo "Veuillez répondre par y ou n." ;; *) echo "Please answer y or n." ;;
esac esac
done done
fi fi
if [ "$DO_UPDATE" = true ]; then if [ "$DO_UPDATE" = true ]; then
if $INTERACTIVE; then if $INTERACTIVE; then
echo -e " 🚀 Mise à jour de ${CYAN}${c_name}${RESET} dans ${c_dir}..." echo -e " 🚀 Updating ${CYAN}${c_name}${RESET} in ${c_dir}..."
fi fi
if cd "$c_dir" 2>/dev/null; then if cd "$c_dir" 2>/dev/null; then
ENV_ARGS="" ENV_ARGS=""
# Env global OMV # Global OMV env
if [ -n "$OMV_GLOBAL_ENV" ] && [ -f "$OMV_GLOBAL_ENV" ]; then if [ -n "$OMV_GLOBAL_ENV" ] && [ -f "$OMV_GLOBAL_ENV" ]; then
ENV_ARGS="--env-file $OMV_GLOBAL_ENV" ENV_ARGS="--env-file $OMV_GLOBAL_ENV"
else else
# Fallback : chercher global.env ou .env dans le dossier parent # Fallback: look for global.env or .env in parent directory
PARENT_DIR=$(dirname "$c_dir") PARENT_DIR=$(dirname "$c_dir")
if [ -f "$PARENT_DIR/global.env" ]; then if [ -f "$PARENT_DIR/global.env" ]; then
ENV_ARGS="--env-file $PARENT_DIR/global.env" ENV_ARGS="--env-file $PARENT_DIR/global.env"
@@ -184,31 +182,31 @@ for i in "${!containers_to_update[@]}"; do
fi fi
fi fi
# .env local (toujours ajouté s'il existe) # Local .env (always added if present)
if [ -f ".env" ]; then if [ -f ".env" ]; then
ENV_ARGS="$ENV_ARGS --env-file .env" ENV_ARGS="$ENV_ARGS --env-file .env"
fi fi
if docker compose $ENV_ARGS up -d --remove-orphans 2>&1; then if docker compose $ENV_ARGS up -d --remove-orphans 2>&1; then
if $INTERACTIVE; then if $INTERACTIVE; then
echo -e " ${GREEN}✅ ${c_name} mis à jour avec succès.${RESET}" echo -e " ${GREEN}✅ ${c_name} updated successfully.${RESET}"
fi fi
upgraded=$((upgraded + 1)) upgraded=$((upgraded + 1))
else else
if $INTERACTIVE; then if $INTERACTIVE; then
echo -e " ${RED}❌ Échec de la mise à jour pour ${c_name}.${RESET}" echo -e " ${RED}❌ Update failed for ${c_name}.${RESET}"
fi fi
failed=$((failed + 1)) failed=$((failed + 1))
fi fi
else else
if $INTERACTIVE; then if $INTERACTIVE; then
echo -e " ${RED}❌ Impossible d'accéder à ${c_dir}.${RESET}" echo -e " ${RED}❌ Cannot access ${c_dir}.${RESET}"
fi fi
failed=$((failed + 1)) failed=$((failed + 1))
fi fi
else else
if $INTERACTIVE; then if $INTERACTIVE; then
echo -e " ⏭ Mise à jour ignorée pour ${c_name}." echo -e " ⏭ Skipped: ${c_name}"
fi fi
fi fi
@@ -217,8 +215,8 @@ done
if $INTERACTIVE; then if $INTERACTIVE; then
echo "" echo ""
echo -e "${BOLD}--- Terminé ---${RESET}" echo -e "${BOLD}--- Done ---${RESET}"
echo -e " ${GREEN}✅ ${upgraded} mis à jour${RESET} ${RED}❌ ${failed} en échec${RESET}" echo -e " ${GREEN}✅ ${upgraded} updated${RESET} ${RED}❌ ${failed} failed${RESET}"
else else
printf '{"upgraded":%d,"failed":%d}\n' "$upgraded" "$failed" printf '{"upgraded":%d,"failed":%d}\n' "$upgraded" "$failed"
fi fi
-51
View File
@@ -1,51 +0,0 @@
# configuration.yaml — Home Assistant shell_command pour le NAS
#
# Ajouter dans configuration.yaml :
#
# shell_command:
# nas_update_system: "ssh -F /config/.ssh/config omv 'sudo nas-update-system'"
# nas_upgrade_system: "ssh -F /config/.ssh/config omv 'sudo nas-upgrade-system'"
# nas_docker_pull: "ssh -F /config/.ssh/config omv 'sudo nas-docker-pull'"
# nas_docker_up: "ssh -F /config/.ssh/config omv 'sudo nas-docker-up'"
# nas_docker_up_stack: "ssh -F /config/.ssh/config omv 'sudo nas-docker-up {{ stack }}'"
#
# Exemples d'utilisation depuis un script HA ou une automation :
#
# service: shell_command.nas_update_system
# service: shell_command.nas_upgrade_system
# service: shell_command.nas_docker_pull
# service: shell_command.nas_docker_up
# service: shell_command.nas_docker_up_stack
# data:
# stack: jellyfin
#
# ─────────────────────────────────────────────────────────────────────────────
# Sensor command_line pour exposer le résultat du check système dans HA
# ─────────────────────────────────────────────────────────────────────────────
#
# command_line:
# - sensor:
# name: NAS System Updates
# unique_id: nas_system_updates
# command: "ssh -F /config/.ssh/config omv 'sudo nas-update-system'"
# scan_interval: 3600
# value_template: "{{ value_json.count }}"
# json_attributes:
# - packages
# - reboot_required
#
# - sensor:
# name: NAS Docker Updates
# unique_id: nas_docker_updates
# command: "ssh -F /config/.ssh/config omv 'sudo nas-docker-pull'"
# scan_interval: 3600
# value_template: "{{ value_json.count }}"
# json_attributes:
# - containers
#
# Ces sensors exposent :
# - sensor.nas_system_updates → nombre de paquets apt upgradables
# - sensor.nas_docker_updates → nombre de conteneurs Docker à mettre à jour
# - sensor.nas_system_updates.reboot_required → true/false
# - sensor.nas_system_updates.packages → liste des paquets
# - sensor.nas_docker_updates.containers → liste des conteneurs
+83
View File
@@ -0,0 +1,83 @@
#!/bin/bash
# nas-system-update — Check available system updates (apt)
# Usage: nas-system-update
# Output: JSON (non-interactive mode) or colored text (terminal mode)
set -euo pipefail
if [ -t 1 ]; then INTERACTIVE=true; else INTERACTIVE=false; fi
# Colors (terminal mode only)
if $INTERACTIVE; then
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
CYAN='\033[0;36m'
BOLD='\033[1m'
RESET='\033[0m'
else
RED='' GREEN='' YELLOW='' CYAN='' BOLD='' RESET=''
fi
if $INTERACTIVE; then
echo -e "${BOLD}--- Refreshing package list ---${RESET}"
fi
apt-get update -qq 2>/dev/null
# Parse apt-get --simulate full-upgrade
# Format:
# Inst name (available_version ...) → new package
# Inst name [current_version] (available_version ...) → upgrade
packages_json=""
count=0
while IFS= read -r line; do
# Extract package name (2nd field)
name=$(echo "$line" | awk '{print $2}' | tr -d '[:space:]')
[ -z "$name" ] && continue
# Current version: inside [...] if present, else N/A
current=$(echo "$line" | grep -oP '(?<=\[)[^\]]+' | tr -d '[:space:]' || echo "")
[ -z "$current" ] && current="N/A"
# Available version: first word inside first (...)
available=$(echo "$line" | grep -oP '(?<=\()[^ ]+' | head -1 | tr -d '[:space:]')
[ -z "$available" ] && continue
if $INTERACTIVE; then
echo -e " ${CYAN}${name}${RESET} : ${YELLOW}${current}${RESET} → ${GREEN}${available}${RESET}"
fi
# Build JSON entry
entry="{\"name\":\"${name}\",\"current\":\"${current}\",\"available\":\"${available}\"}"
if [ $count -eq 0 ]; then
packages_json="${entry}"
else
packages_json="${packages_json},${entry}"
fi
count=$((count + 1))
done < <(apt-get --simulate full-upgrade 2>/dev/null | grep "^Inst")
# Check if reboot is required
reboot_required=false
if [ -f /var/run/reboot-required ]; then
reboot_required=true
fi
if $INTERACTIVE; then
echo ""
echo -e "${BOLD}--- Summary ---${RESET}"
if [ $count -eq 0 ]; then
echo -e "${GREEN}✅ System is up to date.${RESET}"
else
echo -e "${YELLOW}📦 ${count} package(s) available for upgrade.${RESET}"
fi
if $reboot_required; then
echo -e "${RED}⚠️ Reboot required.${RESET}"
fi
else
printf '{"count":%d,"reboot_required":%s,"packages":[%s]}\n' \
"$count" "$reboot_required" "$packages_json"
fi
+95
View File
@@ -0,0 +1,95 @@
#!/bin/bash
# nas-system-upgrade — Apply system updates (apt full-upgrade)
# Usage: nas-system-upgrade
# Non-interactive mode (HA): applies directly, JSON output
# Terminal mode: shows summary + confirmation before applying
set -euo pipefail
if [ -t 1 ]; then INTERACTIVE=true; else INTERACTIVE=false; fi
# Colors (terminal mode only)
if $INTERACTIVE; then
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
CYAN='\033[0;36m'
BOLD='\033[1m'
RESET='\033[0m'
else
RED='' GREEN='' YELLOW='' CYAN='' BOLD='' RESET=''
fi
# Ensure package list is up to date
if $INTERACTIVE; then
echo -e "${BOLD}--- Refreshing package list ---${RESET}"
fi
apt-get update -qq 2>/dev/null
# Get packages to install/upgrade via simulation
# Format:
# Inst name (available_version ...) → new package
# Inst name [current_version] (available_version ...) → upgrade
packages=()
while IFS= read -r line; do
name=$(echo "$line" | awk '{print $2}')
[ -z "$name" ] && continue
current=$(echo "$line" | grep -oP '(?<=\[)[^\]]+' || echo "")
[ -z "$current" ] && current="N/A"
available=$(echo "$line" | grep -oP '(?<=\()[^ ]+' | head -1)
[ -z "$available" ] && continue
packages+=("${name}|${current}|${available}")
done < <(apt-get --simulate full-upgrade 2>/dev/null | grep "^Inst")
count=${#packages[@]}
if [ "$count" -eq 0 ]; then
if $INTERACTIVE; then
echo -e "${GREEN}✅ System is already up to date, nothing to do.${RESET}"
else
printf '{"status":"already_up_to_date","upgraded":0,"reboot_required":false}\n'
fi
exit 0
fi
if $INTERACTIVE; then
echo ""
echo -e "${BOLD}--- Packages to upgrade (${count}) ---${RESET}"
for entry in "${packages[@]}"; do
name=$(echo "$entry" | cut -d'|' -f1)
current=$(echo "$entry" | cut -d'|' -f2)
available=$(echo "$entry" | cut -d'|' -f3)
echo -e " ${CYAN}${name}${RESET} : ${YELLOW}${current}${RESET} → ${GREEN}${available}${RESET}"
done
echo ""
read -p "Apply upgrade for these ${count} package(s)? [y/n]: " confirm
case "$confirm" in
[yY]*) ;;
*) echo -e "${YELLOW}Cancelled.${RESET}"; exit 0 ;;
esac
echo ""
echo -e "${BOLD}--- Running apt full-upgrade ---${RESET}"
fi
# Apply
DEBIAN_FRONTEND=noninteractive apt-get full-upgrade -y \
-o Dpkg::Options::="--force-confdef" \
-o Dpkg::Options::="--force-confold" 2>&1
# Check if reboot is required
reboot_required=false
if [ -f /var/run/reboot-required ]; then
reboot_required=true
fi
if $INTERACTIVE; then
echo ""
echo -e "${GREEN}✅ Upgrade complete.${RESET}"
if $reboot_required; then
echo -e "${RED}⚠️ Reboot required.${RESET}"
fi
else
printf '{"status":"upgraded","upgraded":%d,"reboot_required":%s}\n' \
"$count" "$reboot_required"
fi
+39 -39
View File
@@ -1,11 +1,11 @@
#!/bin/bash #!/bin/bash
# nas-update — Script racine de gestion des mises à jour NAS # nas-update — NAS update manager (interactive root script)
# Orchestre nas-update-system, nas-upgrade-system, nas-docker-pull, nas-docker-up, nas-docker-prune # Orchestrates nas-system-update, nas-system-upgrade, nas-docker-pull, nas-docker-up, nas-docker-prune
# Usage : nas-update # Usage: nas-update
set -euo pipefail set -euo pipefail
# Couleurs # Colors
RED='\033[0;31m' RED='\033[0;31m'
GREEN='\033[0;32m' GREEN='\033[0;32m'
YELLOW='\033[1;33m' YELLOW='\033[1;33m'
@@ -14,25 +14,25 @@ BOLD='\033[1m'
RESET='\033[0m' RESET='\033[0m'
echo -e "${BOLD}=====================================================${RESET}" echo -e "${BOLD}=====================================================${RESET}"
echo -e "${BOLD} NAS — Gestionnaire de mises à jour ${RESET}" echo -e "${BOLD} NAS — Update Manager ${RESET}"
echo -e "${BOLD}=====================================================${RESET}" echo -e "${BOLD}=====================================================${RESET}"
echo "" echo ""
# ───────────────────────────────────────────────────── # ─────────────────────────────────────────────────────
# Phase 1 : Analyse système # Phase 1: System analysis
# ───────────────────────────────────────────────────── # ─────────────────────────────────────────────────────
echo -e "${BOLD}--- Phase 1 : Analyse système (apt) ---${RESET}" echo -e "${BOLD}--- Phase 1: System analysis (apt) ---${RESET}"
nas-update-system nas-system-update
echo "" echo ""
# ───────────────────────────────────────────────────── # ─────────────────────────────────────────────────────
# Phase 2 : Analyse Docker (pull + détection diffs) # Phase 2: Docker analysis (pull + diff detection)
# ───────────────────────────────────────────────────── # ─────────────────────────────────────────────────────
echo -e "${BOLD}--- Phase 2 : Analyse Docker ---${RESET}" echo -e "${BOLD}--- Phase 2: Docker analysis ---${RESET}"
nas-docker-pull nas-docker-pull
# Lire le JSON écrit par nas-docker-pull # Read JSON written by nas-docker-pull
docker_json=$(cat /tmp/nas-docker-pull.json 2>/dev/null || echo '{"count":0,"containers":[]}') docker_json=$(cat /tmp/nas-docker-pull.json 2>/dev/null || echo '{"count":0,"containers":[]}')
containers_to_update=() containers_to_update=()
@@ -55,68 +55,68 @@ done < <(echo "$docker_json" | grep -o '{[^}]*}')
echo "" echo ""
# ───────────────────────────────────────────────────── # ─────────────────────────────────────────────────────
# Phase 3 : Upgrade système # Phase 3: System upgrade
# ───────────────────────────────────────────────────── # ─────────────────────────────────────────────────────
echo -e "${BOLD}--- Phase 3 : Mise à jour système ---${RESET}" echo -e "${BOLD}--- Phase 3: System upgrade ---${RESET}"
while true; do while true; do
read -p "Appliquer apt full-upgrade ? [y/n] : " sys_choice read -p "Apply apt full-upgrade? [y/n]: " sys_choice
case "$sys_choice" in case "$sys_choice" in
[yY]*) [yY]*)
nas-upgrade-system nas-system-upgrade
break break
;; ;;
[nN]*) [nN]*)
echo -e "${YELLOW}⏭ Mise à jour système ignorée.${RESET}" echo -e "${YELLOW}⏭ System upgrade skipped.${RESET}"
break break
;; ;;
*) echo "Veuillez répondre par y ou n." ;; *) echo "Please answer y or n." ;;
esac esac
done done
echo "" echo ""
# ───────────────────────────────────────────────────── # ─────────────────────────────────────────────────────
# Phase 4 : Upgrade Docker # Phase 4: Docker upgrade
# ───────────────────────────────────────────────────── # ─────────────────────────────────────────────────────
echo -e "${BOLD}--- Phase 4 : Mise à jour Docker ---${RESET}" echo -e "${BOLD}--- Phase 4: Docker upgrade ---${RESET}"
if [ ${#containers_to_update[@]} -eq 0 ]; then if [ ${#containers_to_update[@]} -eq 0 ]; then
echo -e "${GREEN}✅ Aucun conteneur à mettre à jour.${RESET}" echo -e "${GREEN}✅ No containers to update.${RESET}"
echo "" echo ""
echo -e "${BOLD}=====================================================${RESET}" echo -e "${BOLD}=====================================================${RESET}"
echo -e "${GREEN} Terminé ! ${RESET}" echo -e "${GREEN} Done! ${RESET}"
echo -e "${BOLD}=====================================================${RESET}" echo -e "${BOLD}=====================================================${RESET}"
exit 0 exit 0
fi fi
echo "" echo ""
echo -e "${BOLD}Bilan des conteneurs à mettre à jour :${RESET}" echo -e "${BOLD}Containers available for update:${RESET}"
for i in "${!containers_to_update[@]}"; do for i in "${!containers_to_update[@]}"; do
echo -e " • ${CYAN}${containers_to_update[$i]}${RESET} : ${YELLOW}${old_versions[$i]}${RESET} → ${GREEN}${new_versions[$i]}${RESET}" echo -e " • ${CYAN}${containers_to_update[$i]}${RESET}: ${YELLOW}${old_versions[$i]}${RESET} → ${GREEN}${new_versions[$i]}${RESET}"
done done
echo "" echo ""
UPDATE_ALL=false UPDATE_ALL=false
while true; do while true; do
read -p "Mettre à jour TOUS ces conteneurs ? [y]es (Tout) / [n]o (Choisir par conteneur) / [s]kip (Ignorer tout) : " docker_choice read -p "Update ALL containers? [y]es (All) / [n]o (Choose per container) / [s]kip (Skip all): " docker_choice
case "$docker_choice" in case "$docker_choice" in
[yY]*) UPDATE_ALL=true; break ;; [yY]*) UPDATE_ALL=true; break ;;
[nN]*) UPDATE_ALL=false; break ;; [nN]*) UPDATE_ALL=false; break ;;
[sS]*) echo -e "${YELLOW}⏭ Mise à jour Docker ignorée.${RESET}"; break ;; [sS]*) echo -e "${YELLOW}⏭ Docker upgrade skipped.${RESET}"; break ;;
*) echo "Veuillez répondre par y, n ou s." ;; *) echo "Please answer y, n or s." ;;
esac esac
done done
if [[ "$docker_choice" =~ ^[sS] ]]; then if [[ "$docker_choice" =~ ^[sS] ]]; then
echo "" echo ""
echo -e "${BOLD}=====================================================${RESET}" echo -e "${BOLD}=====================================================${RESET}"
echo -e "${GREEN} Terminé ! ${RESET}" echo -e "${GREEN} Done! ${RESET}"
echo -e "${BOLD}=====================================================${RESET}" echo -e "${BOLD}=====================================================${RESET}"
exit 0 exit 0
fi fi
# Détection de l'env global OMV # Detect global OMV env file
OMV_GLOBAL_ENV="" OMV_GLOBAL_ENV=""
if [ -f "/etc/omv-compose.env" ]; then if [ -f "/etc/omv-compose.env" ]; then
OMV_GLOBAL_ENV="/etc/omv-compose.env" OMV_GLOBAL_ENV="/etc/omv-compose.env"
@@ -140,17 +140,17 @@ for i in "${!containers_to_update[@]}"; do
DO_UPDATE=true DO_UPDATE=true
else else
while true; do while true; do
read -p " Mettre à jour '${c_name}' (${c_old_ver} → ${c_new_ver}) ? [y/n] : " choice read -p " Update '${c_name}' (${c_old_ver} → ${c_new_ver})? [y/n]: " choice
case "$choice" in case "$choice" in
[yY]*) DO_UPDATE=true; break ;; [yY]*) DO_UPDATE=true; break ;;
[nN]*) DO_UPDATE=false; break ;; [nN]*) DO_UPDATE=false; break ;;
*) echo "Veuillez répondre par y ou n." ;; *) echo "Please answer y or n." ;;
esac esac
done done
fi fi
if [ "$DO_UPDATE" = true ]; then if [ "$DO_UPDATE" = true ]; then
echo -e " 🚀 Mise à jour de ${CYAN}${c_name}${RESET}..." echo -e " 🚀 Updating ${CYAN}${c_name}${RESET}..."
if cd "$c_dir" 2>/dev/null; then if cd "$c_dir" 2>/dev/null; then
ENV_ARGS="" ENV_ARGS=""
@@ -171,32 +171,32 @@ for i in "${!containers_to_update[@]}"; do
fi fi
if docker compose $ENV_ARGS up -d --remove-orphans 2>&1; then if docker compose $ENV_ARGS up -d --remove-orphans 2>&1; then
echo -e " ${GREEN}✅ ${c_name} mis à jour avec succès.${RESET}" echo -e " ${GREEN}✅ ${c_name} updated successfully.${RESET}"
upgraded=$((upgraded + 1)) upgraded=$((upgraded + 1))
else else
echo -e " ${RED}❌ Échec pour ${c_name}.${RESET}" echo -e " ${RED}❌ Update failed for ${c_name}.${RESET}"
failed=$((failed + 1)) failed=$((failed + 1))
fi fi
else else
echo -e " ${RED}❌ Impossible d'accéder à ${c_dir}.${RESET}" echo -e " ${RED}❌ Cannot access ${c_dir}.${RESET}"
failed=$((failed + 1)) failed=$((failed + 1))
fi fi
else else
echo -e " ⏭ Ignoré : ${c_name}" echo -e " ⏭ Skipped: ${c_name}"
fi fi
cd "$START_DIR" cd "$START_DIR"
done done
# ───────────────────────────────────────────────────── # ─────────────────────────────────────────────────────
# Phase 5 : Nettoyage des images orphelines # Phase 5: Cleanup
# ───────────────────────────────────────────────────── # ─────────────────────────────────────────────────────
echo "" echo ""
echo -e "${BOLD}--- Phase 5 : Nettoyage ---${RESET}" echo -e "${BOLD}--- Phase 5: Cleanup ---${RESET}"
nas-docker-prune nas-docker-prune
echo "" echo ""
echo -e "${BOLD}=====================================================${RESET}" echo -e "${BOLD}=====================================================${RESET}"
echo -e "${BOLD} Terminé ! ${RESET}" echo -e "${BOLD} Done! ${RESET}"
echo -e " Docker : ${GREEN}✅ ${upgraded} mis à jour${RESET} ${RED}❌ ${failed} en échec${RESET}" echo -e " Docker: ${GREEN}✅ ${upgraded} updated${RESET} ${RED}❌ ${failed} failed${RESET}"
echo -e "${BOLD}=====================================================${RESET}" echo -e "${BOLD}=====================================================${RESET}"
-80
View File
@@ -1,80 +0,0 @@
#!/bin/bash
# nas-update-system — Vérifie les mises à jour système disponibles (apt)
# Usage : nas-update-system
# Output : JSON (mode non-interactif) ou texte coloré (mode terminal)
set -euo pipefail
if [ -t 1 ]; then INTERACTIVE=true; else INTERACTIVE=false; fi
# Couleurs (mode terminal uniquement)
if $INTERACTIVE; then
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
CYAN='\033[0;36m'
BOLD='\033[1m'
RESET='\033[0m'
else
RED='' GREEN='' YELLOW='' CYAN='' BOLD='' RESET=''
fi
if $INTERACTIVE; then
echo -e "${BOLD}--- Mise à jour de la liste des paquets ---${RESET}"
fi
apt-get update -qq 2>/dev/null
# Parser apt list --upgradable
# Format : nom/suite version arch [upgradable from: ancienne_version]
packages_json=""
count=0
while IFS= read -r line; do
# Ignorer la ligne d'avertissement "Listing..."
[[ "$line" =~ ^Listing ]] && continue
[[ -z "$line" ]] && continue
# Extraire le nom (avant le /)
name=$(echo "$line" | cut -d'/' -f1)
# Extraire la version disponible (2ème champ)
available=$(echo "$line" | awk '{print $2}')
# Extraire la version actuelle (après "upgradable from:")
current=$(echo "$line" | grep -oP 'upgradable from: \K[^\]]+' || echo "inconnue")
if $INTERACTIVE; then
echo -e " ${CYAN}${name}${RESET} : ${YELLOW}${current}${RESET} → ${GREEN}${available}${RESET}"
fi
# Construire JSON
entry="{\"name\":\"${name}\",\"current\":\"${current}\",\"available\":\"${available}\"}"
if [ $count -eq 0 ]; then
packages_json="${entry}"
else
packages_json="${packages_json},${entry}"
fi
count=$((count + 1))
done < <(apt list --upgradable 2>/dev/null)
# Vérifier si reboot requis
reboot_required=false
if [ -f /var/run/reboot-required ]; then
reboot_required=true
fi
if $INTERACTIVE; then
echo ""
echo -e "${BOLD}--- Bilan ---${RESET}"
if [ $count -eq 0 ]; then
echo -e "${GREEN}✅ Système à jour.${RESET}"
else
echo -e "${YELLOW}📦 ${count} paquet(s) à mettre à jour.${RESET}"
fi
if $reboot_required; then
echo -e "${RED}⚠️ Redémarrage requis.${RESET}"
fi
else
printf '{"count":%d,"reboot_required":%s,"packages":[%s]}\n' \
"$count" "$reboot_required" "$packages_json"
fi
-81
View File
@@ -1,81 +0,0 @@
#!/bin/bash
# nas-upgrade-system — Applique les mises à jour système (apt full-upgrade)
# Usage : nas-upgrade-system
# Mode HA (non-interactif) : applique directement, output JSON
# Mode terminal : affiche le bilan + confirmation avant d'appliquer
set -euo pipefail
if [ -t 1 ]; then INTERACTIVE=true; else INTERACTIVE=false; fi
# Couleurs (mode terminal uniquement)
if $INTERACTIVE; then
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
CYAN='\033[0;36m'
BOLD='\033[1m'
RESET='\033[0m'
else
RED='' GREEN='' YELLOW='' CYAN='' BOLD='' RESET=''
fi
# S'assurer que la liste est à jour
if $INTERACTIVE; then
echo -e "${BOLD}--- Mise à jour de la liste des paquets ---${RESET}"
fi
apt-get update -qq 2>/dev/null
# Récupérer les paquets upgradables
upgradable=$(apt list --upgradable 2>/dev/null | grep -v '^Listing' | grep -v '^$')
count=$(echo "$upgradable" | grep -c . || true)
if [ "$count" -eq 0 ]; then
if $INTERACTIVE; then
echo -e "${GREEN}✅ Système déjà à jour, rien à faire.${RESET}"
else
printf '{"status":"already_up_to_date","upgraded":0,"reboot_required":false}\n'
fi
exit 0
fi
if $INTERACTIVE; then
echo ""
echo -e "${BOLD}--- Paquets à mettre à jour (${count}) ---${RESET}"
while IFS= read -r line; do
[[ -z "$line" ]] && continue
name=$(echo "$line" | cut -d'/' -f1)
available=$(echo "$line" | awk '{print $2}')
current=$(echo "$line" | grep -oP 'upgradable from: \K[^\]]+' || echo "inconnue")
echo -e " ${CYAN}${name}${RESET} : ${YELLOW}${current}${RESET} → ${GREEN}${available}${RESET}"
done <<< "$upgradable"
echo ""
read -p "Appliquer la mise à jour de ces ${count} paquet(s) ? [y/n] : " confirm
case "$confirm" in
[yY]*) ;;
*) echo -e "${YELLOW}Annulé.${RESET}"; exit 0 ;;
esac
echo ""
echo -e "${BOLD}--- Application de apt full-upgrade ---${RESET}"
fi
# Appliquer
DEBIAN_FRONTEND=noninteractive apt-get full-upgrade -y -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" 2>&1
# Vérifier reboot requis
reboot_required=false
if [ -f /var/run/reboot-required ]; then
reboot_required=true
fi
if $INTERACTIVE; then
echo ""
echo -e "${GREEN}✅ Mise à jour terminée.${RESET}"
if $reboot_required; then
echo -e "${RED}⚠️ Redémarrage requis.${RESET}"
fi
else
printf '{"status":"upgraded","upgraded":%d,"reboot_required":%s}\n' \
"$count" "$reboot_required"
fi