From 766ce6ff1ea584dfe980a5e0b29251768c4c0726 Mon Sep 17 00:00:00 2001 From: "Danilo M." Date: Wed, 21 Oct 2026 11:36:01 +0200 Subject: sbb-remote 0.12.0: named builds, per-package job results, prune - Packages: named packages run slackrepo build, which also updates outdated ones; update on a named item finds only already-built packages. The per-row Update action is gone, Update all stays. - A real slackrepo job reads its items' buildresults rows into the job's results and is failed when any item failed, aborted or was bad: slackrepo itself exits 0. The notification names the failed items; a Build results dialog lists every item with its log and Retry failed, reopenable from the Jobs ribbon. - slackrepo prune: the helper lists built items whose SlackBuild is gone and runs slackrepo remove on them, without an Update all. stats gives the count; Packages gets a Prune action, the Built tile a 'to prune' note. Remove and prune ask for confirmation at Run it. Co-Authored-By: Claude Opus 5.5 --- remote/sbb-remote | 42 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 39 insertions(+), 3 deletions(-) (limited to 'remote') diff --git a/remote/sbb-remote b/remote/sbb-remote index ace4fb9..15c0fec 100755 --- a/remote/sbb-remote +++ b/remote/sbb-remote @@ -41,7 +41,7 @@ import time import tomllib import urllib.request -VERSION = "0.11.0" +VERSION = "0.12.0" DEFAULTS = { "state_dir": "/var/lib/sb-builder", "repo_dir": "/var/lib/sbopkg/SBo-danix", @@ -63,7 +63,7 @@ JOB_ID = re.compile(r"^\d{8}-\d{6}-[a-z]+(-[a-z]+)*(-\d+)?$") PKG = re.compile(r"^[^/]+/([^/]+)/\1\.SlackBuild$") PKGNAME = re.compile(r"^[A-Za-z0-9._+-]+$") VERSION_ARG = re.compile(r"^[A-Za-z0-9._+-]{1,64}$") -ACTIONS = ("build", "rebuild", "update", "remove") +ACTIONS = ("build", "rebuild", "update", "remove", "prune") PRESET = "/usr/libexec/gpg-preset-passphrase" ITEM = re.compile(r"^[A-Za-z0-9._+-]+/[A-Za-z0-9._+-]+$") # category/pkg LOG_TAIL = 512 * 1024 # bytes of the log returned; the failure is at the end @@ -126,6 +126,27 @@ def kind_sbshadow(cfg, args): return ["sbshadow", _flag(args)], {} +def _prunable(cfg): + """Built items (slackrepo's packages table) whose SlackBuild is gone from the tree.""" + db = sqlite3.connect(f"file:{cfg['slackrepo_db']}?mode=ro", uri=True) + try: + items = [i for (i,) in db.execute("select distinct itemid from packages order by itemid")] + finally: + db.close() + return [i for i in items if ITEM.match(i) and not i.startswith("-") and not os.path.isfile( + os.path.join(cfg["repo_dir"], i, os.path.basename(i) + ".SlackBuild"))] + + +def _results_since(cfg, start): + """{itemid: result} of the slackrepo items that finished since start.""" + db = sqlite3.connect(f"file:{cfg['slackrepo_db']}?mode=ro", uri=True) + try: + return dict(db.execute("select itemid, result from buildresults" + " where cast(time as integer) >= ?", (int(start),))) + finally: + db.close() + + def kind_slackrepo(cfg, args): if not args or args[0] not in ACTIONS: raise Error(f"slackrepo: action must be one of {', '.join(ACTIONS)}") @@ -141,7 +162,14 @@ def kind_slackrepo(cfg, args): raise Error(f"slackrepo: no such package: {p}") if p not in pkgs: pkgs.append(p) - if not pkgs and action != "update": + if action == "prune": + if pkgs: + raise Error("slackrepo: prune takes no packages") + pkgs = _prunable(cfg) # computed here, never named by the GUI + if not pkgs: + raise Error("slackrepo: nothing to prune") + action = "remove" + elif not pkgs and action != "update": raise Error(f"slackrepo: {action} needs at least one package") argv = ["slackrepo", f"--repo={cfg['slackrepo_repo']}", action] if "--preview" in opts: @@ -360,6 +388,13 @@ def cmd_run(cfg, jid=None): rc = p.wait() meta.update(end=time.time(), exit=rc, status="ok" if rc == 0 else "killed" if rc == -signal.SIGTERM else "failed") + if signs(meta["kind"], meta["args"]): + # slackrepo exits 0 whatever its items did: their results tell + results = _try(lambda: _results_since(cfg, meta["start"])) + if results is not None: + meta["results"] = results + if rc == 0 and set(results.values()) & {"failed", "aborted", "bad"}: + meta["status"] = "failed" write_meta(d, meta) return None @@ -502,6 +537,7 @@ def cmd_stats(cfg): "slackpkg": _slackpkg(), "packages": _try(lambda: _packages(cfg)), "orphans": _try(lambda: _orphans(cfg)), + "prunable": _try(lambda: len(_prunable(cfg))), "sync": {o: _try(lambda o=o: int(_git(cfg, "log", "-1", "--format=%ct", "--", o + "/"))) for o in cfg["overlays"]}, "slackrepo": _try(lambda: _slackrepo(cfg)), -- cgit v1.2.3