diff options
| author | Danilo M. <danix@danix.xyz> | 2026-10-21 11:36:01 +0200 |
|---|---|---|
| committer | Danilo M. <danix@danix.xyz> | 2026-10-21 11:36:01 +0200 |
| commit | 766ce6ff1ea584dfe980a5e0b29251768c4c0726 (patch) | |
| tree | eaa047f46fc9b59acae745406a116d2e479239ca /sb_builder/fmt.py | |
| parent | fb93767351dc0d7d4d4da8068b2f94df6ef44b6d (diff) | |
| download | sb-builder-766ce6ff1ea584dfe980a5e0b29251768c4c0726.tar.gz sb-builder-766ce6ff1ea584dfe980a5e0b29251768c4c0726.zip | |
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 <noreply@anthropic.com>
Diffstat (limited to 'sb_builder/fmt.py')
| -rw-r--r-- | sb_builder/fmt.py | 17 |
1 files changed, 16 insertions, 1 deletions
diff --git a/sb_builder/fmt.py b/sb_builder/fmt.py index 5c50628..3676fd0 100644 --- a/sb_builder/fmt.py +++ b/sb_builder/fmt.py @@ -17,10 +17,12 @@ """Text formatting shared by the tabs.""" import time +from collections import Counter NA = "n/a" ICON = {"starting": "…", "running": "▶", "ok": "✔", "failed": "✘", "killed": "■", "lost": "?"} +BAD_RESULTS = ("failed", "aborted", "bad") # slackrepo item results that fail a job def fmt_bytes(n): @@ -54,10 +56,22 @@ def job_title(j): return " ".join([j["kind"], *j["args"]]) +def counts(results): + """'3 ok, 1 failed' from a slackrepo job's {item: result}.""" + return ", ".join(f"{n} {r}" for r, n in Counter(results.values()).most_common()) + + +def failed_items(j): + """The items of a slackrepo job whose result failed it, sorted.""" + return sorted(i for i, r in (j.get("results") or {}).items() if r in BAD_RESULTS) + + def outcome(j): """How an ended job went, as a sentence for its notification.""" took = fmt_duration(j["end"] - j["start"]) if "end" in j and "start" in j else None after = f" after {took}" if took else "" + if failed_items(j): + return f"Failed: {', '.join(failed_items(j))} ({counts(j['results'])}){after}" return {"ok": f"Succeeded in {took}" if took else "Succeeded", "failed": f"Failed with exit code {j.get('exit', '?')}{after}", "killed": f"Killed{after}", @@ -68,4 +82,5 @@ def describe(j): """One-line job summary: icon, kind and args, start time, duration.""" dur = None if "start" not in j else j.get("end", time.time()) - j["start"] return (f"{ICON.get(j['status'], '?')} {job_title(j)} " - f"{fmt_time(j.get('start', j['created']))} {fmt_duration(dur)}") + f"{fmt_time(j.get('start', j['created']))} {fmt_duration(dur)}" + + (f" {counts(j['results'])}" if j.get("results") else "")) |
