aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--desktop/modules/updates/test_updatesctl.py5
-rwxr-xr-xdesktop/modules/updates/updatesctl88
2 files changed, 92 insertions, 1 deletions
diff --git a/desktop/modules/updates/test_updatesctl.py b/desktop/modules/updates/test_updatesctl.py
index b9e3881..2bfe1a8 100644
--- a/desktop/modules/updates/test_updatesctl.py
+++ b/desktop/modules/updates/test_updatesctl.py
@@ -178,5 +178,10 @@ assert rows(d["apps"]) == {
"GIMP": ("flatpak", "3.0.4", "3.0.6"),
}, d["apps"]
+sw = d["slackware"]
+assert sw["upgrade"] == [{"name": "glibc", "from": "2.44-3", "to": "2.44-4"}], sw
+assert sw["new"] == [{"name": "libtatsu", "version": "1.0.5-1"}], sw
+assert sw["removed"] == [{"name": "dhcp", "date": "2026-10-14"}], sw
+
shutil.rmtree(tmp)
print("ok")
diff --git a/desktop/modules/updates/updatesctl b/desktop/modules/updates/updatesctl
index 413bbcd..835f3d0 100755
--- a/desktop/modules/updates/updatesctl
+++ b/desktop/modules/updates/updatesctl
@@ -260,6 +260,91 @@ def tracked(installed, errors):
return out
+def blacklist():
+ """slackpkg's blacklist as regexes, its surrounding quotes dropped.
+
+ ponytail: a pattern blocks when it occurs anywhere in location/full
+ name, so a bare "dhcp" would also block "dhcpcd"; slackpkg itself is
+ stricter. Fine for a list of build tags and directories."""
+ out = []
+ for line in read(sysp("/etc/slackpkg/blacklist")).splitlines():
+ p = line.strip().strip('"')
+ if p and not p.startswith("#"):
+ out.append(re.compile(p))
+ return out
+
+
+def blocked(bl, path):
+ return any(p.search(path) for p in bl)
+
+
+DATE = re.compile(r"^[A-Z][a-z]{2} [A-Z][a-z]{2} +\d+ \d\d:\d\d:\d\d UTC \d{4}$")
+CLPKG = re.compile(r"^(\S+?)\.t[gx]z:\s+(Added|Removed)\.")
+
+
+def changelog(text):
+ """Package name -> (Added|Removed, date) from its newest such line.
+
+ The ChangeLog is newest first, so the first line seen wins. Other
+ actions are skipped: a package added and later upgraded is still new."""
+ seen, date = {}, ""
+ for line in text.splitlines():
+ if DATE.match(line):
+ date = time.strftime("%Y-%m-%d",
+ time.strptime(line, "%a %b %d %H:%M:%S UTC %Y"))
+ continue
+ m = CLPKG.match(line)
+ if m:
+ name = split(m.group(1).rsplit("/", 1)[-1])[0]
+ seen.setdefault(name, (m.group(2), date))
+ return seen
+
+
+def mirror_dir():
+ for line in read(sysp("/etc/slackpkg/mirrors")).splitlines():
+ line = line.strip()
+ if line and not line.startswith("#"):
+ return local_dir(line)
+ return None
+
+
+def verbuild(full):
+ _, v, _, b = split(full)
+ return f"{v}-{b}"
+
+
+def slackware(installed, errors):
+ out = {"upgrade": [], "new": [], "removed": []}
+ d = mirror_dir()
+ if d is None:
+ errors["mirror"] = "not a local mirror"
+ return out
+ pk, cl = nfs_read(d / "PACKAGES.TXT"), nfs_read(d / "ChangeLog.txt")
+ if pk is None or cl is None:
+ errors["mirror"] = "unreachable"
+ return out
+
+ bl = blacklist()
+ mirror = {}
+ for loc, full in packages_txt(pk):
+ if not blocked(bl, f"{loc}/{full}"):
+ mirror.setdefault(split(full)[0], full)
+ every = {split(f)[0] for f in installed if f.count("-") >= 3}
+ mine = {split(f)[0]: f for f in installed
+ if f.count("-") >= 3 and not blocked(bl, f)}
+
+ for name, full in sorted(mine.items()):
+ if name in mirror and mirror[name] != full:
+ out["upgrade"].append({"name": name, "from": verbuild(full),
+ "to": verbuild(mirror[name])})
+ for name, (act, date) in sorted(changelog(cl).items()):
+ if act == "Added" and name in mirror and name not in every:
+ out["new"].append({"name": name, "version": verbuild(mirror[name])})
+ elif act == "Removed" and name in mine and name not in mirror:
+ out["removed"].append({"name": name, "date": date})
+ return out
+
+
def main():
errors = {}
if "--refresh" in sys.argv[1:]:
@@ -274,7 +359,8 @@ def main():
installed = os.listdir(sysp("/var/log/packages"))
except OSError:
installed = []
- doc = {"checked": int(NEWVER.stat().st_mtime) if NEWVER.exists() else 0}
+ doc = {"checked": int(NEWVER.stat().st_mtime) if NEWVER.exists() else 0,
+ "slackware": slackware(installed, errors)}
doc.update(tracked(installed, errors))
doc["errors"] = errors
json.dump(doc, sys.stdout)