Files
easyai/scripts/update-infrastructure.sh
wangbo 89a4ccd7e7
Test deployment public URL (Linux) / test-linux (push) Canceled after 0s
Test start.ps1 (Windows) / test-windows (push) Canceled after 0s
feat(deploy): 为更新组件增加定时维护与双保险
2026-10-08 09:23:25 +08:00

82 lines
3.2 KiB
Bash
Executable File

#!/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"