diff options
| author | Danilo M. <danix@danix.xyz> | 2026-10-19 20:53:43 +0200 |
|---|---|---|
| committer | Danilo M. <danix@danix.xyz> | 2026-10-19 20:53:43 +0200 |
| commit | b8400f38411818aa54a773dffb2c3a0c56b06dba (patch) | |
| tree | e6390c860efb005e424b91594c6c619c120d9b75 /sb_builder/jobs.py | |
| parent | d29fc1cd128b7d6a18e3e50d8ff4d6262f7e88ae (diff) | |
| download | sb-builder-b8400f38411818aa54a773dffb2c3a0c56b06dba.tar.gz sb-builder-b8400f38411818aa54a773dffb2c3a0c56b06dba.zip | |
Add Packages tab, hint view, Run it and signing key unlock
Packages tab over the helper inventory: query bar with cat:/maint:/
req:/status: prefixes, overlay and status chips, check column, and a
detail pane with versions, dependencies after hints and a structured
read-only view of the hint (hints.py mirrors slackrepo's parser).
Actions start slackrepo previews; the Jobs tab offers Run it with a
publish box (default from [builds] publish), asking for the signing
key passphrase first when the key is locked. Status bar shows the key
state; Dashboard tiles open their tabs; inventory reloads after
slackrepo and publish jobs.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'sb_builder/jobs.py')
| -rw-r--r-- | sb_builder/jobs.py | 63 |
1 files changed, 58 insertions, 5 deletions
diff --git a/sb_builder/jobs.py b/sb_builder/jobs.py index bcbd812..b413597 100644 --- a/sb_builder/jobs.py +++ b/sb_builder/jobs.py @@ -21,9 +21,9 @@ import re from PyQt6.QtCore import Qt, pyqtSignal from PyQt6.QtGui import QFontDatabase, QTextCursor -from PyQt6.QtWidgets import (QHBoxLayout, QListWidget, QListWidgetItem, QMenu, - QMessageBox, QPlainTextEdit, QPushButton, QSplitter, - QToolButton, QVBoxLayout, QWidget) +from PyQt6.QtWidgets import (QCheckBox, QHBoxLayout, QInputDialog, QLineEdit, QListWidget, + QListWidgetItem, QMenu, QMessageBox, QPlainTextEdit, QPushButton, + QSplitter, QToolButton, QVBoxLayout, QWidget) from .fmt import describe @@ -36,9 +36,10 @@ BAD = ("failed", "killed", "lost") class JobsTab(QWidget): refresh_requested = pyqtSignal() - def __init__(self, remote, parent=None): + def __init__(self, remote, publish=True, parent=None): super().__init__(parent) self.remote = remote + self.pending = () self.jobs = {} self.current = None self.offset = 0 @@ -52,10 +53,15 @@ class JobsTab(QWidget): self.apply_btn.clicked.connect(lambda: self.confirm_start( "Apply the shadowing plan to the repo (sbshadow --yes)?", "sbshadow", "--yes")) self.apply_btn.hide() + self.publish_box = QCheckBox("publish", checked=publish) + self.publish_box.setToolTip("Push to the mirror when the build ends; " + "unticked, use Publish later") + self.run_btn = QPushButton("Run it") + self.run_btn.clicked.connect(self.run_preview) self.kill_btn = QPushButton("Kill") self.kill_btn.clicked.connect(self.kill) bar = QHBoxLayout() - for w in (start, self.apply_btn): + for w in (start, self.apply_btn, self.run_btn, self.publish_box): bar.addWidget(w) bar.addStretch() bar.addWidget(self.kill_btn) @@ -131,6 +137,7 @@ class JobsTab(QWidget): p.deleteLater() def start(self, *args): + self.pending = args self.remote.call(self._started, "job-start", *args) def confirm_start(self, question, *args): @@ -143,7 +150,49 @@ class JobsTab(QWidget): self.remote.call(lambda _d, err: err and QMessageBox.warning(self, "Kill failed", err), "job-kill", self.current) + def run_preview(self): + """Start the previewed slackrepo run for real.""" + args = [a for a in self.jobs[self.current]["args"] if a != "--preview"] + if not self.publish_box.isChecked(): + args.insert(1, "--no-publish") + self.start_signed("slackrepo", *args) + + def ask_passphrase(self, retry): + text, ok = QInputDialog.getText( + self, "Signing key", ("Wrong passphrase, try again." if retry else + "Passphrase of the package signing key:"), + QLineEdit.EchoMode.Password) + return text if ok else None + + def start_signed(self, *args, retry=False): + """Unlock the signing key first when it is locked, then start the job.""" + def status(data, err): + if err: + QMessageBox.warning(self, "Signing key", err) + elif data["unlocked"] is False: + self._unlock(args, retry) + else: + self.start(*args) + self.remote.call(status, "gpg-status") + + def _unlock(self, args, retry): + passphrase = self.ask_passphrase(retry) + if passphrase is None: + return + + def unlocked(_data, err): + if err == "bad passphrase": + self._unlock(args, True) + elif err: + QMessageBox.warning(self, "Signing key", err) + else: + self.start(*args) + self.remote.call(unlocked, "gpg-unlock", stdin=passphrase.encode() + b"\n") + def _started(self, data, err): + if err == "signing key locked": + self.start_signed(*self.pending) # locked since the last check: ask again + return if err: QMessageBox.warning(self, "Start failed", err) return @@ -198,3 +247,7 @@ class JobsTab(QWidget): self.apply_btn.setVisible( bool(j) and j["status"] == "ok" and "--no" in j["args"] and bool(SHADOW_PLAN.search(self.log.toPlainText()))) + preview = bool(j) and j["kind"] == "slackrepo" and j["status"] == "ok" \ + and "--preview" in j["args"] + self.run_btn.setVisible(preview) + self.publish_box.setVisible(preview) |
