Files
RFB/update.sh

113 lines
5.2 KiB
Bash
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env bash
# Update an installed server from an unpacked release. Keeps .env, team and venv.
set -Eeuo pipefail
source_dir="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
target_dir="${1:-/root/RFB}"
service_name="rfb-data.service"
systemd_root="${RFB_SYSTEMD_ROOT:-/etc/systemd/system}"
dropin="$systemd_root/$service_name.d/20-rfb-runtime.conf"
check_only="${2:-}"
files=(get_data.py rfb_polling.py rfb_runtime.py rfb_logs.html rfb_version.json polling_config.json deploy.sh README_RU.txt update.sh)
python_bin="$target_dir/.venv/bin/python"
if [[ ! -x "$python_bin" ]]; then python_bin="$(command -v python3)"; fi
"$python_bin" - "$source_dir" <<'PY'
from pathlib import Path
import hashlib, json, sys
root = Path(sys.argv[1]); version = json.loads((root/'rfb_version.json').read_text())
for name, expected in version['files'].items():
if hashlib.sha256((root/name).read_bytes()).hexdigest() != expected:
raise SystemExit('Release checksum mismatch: '+name)
for name in ('get_data.py','rfb_polling.py','rfb_runtime.py'):
compile((root/name).read_bytes(),str(root/name),'exec')
print('RFB '+version['version']+': release validated')
PY
if [[ "$check_only" == "--check" ]]; then exit 0; fi
if [[ "$source_dir" == "$(realpath -m "$target_dir")" ]]; then
echo 'Распакуйте архив в отдельную папку, затем запустите update.sh с путём рабочего проекта.' >&2
exit 1
fi
if [[ ! -d "$target_dir" ]]; then echo "Не найден установленный проект: $target_dir" >&2; exit 1; fi
"$python_bin" -c 'import requests, fastapi, uvicorn, pandas, PIL, dotenv, nasio'
backup_dir="${target_dir}-backup-$(date +%Y%m%d-%H%M%S)"
mkdir -p -- "$backup_dir"
for name in "${files[@]}" .rfb_deployment.json; do
if [[ -f "$target_dir/$name" ]]; then cp -a -- "$target_dir/$name" "$backup_dir/$name"; fi
done
if [[ -f "$dropin" ]]; then cp -a -- "$dropin" "$backup_dir/systemd-rfb-runtime.conf"; fi
was_active=0
if systemctl is-active --quiet "$service_name"; then was_active=1; fi
changed=0
rollback() {
local result=$?
trap - ERR
if [[ "$changed" == 1 ]]; then
echo "Обновление не завершилось. Восстанавливаю файлы из $backup_dir" >&2
systemctl stop "$service_name" || true
mkdir -p -- "$backup_dir/failed-update"
for name in "${files[@]}" .rfb_deployment.json; do
if [[ -f "$backup_dir/$name" ]]; then
cp -a -- "$backup_dir/$name" "$target_dir/$name"
elif [[ -f "$target_dir/$name" ]]; then
mv -- "$target_dir/$name" "$backup_dir/failed-update/$name"
fi
done
if [[ -f "$backup_dir/systemd-rfb-runtime.conf" ]]; then
cp -a -- "$backup_dir/systemd-rfb-runtime.conf" "$dropin"
elif [[ -f "$dropin" ]]; then
mv -- "$dropin" "$backup_dir/failed-update/systemd-rfb-runtime.conf"
fi
systemctl daemon-reload || true
if [[ "$was_active" == 1 ]]; then systemctl start "$service_name" || true; fi
fi
exit "$result"
}
trap rollback ERR
systemctl stop "$service_name"
changed=1
for name in "${files[@]}"; do
if [[ "$name" != "polling_config.json" ]]; then cp -- "$source_dir/$name" "$target_dir/$name"; fi
done
"$python_bin" - "$source_dir" "$target_dir" <<'PY'
from pathlib import Path
import json, sys
source, target = map(Path, sys.argv[1:])
defaults = json.loads((source/'polling_config.json').read_text())
path = target/'polling_config.json'
previous = json.loads(path.read_text()) if path.exists() else {}
if not isinstance(previous,dict): raise SystemExit('polling_config.json must contain an object')
# Keep user-specific budgets and intervals. Migrate the old default preparation window.
if previous.get('prepare_minutes') == 70: previous['prepare_minutes'] = 90
defaults.update(previous)
path.write_text(json.dumps(defaults,ensure_ascii=False,indent=2)+'\n',encoding='utf-8')
PY
chmod +x "$target_dir/update.sh" "$target_dir/deploy.sh"
"$python_bin" "$target_dir/rfb_runtime.py" --mark-deployed "$target_dir"
mkdir -p -- "$(dirname -- "$dropin")"
cat > "$dropin" <<'UNIT'
[Unit]
After=network-online.target
Wants=network-online.target
StartLimitIntervalSec=300
StartLimitBurst=5
[Service]
Environment=PYTHONUNBUFFERED=1
UNIT
systemctl daemon-reload
systemctl start "$service_name"
# HTTP readiness does not depend on the upstream API or Telegram being reachable.
"$python_bin" - "$source_dir/rfb_version.json" <<'PY'
import json, sys, time, urllib.request
expected = json.load(open(sys.argv[1]))['version']
last = None
for _ in range(30):
try:
with urllib.request.urlopen('http://127.0.0.1:8000/api_health',timeout=2) as response:
if response.status == 200 and json.load(response).get("version") == expected: break
except Exception as ex: last = ex
time.sleep(1)
else: raise SystemExit('Сервис не ответил после обновления: '+str(last))
PY
trap - ERR
echo "Обновление установлено. Резервная копия: $backup_dir"
echo 'Диагностика: /logs; состояние доставки Telegram и тест доступны на странице.'