#!/bin/bash set -euo pipefail PROJECT_ROOT="${1:-$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)}" cd "$PROJECT_ROOT" . ./scripts/init-security-env.sh [ "$(read_env_value .env INFRASTRUCTURE_AUTO_UPDATE_ENABLED)" != false ] || exit 0 state="$PROJECT_ROOT/data/infrastructure-update" mkdir -p "$state" lock="$state/lock" if ! mkdir "$lock" 2>/dev/null; then pid="$(cat "$lock/pid" 2>/dev/null || true)" if [[ "$pid" =~ ^[0-9]+$ ]] && ! kill -0 "$pid" 2>/dev/null; then rm -f "$lock/pid" rmdir "$lock" 2>/dev/null || exit 0 mkdir "$lock" 2>/dev/null || exit 0 else exit 0 fi fi printf '%s\n' "$$" > "$lock/pid" trap 'rm -f "$lock/pid"; rmdir "$lock" 2>/dev/null || true' EXIT exec > "$state/last-run.log" 2>&1 printf '%s 检查 Watchtower 和 updater 更新\n' "$(date '+%Y-%m-%d %H:%M:%S')" registry_config="$(read_env_value .env DOCKER_CONFIG_FILE)" docker_options=() if [ -n "$registry_config" ] && [ -f "$registry_config" ]; then docker_options=(--config "$(dirname "$registry_config")") fi if docker "${docker_options[@]}" compose version >/dev/null 2>&1; then compose() { docker "${docker_options[@]}" compose "$@"; } else compose() { if [ -n "$registry_config" ] && [ -f "$registry_config" ]; then DOCKER_CONFIG="$(dirname "$registry_config")" docker-compose "$@" else docker-compose "$@" fi } fi available="$(compose config --services)" printf '%s\n' "$available" | grep -qx watchtower || { echo '未配置 Watchtower'; exit 1; } maintain_service() { local service="$1" container_id status attempt echo "检查 $service" # A failed service must not block the other update channel. compose pull "$service" || return 1 compose up -d --no-deps "$service" || return 1 for ((attempt=0; attempt<12; attempt++)); do container_id="$(compose ps -q "$service")" || return 1 if [ -n "$container_id" ]; then status="$(docker inspect -f '{{.State.Running}} {{if .State.Health}}{{.State.Health.Status}}{{else}}none{{end}}' "$container_id")" || return 1 if [ "$status" = 'true healthy' ]; then echo "$service 已恢复可用" return 0 fi if [ "$status" = 'true none' ]; then if [ "$service" = watchtower ]; then if docker exec "$container_id" /watchtower --health-check; then return 0; fi else if docker exec "$container_id" node -e "require('http').get('http://127.0.0.1:3015/health',r=>{r.resume();process.exit(r.statusCode===200?0:1)}).on('error',()=>process.exit(1))"; then return 0; fi fi fi fi sleep 5 done echo "$service 暂未恢复可用" >&2 return 1 } failed=0 for service in watchtower easyai-updater; do # config --services respects active profiles: legacy deployments stay legacy. if ! printf '%s\n' "$available" | grep -qx "$service"; then echo "$service 未启用,跳过维护" continue fi if ! maintain_service "$service"; then echo "$service 本次维护未成功,下次定时任务将继续检查" >&2 failed=1 fi done exit "$failed"