aboutsummaryrefslogtreecommitdiffstats
path: root/remote
diff options
context:
space:
mode:
Diffstat (limited to 'remote')
-rw-r--r--remote/config.example.toml1
-rwxr-xr-xremote/sbb-remote47
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,