diff options
Diffstat (limited to 'remote')
| -rw-r--r-- | remote/config.example.toml | 1 | ||||
| -rwxr-xr-x | remote/sbb-remote | 47 |
2 files changed, 46 insertions, 2 deletions
diff --git a/remote/config.example.toml b/remote/config.example.toml index e9b76d0..d275401 100644 --- a/remote/config.example.toml +++ b/remote/config.example.toml @@ -11,3 +11,4 @@ slackrepo_repo = "SBo-danix" hintdir = "/etc/slackrepo/SBo-danix/hintfiles" default_hintdir = "" # searched after hintdir when set genreprc = "/root/.genreprc" # REPOSOWNERGPG names the signing key +slackrepo_logdir = "/var/log/slackrepo/SBo-danix" diff --git a/remote/sbb-remote b/remote/sbb-remote index d82120a..a3b83a6 100755 --- a/remote/sbb-remote +++ b/remote/sbb-remote @@ -38,7 +38,7 @@ import tempfile import time import tomllib -VERSION = "0.3.0" +VERSION = "0.4.0" DEFAULTS = { "state_dir": "/var/lib/sb-builder", "repo_dir": "/var/lib/sbopkg/SBo-danix", @@ -50,6 +50,7 @@ DEFAULTS = { "hintdir": "/etc/slackrepo/SBo-danix/hintfiles", "default_hintdir": "", "genreprc": "/root/.genreprc", + "slackrepo_logdir": "/var/log/slackrepo/SBo-danix", } FLAGS = ("--yes", "--no") JOB_ID = re.compile(r"^\d{8}-\d{6}-[a-z]+(-\d+)?$") @@ -57,6 +58,8 @@ PKG = re.compile(r"^[^/]+/([^/]+)/\1\.SlackBuild$") PKGNAME = re.compile(r"^[A-Za-z0-9._+-]+$") ACTIONS = ("build", "rebuild", "update", "remove") 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 build.log returned; the failure is at the end ACTIVE = ("starting", "running") START_GRACE = 10 # seconds a job may sit in "starting" before it counts as lost @@ -346,7 +349,9 @@ def _packages(cfg): def _slackrepo(cfg): db = sqlite3.connect(f"file:{cfg['slackrepo_db']}?mode=ro", uri=True) try: - results = dict(db.execute("select result, count(*) from buildresults group by result")) + # legacy rows keyed by package file name, not category/pkg, match nothing: skip + results = dict(db.execute("select result, count(*) from buildresults" + " where itemid like '%/%' group by result")) built = db.execute("select count(distinct itemid) from revisions where dep = '/'").fetchone()[0] finally: db.close() @@ -478,6 +483,42 @@ def _builds(cfg): return built, results +def cmd_results(cfg): + """Last build attempt per package, newest first.""" + db = sqlite3.connect(f"file:{cfg['slackrepo_db']}?mode=ro", uri=True) + try: + rows = db.execute(""" + select b.itemid, b.result, cast(b.time as integer), r.version + from buildresults b left join revisions r on r.itemid = b.itemid and r.dep = '/' + where b.itemid like '%/%' order by cast(b.time as integer) desc""").fetchall() + finally: + db.close() + logdir = cfg["slackrepo_logdir"] + return {"rows": [[i, res, t, v, os.path.isdir(os.path.join(logdir, i))] + for i, res, t, v in rows]} + + +def _tail(path, limit): + with open(path, "rb") as f: + f.seek(0, os.SEEK_END) + size = f.tell() + f.seek(max(0, size - limit)) + return f.read().decode(errors="replace"), size > limit + + +def cmd_build_log(cfg, item=None): + if not ITEM.match(item or "") or ".." in item: + raise Error(f"bad package path: {item}") + d = os.path.join(cfg["slackrepo_logdir"], item) + build = os.path.join(d, "build.log") + if not os.path.isfile(build): + raise Error(f"no build log for {item}") + text, truncated = _tail(build, LOG_TAIL) + resource = os.path.join(d, "resource.log") + return {"build": text, "truncated": truncated, "mtime": int(os.path.getmtime(build)), + "resource": _tail(resource, LOG_TAIL)[0] if os.path.isfile(resource) else None} + + COLUMNS = ["path", "version", "info_version", "hint_version", "built_version", "built_time", "result", "maintainer", "requires", "summary", "hint_path", "hints"] @@ -577,6 +618,8 @@ COMMANDS = { "job-follow": cmd_job_follow, "job-kill": cmd_job_kill, "inventory": cmd_inventory, + "results": cmd_results, + "build-log": cmd_build_log, "gpg-status": cmd_gpg_status, "gpg-unlock": cmd_gpg_unlock, "_run": cmd_run, |
