fix(updater): keep auto-updater alive across watcher restarts (v0.4.1)

Saving the Settings dialog calls _restart_watcher, which set+replaced the
shared stop_event the updater loop also keyed off — so a settings save
silently killed the auto-updater thread (race: survived one save, died on
the next). Heartbeats kept flowing but the agent stopped checking for /
applying updates until relaunch.

Give the updater its own app-lifetime event (_app_stop), set only on
Exit / when applying an update and untouched by watcher restarts. Adds
test_thor_tray.py (4 lifecycle tests). Bump to v0.4.1.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-25 18:01:58 +00:00
parent 302934ced6
commit a92afd64cd
7 changed files with 101 additions and 11 deletions
+7 -4
View File
@@ -1,5 +1,5 @@
"""
Thor Watcher — System Tray Launcher v0.4.0
Thor Watcher — System Tray Launcher v0.4.1
Requires: pystray, Pillow, tkinter (stdlib)
Run with: pythonw thor_tray.py (no console window)
@@ -334,7 +334,8 @@ def _show_cancel_message():
class WatcherTray:
def __init__(self):
self.state = {}
self.stop_event = threading.Event()
self.stop_event = threading.Event() # watcher lifecycle; replaced on restart
self._app_stop = threading.Event() # app lifetime; only set on exit/update
self._watcher_thread = None
self._icon = None
self._menu_lock = threading.Lock()
@@ -390,6 +391,7 @@ class WatcherTray:
subprocess.Popen(["explorer", HERE])
def _exit(self, icon, item):
self._app_stop.set()
self.stop_event.set()
icon.stop()
@@ -478,7 +480,7 @@ class WatcherTray:
# line in the log soon after startup; subsequent checks every ~5 min.
update_check_counter = 27
while not self.stop_event.is_set():
while not self._app_stop.is_set():
icon_status = self._tray_status()
if self._icon is not None:
@@ -504,7 +506,7 @@ class WatcherTray:
self._do_update(url)
return
self.stop_event.wait(timeout=10)
self._app_stop.wait(timeout=10)
def _do_update(self, download_url=None):
"""Notify tray then apply update. If url is None, fetch it first."""
@@ -519,6 +521,7 @@ class WatcherTray:
success = apply_update(download_url)
if success:
self._app_stop.set()
self.stop_event.set()
if self._icon is not None:
self._icon.stop()