mirror of
https://github.com/baketnk/frame-yap.git
synced 2026-10-06 03:00:04 +02:00
Add opt-in Settings release check and attended update handoff
This commit is contained in:
1 parent
dd164aacee
commit
eb054199e1
13 files changed
+544
-13
No files matched your search
@@ -0,0 +1,63 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Explicit, bounded GitHub release check. No network access unless --check is passed."""
|
||||
import argparse
|
||||
from datetime import datetime
|
||||
import json
|
||||
import re
|
||||
import sys
|
||||
from urllib import request
|
||||
|
||||
RELEASE = re.compile(r"v?((?:0|[1-9][0-9]*)\.(?:0|[1-9][0-9]*)\.([1-9][0-9]{11}))\Z")
|
||||
URL = "https://api.github.com/repos/baketnk/frame-yap/releases/latest"
|
||||
|
||||
|
||||
def version(value):
|
||||
match = RELEASE.fullmatch(value)
|
||||
if not match:
|
||||
raise ValueError("invalid release version")
|
||||
datetime.strptime(match[2], "%Y%m%d%H%M")
|
||||
return tuple(map(int, match[1].split(".")))
|
||||
|
||||
|
||||
def outcome(payload, current):
|
||||
data = json.loads(payload)
|
||||
tag = data["tag_name"]
|
||||
if not isinstance(tag, str) or not tag.startswith("v"):
|
||||
raise ValueError("invalid release tag")
|
||||
candidate = version(tag)
|
||||
return "AVAILABLE " + tag[1:] if candidate > version(current) else "CURRENT"
|
||||
|
||||
|
||||
class NoRedirect(request.HTTPRedirectHandler):
|
||||
def redirect_request(self, req, fp, code, msg, headers, newurl):
|
||||
return None
|
||||
|
||||
|
||||
def check(current):
|
||||
version(current) # Reject malformed local metadata before making any request.
|
||||
opener = request.build_opener(request.ProxyHandler({}), NoRedirect())
|
||||
req = request.Request(URL, headers={"Accept": "application/vnd.github+json", "User-Agent": "FrameYap-update-check"})
|
||||
with opener.open(req, timeout=6) as response:
|
||||
payload = response.read(65537)
|
||||
if len(payload) > 65536:
|
||||
raise ValueError("oversized response")
|
||||
return outcome(payload, current)
|
||||
|
||||
|
||||
def main(argv=None):
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("--check", action="store_true", help="explicit network check")
|
||||
parser.add_argument("--current", required=True)
|
||||
args = parser.parse_args(argv)
|
||||
if not args.check:
|
||||
parser.error("--check required for network access")
|
||||
try:
|
||||
print(check(args.current), flush=True)
|
||||
except (ValueError, KeyError, TypeError, OSError):
|
||||
print("ERROR", flush=True)
|
||||
return 1
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
@@ -0,0 +1,47 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Attended terminal handoff for a user-requested FrameYap update."""
|
||||
import argparse
|
||||
from pathlib import Path
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
# The same validation as check-update.py, without importing the network helper.
|
||||
import re
|
||||
VERSION = re.compile(r"(?:0|[1-9][0-9]*)\.(?:0|[1-9][0-9]*)\.[1-9][0-9]{11}\Z")
|
||||
|
||||
|
||||
def installer_command(script, version):
|
||||
if not VERSION.fullmatch(version):
|
||||
raise ValueError("invalid version")
|
||||
return ["sh", str(script), "--mode", "binary", "--version", version, "--yes"]
|
||||
|
||||
|
||||
def main(argv=None):
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("--version", required=True)
|
||||
args = parser.parse_args(argv)
|
||||
script = Path(__file__).resolve().parent.parent / "bin/install.sh"
|
||||
try:
|
||||
command = installer_command(script, args.version)
|
||||
except ValueError:
|
||||
parser.error("invalid release version")
|
||||
if not script.is_file():
|
||||
print("Installed FrameYap installer not found.")
|
||||
return 1
|
||||
print(f"FrameYap update: {args.version}")
|
||||
print("Close FrameYap, then press Enter here to download and install this version.")
|
||||
if not sys.stdin.isatty():
|
||||
print("A terminal is required; no download started.")
|
||||
return 1
|
||||
try:
|
||||
input()
|
||||
except (KeyboardInterrupt, EOFError):
|
||||
print("Update cancelled.")
|
||||
return 1
|
||||
result = subprocess.run(command, check=False)
|
||||
print("Update installed." if result.returncode == 0 else "Update failed; check the installer message above.")
|
||||
return result.returncode
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Reference in new issue
Block a user