Files
niritings/nirimod/updater.py
2026-05-29 00:41:12 +00:00

136 lines
3.7 KiB
Python

import json
import os
import shlex
import shutil
import subprocess
import tempfile
import threading
import urllib.request
import stat
API_URL = "https://api.github.com/repos/srinivasr/nirimod/commits/main"
INSTALL_DIR = os.path.expanduser("~/.local/share/nirimod")
FALLBACK_TERMINALS = [
"xdg-terminal-exec",
"gnome-terminal",
"kgx", # GNOME Console
"kitty",
"ghostty",
"alacritty",
"konsole",
"foot",
"xterm",
]
def check_for_updates(callback):
def _do_check():
try:
from gi.repository import GLib
if not os.path.isdir(os.path.join(INSTALL_DIR, ".git")):
GLib.idle_add(callback, None, None)
return
local_hash = subprocess.check_output(
["git", "rev-parse", "HEAD"],
cwd=INSTALL_DIR,
text=True,
).strip()
req = urllib.request.Request(
API_URL, headers={"User-Agent": "NiriMod-Updater"}
)
with urllib.request.urlopen(req, timeout=5) as resp:
data = json.loads(resp.read().decode("utf-8"))
remote_hash = data.get("sha")
commit_msg = data.get("commit", {}).get(
"message", "New update available"
)
if _update_available(local_hash, remote_hash, INSTALL_DIR):
GLib.idle_add(callback, remote_hash, commit_msg)
else:
GLib.idle_add(callback, None, None)
except Exception as e:
print(f"Update check failed: {e}")
GLib.idle_add(callback, None, None)
threading.Thread(target=_do_check, daemon=True).start()
def _commit_is_ancestor(commit_hash: str, install_dir: str) -> bool:
result = subprocess.run(
["git", "merge-base", "--is-ancestor", commit_hash, "HEAD"],
cwd=install_dir,
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
check=False,
)
return result.returncode == 0
def _update_available(
local_hash: str, remote_hash: str | None, install_dir: str = INSTALL_DIR
) -> bool:
if not remote_hash or remote_hash == local_hash:
return False
if _commit_is_ancestor(remote_hash, install_dir):
return False
return True
def _terminal_candidates():
terminal = os.environ.get("TERMINAL", "").strip()
if terminal:
yield terminal
yield from FALLBACK_TERMINALS
def _build_terminal_command(terminal: str, script_path: str) -> list[str] | None:
try:
parts = shlex.split(terminal)
except ValueError:
return None
if not parts:
return None
if os.path.basename(parts[0]) == "xdg-terminal-exec":
return [*parts, script_path]
if parts[-1] in {"-e", "--execute", "-x"}:
return [*parts, script_path]
return [*parts, "-e", script_path]
def launch_updater_in_terminal():
script_content = """#!/usr/bin/env bash
echo "Starting NiriMod update..."
curl -sSL https://raw.githubusercontent.com/srinivasr/nirimod/main/install.sh | bash -s -- --install
echo ""
echo "Update complete! Press Enter to close this window."
read
"""
script_path = os.path.join(tempfile.gettempdir(), "nirimod_update.sh")
with open(script_path, "w") as f:
f.write(script_content)
os.chmod(script_path, stat.S_IRWXU)
for term in _terminal_candidates():
command = _build_terminal_command(term, script_path)
if command is None or shutil.which(command[0]) is None:
continue
try:
subprocess.Popen(command)
return
except Exception:
continue
print("Could not find a suitable terminal to launch the update.")