Add built-in updater, bump to 1.2.0
- predatord checks the Gitea releases every 6 hours, downloads the newest pacman package, verifies its SHA-256 from the release notes and installs it with `pacman -U` in a separate systemd unit - Update notice and install button on the System page, in the sidebar and in the panel widget; `predatorctl update [--check] [--yes]` - Optional API token for private repositories - packaging/release.py builds and publishes releases in the format the updater expects Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -24,6 +24,7 @@ import traceback
|
||||
|
||||
from . import CONFIG_DIR, CONFIG_PATH, SOCKET_PATH, STATE_PATH, __version__
|
||||
from . import hardware as hw
|
||||
from . import updater
|
||||
from .keyboard import EFFECTS, NUM_CELLS, Keyboard
|
||||
|
||||
DEFAULT_CONFIG = {
|
||||
@@ -47,6 +48,7 @@ DEFAULT_CONFIG = {
|
||||
},
|
||||
"battery": {"limit": None}, # None = leave the firmware state alone
|
||||
"keyboard_presets": {},
|
||||
"updates": {"auto_check": True, "interval_hours": 6},
|
||||
}
|
||||
|
||||
|
||||
@@ -93,6 +95,9 @@ class Daemon:
|
||||
self.state = {}
|
||||
self.battery_limit = None
|
||||
self._ticks = 0
|
||||
self.update_info = None
|
||||
self.update_error = None
|
||||
self._update_checking = False
|
||||
|
||||
# ---------------------------------------------------------- Configuration
|
||||
def _load_config(self):
|
||||
@@ -230,6 +235,9 @@ class Daemon:
|
||||
"lightbar_effective": self.lightbar_effective(),
|
||||
"fan_curve_applied": self.curve_applied,
|
||||
"battery_limit": self.battery_limit,
|
||||
"update": self.update_info,
|
||||
"update_error": self.update_error,
|
||||
"update_status": updater.status(),
|
||||
"available": {
|
||||
"keyboard": self.kbd_present,
|
||||
"wmi": hw.wmi_available(hw.GUID_GAMING),
|
||||
@@ -241,6 +249,34 @@ class Daemon:
|
||||
}
|
||||
return st
|
||||
|
||||
def check_update_async(self):
|
||||
if self._update_checking:
|
||||
return
|
||||
self._update_checking = True
|
||||
|
||||
def work():
|
||||
try:
|
||||
info = updater.check()
|
||||
if info["available"] and (self.update_info or {}).get("latest") != info["latest"]:
|
||||
log("Update available: %s -> %s" % (info["current"], info["latest"]))
|
||||
self.update_info, self.update_error = info, None
|
||||
except Exception as e:
|
||||
self.update_error = str(e)
|
||||
finally:
|
||||
self._update_checking = False
|
||||
threading.Thread(target=work, daemon=True).start()
|
||||
|
||||
def maybe_check_update(self):
|
||||
u = self.config.get("updates", {})
|
||||
if not u.get("auto_check", True):
|
||||
return
|
||||
last = (self.update_info or {}).get("checked", 0)
|
||||
if time.time() - last > float(u.get("interval_hours", 6)) * 3600:
|
||||
if self.update_error and time.time() - getattr(self, "_last_try", 0) < 900:
|
||||
return # offline: retry every 15 min
|
||||
self._last_try = time.time()
|
||||
self.check_update_async()
|
||||
|
||||
def loop(self):
|
||||
while True:
|
||||
try:
|
||||
@@ -261,6 +297,8 @@ class Daemon:
|
||||
self.last_ac = ac
|
||||
self.fan_curve_tick()
|
||||
self._ticks += 1
|
||||
if self._ticks % 20 == 1:
|
||||
self.maybe_check_update()
|
||||
if self._ticks % 40 == 0:
|
||||
self.refresh_battery_limit()
|
||||
self.state = self.build_state()
|
||||
@@ -279,6 +317,17 @@ class Daemon:
|
||||
if cmd == "state":
|
||||
self.state = self.build_state()
|
||||
return self.state
|
||||
if cmd == "check_update":
|
||||
self.update_info, self.update_error = updater.check(), None
|
||||
return self.update_info
|
||||
if cmd == "install_update":
|
||||
info = updater.check()
|
||||
self.update_info = info
|
||||
return updater.install(info)
|
||||
if cmd == "set_updates":
|
||||
merge(self.config["updates"], {k: a[k] for k in ("auto_check", "interval_hours") if k in a})
|
||||
self.save_config()
|
||||
return {}
|
||||
if cmd == "restore":
|
||||
self.restore()
|
||||
return {}
|
||||
|
||||
Reference in New Issue
Block a user