From e87b90bf9f8df2223ceb8c90cad4b471f6263d45 Mon Sep 17 00:00:00 2001 From: Aaron Date: Sun, 14 Dec 2025 19:07:47 -0500 Subject: [PATCH] Auto-clear stale updater state --- pikit_api/releases.py | 63 ++++++++++++++++++++++++++++++++----------- 1 file changed, 48 insertions(+), 15 deletions(-) diff --git a/pikit_api/releases.py b/pikit_api/releases.py index 4d044d5..e37bc35 100644 --- a/pikit_api/releases.py +++ b/pikit_api/releases.py @@ -43,29 +43,62 @@ def read_current_version() -> str: def load_update_state() -> Dict[str, Any]: UPDATE_STATE_DIR.mkdir(parents=True, exist_ok=True) + def _reset_if_stale(state: Dict[str, Any]) -> Dict[str, Any]: + """ + If state thinks an update is running but the lock holder is gone, + clear it so the UI can recover instead of getting stuck forever. + """ + lock_alive = False + if UPDATE_LOCK.exists(): + try: + pid = int(UPDATE_LOCK.read_text().strip() or "0") + if pid > 0: + os.kill(pid, 0) + lock_alive = True + else: + UPDATE_LOCK.unlink(missing_ok=True) + except OSError: + UPDATE_LOCK.unlink(missing_ok=True) + except Exception: + UPDATE_LOCK.unlink(missing_ok=True) + + if state.get("in_progress") and not lock_alive: + state["in_progress"] = False + state["progress"] = None + if state.get("status") == "in_progress": + state["status"] = "up_to_date" + state["message"] = state.get("message") or "Recovered from interrupted update" + try: + save_update_state(state) + except Exception: + pass + return state + if UPDATE_STATE.exists(): try: state = json.loads(UPDATE_STATE.read_text()) state.setdefault("changelog_url", None) state.setdefault("latest_release_date", None) state.setdefault("current_release_date", None) - return state + return _reset_if_stale(state) except Exception: pass - return { - "current_version": read_current_version(), - "latest_version": None, - "last_check": None, - "status": "unknown", - "message": "", - "auto_check": False, - "in_progress": False, - "progress": None, - "channel": os.environ.get("PIKIT_CHANNEL", "dev"), - "changelog_url": None, - "latest_release_date": None, - "current_release_date": None, - } + return _reset_if_stale( + { + "current_version": read_current_version(), + "latest_version": None, + "last_check": None, + "status": "unknown", + "message": "", + "auto_check": False, + "in_progress": False, + "progress": None, + "channel": os.environ.get("PIKIT_CHANNEL", "dev"), + "changelog_url": None, + "latest_release_date": None, + "current_release_date": None, + } + ) def save_update_state(state: Dict[str, Any]) -> None: