zapret-kvn/zapret_update/handoff.py
loop-uh ced48cb34c
Some checks failed
Windows project source guards / test (push) Failing after 25s
Установщик обновления — на общем ядре zapret_update, том же, что у Zapret 2 GUI
Замена файлов, порядок шагов, проверка запуска и откат теперь в общем
ядре (байт-в-байт копия). Своё у Zapret KVN — только прослойка runner:
план прежнего вида (его пишет ещё прежняя версия), журнал update.log,
хвост startup.log упавшего запуска и сообщение об ошибке. Журнал замены
теперь пишется на диск: замену, оборванную выключением, можно откатить.
Замок окна обновления — общая переменная ZAPRET_UPDATE_HOLD.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 01:54:26 +03:00

75 lines
3.1 KiB
Python
Raw Permalink 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.

"""Передача экрана от окна обновления новой версии программы (из Zapret KVN).
Новая версия ставит себя сама и запускает программу раньше, чем закрывает
своё окно: иначе не узнать, способна ли она вообще стартовать. Чтобы два окна
не оказались на экране одновременно, программа стартует «за кулисами»:
* установщик создаёт файл-замок и передаёт его путь в переменной окружения;
* программа, собрав окно, создаёт рядом файл готовности и не показывает окно,
пока замок существует;
* установщик закрывает окно обновления и удаляет замок.
Переменная окружения, а не аргумент командной строки: при откате запускается
прежняя версия, и незнакомый аргумент она бы не пережила. Имя переменной одно
на обе программы.
"""
from __future__ import annotations
import os
from pathlib import Path
HOLD_ENV = "ZAPRET_UPDATE_HOLD"
# Если установщик исчез, не сняв замок, программа показывается сама.
MAX_HOLD_S = 20.0
def ready_marker(hold: Path) -> Path:
return hold.with_name(hold.name + ".ready")
# ── сторона установщика ─────────────────────────────────────────
def arm(hold: Path) -> dict[str, str]:
"""Поставить замок; вернуть окружение для запускаемой программы."""
hold.parent.mkdir(parents=True, exist_ok=True)
ready_marker(hold).unlink(missing_ok=True)
hold.write_text("hold", encoding="utf-8")
return {HOLD_ENV: str(hold)}
def is_ready(hold: Path) -> bool:
return ready_marker(hold).exists()
def release(hold: Path) -> None:
for path in (hold, ready_marker(hold)):
try:
path.unlink(missing_ok=True)
except OSError:
pass
# ── сторона программы ───────────────────────────────────────────
def claim() -> Path | None:
"""Замок, под которым запущена программа (None — обычный запуск)."""
return claim_from(os.environ)
def claim_from(environment) -> Path | None:
# pop: переменная не должна достаться дочерним процессам.
value = environment.pop(HOLD_ENV, "")
if not value:
return None
hold = Path(value)
return hold if hold.exists() else None
def confirm_ready(hold: Path) -> None:
try:
ready_marker(hold).write_text("ready", encoding="utf-8")
except OSError:
pass