оптимизация 2 и добавлены логи
This commit is contained in:
112
update.sh
Normal file
112
update.sh
Normal file
@@ -0,0 +1,112 @@
|
||||
#!/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 и тест доступны на странице.'
|
||||
Reference in New Issue
Block a user