#!/bin/bash # nas-docker-pull — Pull Docker images and detect available updates # Usage: nas-docker-pull # 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 # 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 containers_json="" count=0 if $INTERACTIVE; then echo -e "${BOLD}--- Checking Docker images ---${RESET}" fi while IFS=: read -r container_id container_name; do # 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) # Skip containers not managed by compose if [ -z "$compose_dir" ] || [ ! -d "$compose_dir" ]; then continue fi 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="unknown" if $INTERACTIVE; then echo -ne " Checking ${CYAN}${container_name}${RESET}... " fi if ! docker pull "$image_name" > /dev/null 2>&1; then if $INTERACTIVE; then echo -e "${YELLOW}⚠ Pull error (skipped)${RESET}"; fi continue fi new_image_id=$(docker inspect --format='{{.Id}}' "$image_name" 2>/dev/null) 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="available" if $INTERACTIVE; then 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}\"}" if [ $count -eq 0 ]; then containers_json="${entry}" else containers_json="${containers_json},${entry}" fi count=$((count + 1)) else if $INTERACTIVE; then echo -e "${GREEN}✅ Up to date${RESET}"; fi fi done < <(docker ps --format "{{.ID}}:{{.Names}}") # 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}--- Summary ---${RESET}" if [ $count -eq 0 ]; then echo -e "${GREEN}✅ All containers are up to date.${RESET}" else echo -e "${YELLOW}🐳 ${count} container(s) available for update.${RESET}" fi else cat /tmp/nas-docker-pull.json fi