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
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 :
- **Terminal interactif** : affichage coloré, confirmations
- **Non-interactif** (Home Assistant, cron) : output JSON
Handles system updates (`apt`) and Docker updates (`docker compose`), with two modes:
- **Interactive terminal**: colored output, confirmations
- **Non-interactive** (Home Assistant, cron): JSON output
## Installation
```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
### `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)
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
1. System analysis (apt)
2. Docker pull and update detection
3. System upgrade (with confirmation)
4. Docker upgrade (all at once or container by container)
5. Orphaned image cleanup
```bash
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
- Mode non-interactif (HA) : output JSON
- Terminal mode: colored output of upgradable packages
- Non-interactive mode (HA): JSON output
```bash
nas-update-system
nas-system-update
```
```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
- Mode non-interactif (HA) : applique directement
- Terminal mode: shows summary + confirmation before applying
- Non-interactive mode (HA): applies directly
```bash
nas-upgrade-system
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.**
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é
- Mode non-interactif (HA) : output JSON
- Terminal mode: colored output
- Non-interactive mode (HA): JSON output
```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"}]}
{"count":1,"containers":[{"name":"jellyfin","image":"jellyfin/jellyfin:latest","compose_dir":"/opt/stacks/jellyfin","current":"10.9.0","available":"available"}]}
```
---
### `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
- Avec argument : cible une stack spécifique
- Mode terminal : confirmation par stack ou tout d'un coup
- Mode non-interactif (HA) : applique directement
- No argument: offers to update all containers with a newer image
- With argument: targets a specific stack
- Terminal mode: confirmation per stack or all at once
- Non-interactive mode (HA): applies directly
```bash
nas-docker-up # toutes les stacks
nas-docker-up jellyfin # stack spécifique
nas-docker-up # all stacks
nas-docker-up jellyfin # specific stack
```
---
### `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
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
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 }}'"
nas_docker_prune: "ssh -F /config/.ssh/config omv 'sudo nas-docker-prune'"
shell_command: !include ha-shell-command.yaml
```
## 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_up → applique les mises à jour
nas_docker_prune → nettoie les anciennes images
nas_docker_pull → detect available updates (idempotent)
nas_docker_up → apply updates
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
# 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
# Redirect stdin from /dev/tty to avoid issues when piped from curl
exec < /dev/tty
REPO="GuiPoM/nas-ops"
BRANCH="main"
BASE_URL="https://raw.githubusercontent.com/${REPO}/${BRANCH}"
INSTALL_DIR="/usr/local/bin"
SCRIPTS=(
"nas-update-system"
"nas-upgrade-system"
"nas-system-update"
"nas-system-upgrade"
"nas-docker-pull"
"nas-docker-up"
"nas-docker-prune"
@@ -39,8 +42,8 @@ fi
# Check dependencies
echo -e "${CYAN}Checking dependencies...${RESET}"
for cmd in curl docker; do
if command -v "$cmd" > /dev/null 2>&1; then
for cmd in curl docker apt-get; do
if type "$cmd" > /dev/null 2>&1 || [ -x "/usr/bin/$cmd" ] || [ -x "/bin/$cmd" ]; then
echo -e " ${GREEN}${cmd}${RESET}"
else
echo -e " ${YELLOW}${cmd} not found — some scripts may not work${RESET}"
@@ -51,12 +54,11 @@ echo ""
# Download and install scripts
echo -e "${CYAN}Installing scripts to ${INSTALL_DIR}...${RESET}"
for script in "${SCRIPTS[@]}"; do
echo -ne " Downloading ${script}... "
if curl -fsSL "${BASE_URL}/${script}" -o "${INSTALL_DIR}/${script}"; then
chmod +x "${INSTALL_DIR}/${script}"
echo -e "${GREEN}${RESET}"
echo -e " ${GREEN} ${script}${RESET}"
else
echo -e "${RED}❌ Failed${RESET}"
echo -e " ${RED} ${script} Failed${RESET}"
exit 1
fi
done
+4 -4
View File
@@ -1,7 +1,7 @@
#!/bin/bash
# nas-docker-prune — Nettoie les images Docker orphelines (dangling)
# nas-docker-prune — Remove orphaned (dangling) Docker images
# 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
@@ -12,11 +12,11 @@ if $INTERACTIVE; then
GREEN='\033[0;32m'
BOLD='\033[1m'
RESET='\033[0m'
echo -e "${CYAN}🧹 Nettoyage des images orphelines...${RESET}"
echo -e "${CYAN}🧹 Removing orphaned images...${RESET}"
fi
docker image prune -f
if $INTERACTIVE; then
echo -e "${GREEN}✅ Nettoyage terminé.${RESET}"
echo -e "${GREEN}✅ Cleanup complete.${RESET}"
fi
+19 -18
View File
@@ -1,15 +1,16 @@
#!/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
# Output : JSON (mode non-interactif) ou texte coloré (mode terminal)
# Idempotent : tant que nas-docker-up n'a pas recréé les conteneurs,
# le conteneur tourne sur l'ancien image ID → l'écart est toujours détecté
# Output: JSON (non-interactive mode) or colored text (terminal mode)
# Idempotent: as long as nas-docker-up has not recreated the containers,
# 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
if [ -t 1 ]; then INTERACTIVE=true; else INTERACTIVE=false; fi
# Couleurs (mode terminal uniquement)
# Colors (terminal mode only)
if $INTERACTIVE; then
RED='\033[0;31m'
GREEN='\033[0;32m'
@@ -25,14 +26,14 @@ containers_json=""
count=0
if $INTERACTIVE; then
echo -e "${BOLD}--- Vérification des images Docker ---${RESET}"
echo -e "${BOLD}--- Checking Docker images ---${RESET}"
fi
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)
# Ignorer les conteneurs hors compose
# Skip containers not managed by compose
if [ -z "$compose_dir" ] || [ ! -d "$compose_dir" ]; then
continue
fi
@@ -40,14 +41,14 @@ while IFS=: read -r container_id container_name; do
image_name=$(docker inspect --format='{{.Config.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 "")
[ -z "$old_ver" ] && old_ver="inconnue"
[ -z "$old_ver" ] && old_ver="unknown"
if $INTERACTIVE; then
echo -ne " Vérification de ${CYAN}${container_name}${RESET}... "
echo -ne " Checking ${CYAN}${container_name}${RESET}... "
fi
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
fi
@@ -55,10 +56,10 @@ while IFS=: read -r container_id container_name; do
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 "")
[ -z "$new_ver" ] && new_ver="disponible"
[ -z "$new_ver" ] && new_ver="available"
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
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
count=$((count + 1))
else
if $INTERACTIVE; then echo -e "${GREEN}✅ À jour${RESET}"; fi
if $INTERACTIVE; then echo -e "${GREEN}✅ Up to date${RESET}"; fi
fi
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
if $INTERACTIVE; then
echo ""
echo -e "${BOLD}--- Bilan ---${RESET}"
echo -e "${BOLD}--- Summary ---${RESET}"
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
echo -e "${YELLOW}🐳 ${count} conteneur(s) à mettre à jour.${RESET}"
echo -e "${YELLOW}🐳 ${count} container(s) available for update.${RESET}"
fi
else
cat /tmp/nas-docker-pull.json
+36 -38
View File
@@ -1,10 +1,10 @@
#!/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]
# Sans argument : met à jour tous les conteneurs ayant une image plus récente
# Avec argument : met à jour uniquement la stack spécifiée
# Mode HA (non-interactif) : applique directement, pas de prompt
# Mode terminal : confirmation par stack (sauf si stack spécifiée en argument)
# No argument: updates all containers with a newer image available
# With argument: updates only the specified stack
# Non-interactive mode (HA): applies directly, no prompt
# Terminal mode: confirmation per stack (unless a specific stack is given)
set -euo pipefail
@@ -12,7 +12,7 @@ if [ -t 1 ]; then INTERACTIVE=true; else INTERACTIVE=false; fi
TARGET_STACK="${1:-}"
# Couleurs (mode terminal uniquement)
# Colors (terminal mode only)
if $INTERACTIVE; then
RED='\033[0;31m'
GREEN='\033[0;32m'
@@ -24,7 +24,7 @@ else
RED='' GREEN='' YELLOW='' CYAN='' BOLD='' RESET=''
fi
# --- Détection de l'env global OMV ---
# --- Detect global OMV env file ---
OMV_GLOBAL_ENV=""
if [ -f "/etc/omv-compose.env" ]; then
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"
fi
# --- Collecte des conteneurs à mettre à jour ---
# --- Collect containers to update ---
containers_to_update=()
images_to_update=()
compose_dirs=()
@@ -41,11 +41,11 @@ old_versions=()
new_versions=()
if $INTERACTIVE; then
echo -e "${BOLD}--- Phase 1 : Recherche des mises à jour ---${RESET}"
echo -e "${BOLD}--- Phase 1: Detecting updates ---${RESET}"
fi
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
stack_label=$(docker inspect --format='{{index .Config.Labels "com.docker.compose.project"}}' "$container_id" 2>/dev/null | xargs)
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")
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 "")
[ -z "$old_ver" ] && old_ver="inconnue"
[ -z "$old_ver" ] && old_ver="unknown"
if $INTERACTIVE; then
echo -ne " Vérification de ${CYAN}${container_name}${RESET}... "
echo -ne " Checking ${CYAN}${container_name}${RESET}... "
fi
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
fi
@@ -77,10 +77,10 @@ while IFS=: read -r container_id container_name; do
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 "")
[ -z "$new_ver" ] && new_ver="disponible"
[ -z "$new_ver" ] && new_ver="available"
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
containers_to_update+=("$container_name")
@@ -90,7 +90,7 @@ while IFS=: read -r container_id container_name; do
old_versions+=("$old_ver")
new_versions+=("$new_ver")
else
if $INTERACTIVE; then echo -e "${GREEN}✅ À jour${RESET}"; fi
if $INTERACTIVE; then echo -e "${GREEN}✅ Up to date${RESET}"; fi
fi
done < <(docker ps --format "{{.ID}}:{{.Names}}")
@@ -98,30 +98,28 @@ done < <(docker ps --format "{{.ID}}:{{.Names}}")
if [ ${#containers_to_update[@]} -eq 0 ]; then
if $INTERACTIVE; then
echo ""
echo -e "${GREEN}✅ Aucun conteneur à mettre à jour.${RESET}"
echo -e "${CYAN}🧹 Nettoyage des images orphelines...${RESET}"
echo -e "${GREEN}✅ No containers to update.${RESET}"
fi
docker image prune -f > /dev/null
exit 0
fi
if $INTERACTIVE; then
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
echo -e " • ${CYAN}${containers_to_update[$i]}${RESET}: ${YELLOW}${old_versions[$i]}${RESET} → ${GREEN}${new_versions[$i]}${RESET}"
done
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
UPDATE_ALL=false
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
[yY]*) UPDATE_ALL=true; break ;;
[nN]*) UPDATE_ALL=false; break ;;
*) echo "Veuillez répondre par y ou n." ;;
*) echo "Please answer y or n." ;;
esac
done
else
@@ -129,10 +127,10 @@ if $INTERACTIVE; then
fi
fi
# --- Phase 3 : Application ---
# --- Phase 3: Apply ---
if $INTERACTIVE; then
echo ""
echo -e "${BOLD}--- Phase 3 : Application des mises à jour ---${RESET}"
echo -e "${BOLD}--- Phase 3: Applying updates ---${RESET}"
fi
START_DIR=$(pwd)
@@ -148,34 +146,34 @@ for i in "${!containers_to_update[@]}"; do
DO_UPDATE=false
if ! $INTERACTIVE; then
# Mode HA : toujours appliquer
# HA mode: always apply
DO_UPDATE=true
elif [ "$UPDATE_ALL" = true ]; then
DO_UPDATE=true
else
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
[yY]*) DO_UPDATE=true; break ;;
[nN]*) DO_UPDATE=false; break ;;
*) echo "Veuillez répondre par y ou n." ;;
*) echo "Please answer y or n." ;;
esac
done
fi
if [ "$DO_UPDATE" = true ]; 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
if cd "$c_dir" 2>/dev/null; then
ENV_ARGS=""
# Env global OMV
# Global OMV env
if [ -n "$OMV_GLOBAL_ENV" ] && [ -f "$OMV_GLOBAL_ENV" ]; then
ENV_ARGS="--env-file $OMV_GLOBAL_ENV"
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")
if [ -f "$PARENT_DIR/global.env" ]; then
ENV_ARGS="--env-file $PARENT_DIR/global.env"
@@ -184,31 +182,31 @@ for i in "${!containers_to_update[@]}"; do
fi
fi
# .env local (toujours ajouté s'il existe)
# Local .env (always added if present)
if [ -f ".env" ]; then
ENV_ARGS="$ENV_ARGS --env-file .env"
fi
if docker compose $ENV_ARGS up -d --remove-orphans 2>&1; then
if $INTERACTIVE; then
echo -e " ${GREEN}✅ ${c_name} mis à jour avec succès.${RESET}"
echo -e " ${GREEN}✅ ${c_name} updated successfully.${RESET}"
fi
upgraded=$((upgraded + 1))
else
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
failed=$((failed + 1))
fi
else
if $INTERACTIVE; then
echo -e " ${RED}❌ Impossible d'accéder à ${c_dir}.${RESET}"
echo -e " ${RED}❌ Cannot access ${c_dir}.${RESET}"
fi
failed=$((failed + 1))
fi
else
if $INTERACTIVE; then
echo -e " ⏭ Mise à jour ignorée pour ${c_name}."
echo -e " ⏭ Skipped: ${c_name}"
fi
fi
@@ -217,8 +215,8 @@ done
if $INTERACTIVE; then
echo ""
echo -e "${BOLD}--- Terminé ---${RESET}"
echo -e " ${GREEN}✅ ${upgraded} mis à jour${RESET} ${RED}❌ ${failed} en échec${RESET}"
echo -e "${BOLD}--- Done ---${RESET}"
echo -e " ${GREEN}✅ ${upgraded} updated${RESET} ${RED}❌ ${failed} failed${RESET}"
else
printf '{"upgraded":%d,"failed":%d}\n' "$upgraded" "$failed"
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
+37 -37
View File
@@ -1,11 +1,11 @@
#!/bin/bash
# nas-update — Script racine de gestion des mises à jour NAS
# Orchestre nas-update-system, nas-upgrade-system, nas-docker-pull, nas-docker-up, nas-docker-prune
# nas-update — NAS update manager (interactive root script)
# Orchestrates nas-system-update, nas-system-upgrade, nas-docker-pull, nas-docker-up, nas-docker-prune
# Usage: nas-update
set -euo pipefail
# Couleurs
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
@@ -14,25 +14,25 @@ BOLD='\033[1m'
RESET='\033[0m'
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 ""
# ─────────────────────────────────────────────────────
# Phase 1 : Analyse système
# Phase 1: System analysis
# ─────────────────────────────────────────────────────
echo -e "${BOLD}--- Phase 1 : Analyse système (apt) ---${RESET}"
nas-update-system
echo -e "${BOLD}--- Phase 1: System analysis (apt) ---${RESET}"
nas-system-update
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
# 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":[]}')
containers_to_update=()
@@ -55,43 +55,43 @@ done < <(echo "$docker_json" | grep -o '{[^}]*}')
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
read -p "Appliquer apt full-upgrade ? [y/n] : " sys_choice
read -p "Apply apt full-upgrade? [y/n]: " sys_choice
case "$sys_choice" in
[yY]*)
nas-upgrade-system
nas-system-upgrade
break
;;
[nN]*)
echo -e "${YELLOW}⏭ Mise à jour système ignorée.${RESET}"
echo -e "${YELLOW}⏭ System upgrade skipped.${RESET}"
break
;;
*) echo "Veuillez répondre par y ou n." ;;
*) echo "Please answer y or n." ;;
esac
done
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
echo -e "${GREEN}✅ Aucun conteneur à mettre à jour.${RESET}"
echo -e "${GREEN}✅ No containers to update.${RESET}"
echo ""
echo -e "${BOLD}=====================================================${RESET}"
echo -e "${GREEN} Terminé ! ${RESET}"
echo -e "${GREEN} Done! ${RESET}"
echo -e "${BOLD}=====================================================${RESET}"
exit 0
fi
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
echo -e " • ${CYAN}${containers_to_update[$i]}${RESET}: ${YELLOW}${old_versions[$i]}${RESET} → ${GREEN}${new_versions[$i]}${RESET}"
done
@@ -99,24 +99,24 @@ echo ""
UPDATE_ALL=false
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
[yY]*) UPDATE_ALL=true; break ;;
[nN]*) UPDATE_ALL=false; break ;;
[sS]*) echo -e "${YELLOW}⏭ Mise à jour Docker ignorée.${RESET}"; break ;;
*) echo "Veuillez répondre par y, n ou s." ;;
[sS]*) echo -e "${YELLOW}⏭ Docker upgrade skipped.${RESET}"; break ;;
*) echo "Please answer y, n or s." ;;
esac
done
if [[ "$docker_choice" =~ ^[sS] ]]; then
echo ""
echo -e "${BOLD}=====================================================${RESET}"
echo -e "${GREEN} Terminé ! ${RESET}"
echo -e "${GREEN} Done! ${RESET}"
echo -e "${BOLD}=====================================================${RESET}"
exit 0
fi
# Détection de l'env global OMV
# Detect global OMV env file
OMV_GLOBAL_ENV=""
if [ -f "/etc/omv-compose.env" ]; then
OMV_GLOBAL_ENV="/etc/omv-compose.env"
@@ -140,17 +140,17 @@ for i in "${!containers_to_update[@]}"; do
DO_UPDATE=true
else
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
[yY]*) DO_UPDATE=true; break ;;
[nN]*) DO_UPDATE=false; break ;;
*) echo "Veuillez répondre par y ou n." ;;
*) echo "Please answer y or n." ;;
esac
done
fi
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
ENV_ARGS=""
@@ -171,32 +171,32 @@ for i in "${!containers_to_update[@]}"; do
fi
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))
else
echo -e " ${RED}❌ Échec pour ${c_name}.${RESET}"
echo -e " ${RED}❌ Update failed for ${c_name}.${RESET}"
failed=$((failed + 1))
fi
else
echo -e " ${RED}❌ Impossible d'accéder à ${c_dir}.${RESET}"
echo -e " ${RED}❌ Cannot access ${c_dir}.${RESET}"
failed=$((failed + 1))
fi
else
echo -e " ⏭ Ignoré : ${c_name}"
echo -e " ⏭ Skipped: ${c_name}"
fi
cd "$START_DIR"
done
# ─────────────────────────────────────────────────────
# Phase 5 : Nettoyage des images orphelines
# Phase 5: Cleanup
# ─────────────────────────────────────────────────────
echo ""
echo -e "${BOLD}--- Phase 5 : Nettoyage ---${RESET}"
echo -e "${BOLD}--- Phase 5: Cleanup ---${RESET}"
nas-docker-prune
echo ""
echo -e "${BOLD}=====================================================${RESET}"
echo -e "${BOLD} Terminé ! ${RESET}"
echo -e " Docker : ${GREEN}✅ ${upgraded} mis à jour${RESET} ${RED}❌ ${failed} en échec${RESET}"
echo -e "${BOLD} Done! ${RESET}"
echo -e " Docker: ${GREEN}✅ ${upgraded} updated${RESET} ${RED}❌ ${failed} failed${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