cdx-manager 0.3.4 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,107 @@
1
+ import json
2
+ import os
3
+ import urllib.error
4
+ import urllib.request
5
+ from datetime import datetime, timezone
6
+
7
+
8
+ UPDATE_CHECK_TTL_SECONDS = 12 * 60 * 60
9
+ LATEST_RELEASE_URL = "https://api.github.com/repos/AlexAgo83/cdx-manager/releases/latest"
10
+
11
+
12
+ def _parse_version(value):
13
+ raw = str(value or "").strip().lstrip("v")
14
+ parts = raw.split(".")
15
+ if len(parts) != 3:
16
+ return None
17
+ try:
18
+ return tuple(int(part) for part in parts)
19
+ except ValueError:
20
+ return None
21
+
22
+
23
+ def _is_newer_version(current_version, latest_version):
24
+ current = _parse_version(current_version)
25
+ latest = _parse_version(latest_version)
26
+ if not current or not latest:
27
+ return False
28
+ return latest > current
29
+
30
+
31
+ def _cache_path(base_dir):
32
+ return os.path.join(base_dir, "state", "update-check.json")
33
+
34
+
35
+ def _read_cache(path):
36
+ try:
37
+ with open(path, "r", encoding="utf-8") as handle:
38
+ return json.load(handle)
39
+ except (FileNotFoundError, OSError, json.JSONDecodeError):
40
+ return None
41
+
42
+
43
+ def _write_cache(path, payload):
44
+ os.makedirs(os.path.dirname(path), exist_ok=True)
45
+ with open(path, "w", encoding="utf-8") as handle:
46
+ json.dump(payload, handle, indent=2)
47
+ handle.write("\n")
48
+
49
+
50
+ def _fetch_latest_release():
51
+ request = urllib.request.Request(
52
+ LATEST_RELEASE_URL,
53
+ headers={
54
+ "Accept": "application/vnd.github+json",
55
+ "User-Agent": "cdx-manager-update-check",
56
+ },
57
+ )
58
+ with urllib.request.urlopen(request, timeout=5) as response:
59
+ payload = json.loads(response.read().decode("utf-8"))
60
+ return {
61
+ "latest_version": str(payload.get("tag_name") or "").lstrip("v"),
62
+ "url": payload.get("html_url") or payload.get("url"),
63
+ }
64
+
65
+
66
+ def check_for_update(base_dir, current_version, env=None, now_fn=None):
67
+ env = env or os.environ
68
+ now_fn = now_fn or (lambda: datetime.now(timezone.utc).timestamp())
69
+ if env.get("CDX_DISABLE_UPDATE_CHECK") in {"1", "true", "TRUE", "yes", "YES"}:
70
+ return None
71
+
72
+ path = _cache_path(base_dir)
73
+ now_ts = float(now_fn())
74
+ cached = _read_cache(path) or {}
75
+ checked_at = cached.get("checked_at")
76
+ if isinstance(checked_at, (int, float)) and (now_ts - checked_at) < UPDATE_CHECK_TTL_SECONDS:
77
+ latest_version = cached.get("latest_version")
78
+ if _is_newer_version(current_version, latest_version):
79
+ return {
80
+ "latest_version": latest_version,
81
+ "url": cached.get("url"),
82
+ "cached": True,
83
+ }
84
+ return None
85
+
86
+ try:
87
+ latest = _fetch_latest_release()
88
+ except (urllib.error.URLError, TimeoutError, ValueError, OSError):
89
+ return None
90
+
91
+ payload = {
92
+ "checked_at": now_ts,
93
+ "latest_version": latest.get("latest_version"),
94
+ "url": latest.get("url"),
95
+ }
96
+ try:
97
+ _write_cache(path, payload)
98
+ except OSError:
99
+ pass
100
+
101
+ if _is_newer_version(current_version, latest.get("latest_version")):
102
+ return {
103
+ "latest_version": latest.get("latest_version"),
104
+ "url": latest.get("url"),
105
+ "cached": False,
106
+ }
107
+ return None