Интегрирован wesp в сайт
CI / quality (push) Canceled after 0s

This commit is contained in:
влад
2026-07-17 12:57:18 +03:00
parent 5dfa06ddbe
commit 355c0ef9f1
883 changed files with 194576 additions and 177 deletions
+145
View File
@@ -0,0 +1,145 @@
"""Persistent OTA update state in data/update_state.json."""
from __future__ import annotations
import json
import os
from datetime import datetime, timezone
from pathlib import Path
from typing import Any, Dict, Optional
VALID_STATUSES = frozenset(
{"idle", "in_progress", "success", "failed", "rolled_back", "pending_restart"}
)
def _utc_now_iso() -> str:
return datetime.now(timezone.utc).replace(microsecond=0).isoformat().replace("+00:00", "Z")
def update_state_path(base_dir: str) -> str:
return os.path.join(base_dir, "data", "update_state.json")
def read_update_state(base_dir: str) -> Dict[str, Any]:
path = update_state_path(base_dir)
if not os.path.isfile(path):
return {"status": "idle"}
try:
with open(path, "r", encoding="utf-8") as f:
data = json.load(f)
if isinstance(data, dict):
return data
except (OSError, json.JSONDecodeError):
pass
return {"status": "idle"}
def write_update_state(base_dir: str, payload: Dict[str, Any]) -> Dict[str, Any]:
os.makedirs(os.path.join(base_dir, "data"), exist_ok=True)
path = update_state_path(base_dir)
current = read_update_state(base_dir)
if current.get("status") == "idle" and "status" not in payload:
payload = {**payload, "status": "in_progress"}
merged = {**current, **payload}
status = str(merged.get("status", "idle"))
if status not in VALID_STATUSES:
merged["status"] = "failed"
with open(path, "w", encoding="utf-8") as f:
json.dump(merged, f, ensure_ascii=False, indent=2)
f.write("\n")
return merged
def mark_in_progress(
base_dir: str,
*,
target_version: str,
previous_version: str,
backup_path: str,
) -> Dict[str, Any]:
return write_update_state(
base_dir,
{
"status": "in_progress",
"target_version": target_version,
"previous_version": previous_version,
"backup_path": backup_path,
"started_at": _utc_now_iso(),
"stage": "prepare",
"message": "Подготовка к обновлению…",
"detail": None,
"error": None,
},
)
def mark_success(base_dir: str, *, target_version: str) -> Dict[str, Any]:
return write_update_state(
base_dir,
{
"status": "success",
"target_version": target_version,
"finished_at": _utc_now_iso(),
"message": f"Обновление до версии {target_version} завершено",
"error": None,
},
)
def mark_failed(
base_dir: str,
*,
message: str,
stage: Optional[str] = None,
detail: Optional[str] = None,
) -> Dict[str, Any]:
payload: Dict[str, Any] = {
"status": "failed",
"message": message,
"finished_at": _utc_now_iso(),
"error": message,
}
if stage:
payload["stage"] = stage
if detail:
payload["detail"] = detail
return write_update_state(base_dir, payload)
def mark_rolled_back(
base_dir: str,
*,
previous_version: str,
message: str,
) -> Dict[str, Any]:
return write_update_state(
base_dir,
{
"status": "rolled_back",
"previous_version": previous_version,
"message": message,
"finished_at": _utc_now_iso(),
"error": message,
},
)
def mark_pending_restart(base_dir: str) -> Dict[str, Any]:
return write_update_state(
base_dir,
{
"status": "pending_restart",
"stage": "restart",
"message": "Перезапуск системы…",
},
)
def clear_update_state(base_dir: str) -> None:
path = update_state_path(base_dir)
if os.path.isfile(path):
try:
os.remove(path)
except OSError:
pass