aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--README.md7
-rw-r--r--docs/design/2026-10-20-sb-builder-ui-design.md2
-rwxr-xr-xremote/sbb-remote10
-rw-r--r--sb_builder/__init__.py2
-rw-r--r--sb_builder/jobs.py10
-rw-r--r--tests/test_gui.py13
-rw-r--r--tests/test_remote.py5
7 files changed, 42 insertions, 7 deletions
diff --git a/README.md b/README.md
index 13a5a9e..b93a826 100644
--- a/README.md
+++ b/README.md
@@ -24,7 +24,8 @@ The design, one spec per phase, is in `docs/design/`.
- Desktop: Python 3.11 or newer, PyQt6 (with QtDBus), ssh access to the VM,
optionally `virsh` for starting the VM and `nvchecker` for upstream checks
- VM: Python 3.11 or newer, root ssh login, the
- [slackrepo_setup](https://forge.slackware.nl/danix/slackrepo_setup) tools (`sbpull`, `sbshadow`)
+ [slackrepo_setup](https://forge.slackware.nl/danix/slackrepo_setup) tools (`sbpull`, `sbshadow`,
+ `slackrepo_setup_SBo-danix.sh`)
## VM setup
@@ -137,7 +138,9 @@ reopens it.
- **Jobs**: every job with its live log. **Pull overlay** (`sbpull <overlay>`)
and **Check shadowing** (`sbshadow`) run as previews; when one finds
`shadow:` or `unshadow:` lines, its dialog offers **Apply shadowing**.
- **Kill** stops the selected running job.
+ **Rebuild tree** (after a confirm) replaces the SBo-danix tree with a fresh
+ clone of upstream and the overlays (`slackrepo_setup_SBo-danix.sh`); the live
+ tree stays until the new one is ready. **Kill** stops the selected running job.
- Real runs sign the repository. When the package signing key is locked
(status bar 🔒), the GUI asks for its passphrase first and hands it to the
VM's gpg-agent; it is never stored.
diff --git a/docs/design/2026-10-20-sb-builder-ui-design.md b/docs/design/2026-10-20-sb-builder-ui-design.md
index 826c6f9..80df13c 100644
--- a/docs/design/2026-10-20-sb-builder-ui-design.md
+++ b/docs/design/2026-10-20-sb-builder-ui-design.md
@@ -60,7 +60,7 @@ filter bar first), status bar.
| Packages | Build, Update, Rebuild, Remove, Update all, New hint, Publish | Select visible, Clear, Refresh |
| Hints | Check upstream, Update selected, Delete hint | Select visible, Clear, No download (checkable), Refresh |
| Results | Show build log | Refresh |
-| Jobs | Pull overlay (menu: one entry per overlay), Check shadowing, Run it, Kill | none |
+| Jobs | Pull overlay (menu: one entry per overlay), Check shadowing, Rebuild tree, Run it, Kill | none |
Disabled reasons (tooltips):
diff --git a/remote/sbb-remote b/remote/sbb-remote
index cabb8d1..ace4fb9 100755
--- a/remote/sbb-remote
+++ b/remote/sbb-remote
@@ -41,7 +41,7 @@ import time
import tomllib
import urllib.request
-VERSION = "0.10.0"
+VERSION = "0.11.0"
DEFAULTS = {
"state_dir": "/var/lib/sb-builder",
"repo_dir": "/var/lib/sbopkg/SBo-danix",
@@ -155,6 +155,12 @@ def kind_publish(cfg, args):
return ["sb-publish"], {}
+def kind_rebuild_tree(cfg, args):
+ if args:
+ raise Error("rebuild-tree takes no arguments")
+ return ["slackrepo_setup_SBo-danix.sh"], {}
+
+
def _pkgmap(cfg):
"""{package: category/package} of the repo."""
return {os.path.basename(d): os.path.relpath(d, cfg["repo_dir"])
@@ -216,7 +222,7 @@ def _self_job(kind):
KINDS = {"sbpull": kind_sbpull, "sbshadow": kind_sbshadow,
- "slackrepo": kind_slackrepo, "publish": kind_publish,
+ "slackrepo": kind_slackrepo, "publish": kind_publish, "rebuild-tree": kind_rebuild_tree,
**{k: _self_job(k) for k in ("hint-update", "hint-new", "fix-current")}}
diff --git a/sb_builder/__init__.py b/sb_builder/__init__.py
index 267ce94..87e7f4e 100644
--- a/sb_builder/__init__.py
+++ b/sb_builder/__init__.py
@@ -16,4 +16,4 @@
"""sb-builder: Qt6 GUI driving the SlackBuild tooling on a build VM."""
-VERSION = "0.10.0" # must match remote/sbb-remote VERSION
+VERSION = "0.11.0" # must match remote/sbb-remote VERSION
diff --git a/sb_builder/jobs.py b/sb_builder/jobs.py
index 111d804..9bfa2e3 100644
--- a/sb_builder/jobs.py
+++ b/sb_builder/jobs.py
@@ -134,6 +134,8 @@ class JobsTab(QWidget):
self.actions = [self.act_pull,
action(self, "Check shadowing", "edit-find", SP.SP_FileDialogContentsView,
lambda: self.start("sbshadow", "--no")),
+ action(self, "Rebuild tree", "edit-clear-all", SP.SP_DialogResetButton,
+ self.rebuild_tree),
self.act_run, self.act_kill]
self.view_actions = []
@@ -206,6 +208,14 @@ class JobsTab(QWidget):
self.pending = args
self.remote.call(self._started, "job-start", *args)
+ def confirm(self, question):
+ return QMessageBox.question(self, "Confirm", question) == QMessageBox.StandardButton.Yes
+
+ def rebuild_tree(self):
+ if self.confirm("Replace SBo-danix with a fresh clone of upstream and the overlays, "
+ "then reapply shadowing?"):
+ self.start("rebuild-tree")
+
def kill(self):
if QMessageBox.question(self, "Kill job", f"Send SIGTERM to {self.current}?") \
== QMessageBox.StandardButton.Yes:
diff --git a/tests/test_gui.py b/tests/test_gui.py
index c89dc86..aa6d681 100644
--- a/tests/test_gui.py
+++ b/tests/test_gui.py
@@ -602,6 +602,17 @@ class SmokeTest(unittest.TestCase):
d.run_btn.click()
self.assertIn(("job-start", "sbshadow", "--yes"), remote.calls)
+ def test_rebuild_tree_confirms(self):
+ w, remote = self.window(**{"job-start": ({"id": "20260101-000001-rebuild-tree"}, None)})
+ tab = w.jobs_tab
+ act = next(a for a in tab.actions if a.text() == "Rebuild tree")
+ tab.confirm = lambda q: False
+ act.trigger()
+ self.assertNotIn(("job-start", "rebuild-tree"), remote.calls)
+ tab.confirm = lambda q: True
+ act.trigger()
+ self.assertIn(("job-start", "rebuild-tree"), remote.calls)
+
def test_jobs_tab_badge(self):
w, remote = self.window(jobs=({"jobs": [JOB]}, None))
w.poll_jobs()
@@ -705,7 +716,7 @@ class SmokeTest(unittest.TestCase):
["Select visible", "Clear", "No download", "Refresh"]),
"Results": (["Show build log"], ["Refresh"]),
"Repository": ([], ["Refresh"]),
- "Jobs": (["Pull overlay", "Check shadowing", "Run it", "Kill"], []),
+ "Jobs": (["Pull overlay", "Check shadowing", "Rebuild tree", "Run it", "Kill"], []),
}
for i in range(w.tabs.count()):
w.tabs.setCurrentIndex(i)
diff --git a/tests/test_remote.py b/tests/test_remote.py
index 3ac584a..44d3a90 100644
--- a/tests/test_remote.py
+++ b/tests/test_remote.py
@@ -177,6 +177,11 @@ class KindsTest(Base):
with self.assertRaises(sbb.Error):
sbb.KINDS["publish"](self.cfg, ["x"])
+ def test_rebuild_tree(self):
+ self.assertEqual(sbb.KINDS["rebuild-tree"](self.cfg, []), (["slackrepo_setup_SBo-danix.sh"], {}))
+ with self.assertRaises(sbb.Error):
+ sbb.KINDS["rebuild-tree"](self.cfg, ["--yes"])
+
def test_rejects_bad_args(self):
for kind, args in (("sbpull", ["evil", "--no"]), ("sbpull", ["personal"]),
("sbpull", ["personal", "--force"]), ("sbshadow", []),