Run a whole project: project runs in manual and auto-confirm mode (RBT-WP-0008-T10)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Assistant: claude-code Assistant-Model: sonnet Assistant-Process: 295952@bnt-lap001 Assistant-Session: e93f64ad-516c-46eb-9666-aad8d300c477
This commit is contained in:
parent
1d1087fa7f
commit
eff75a303c
14 changed files with 1112 additions and 15 deletions
17
rtut/ui.py
17
rtut/ui.py
|
|
@ -15,6 +15,8 @@ from . import ui_projects as UP
|
|||
from . import ui_v2 as UI2
|
||||
from . import activity as ACT, user as USER
|
||||
from . import ui_attrs as UA
|
||||
from . import ui_projectrun as UPR
|
||||
from . import projectrun as PRUN
|
||||
from .attrs import Registry
|
||||
|
||||
LIGHT = "--fg:#141414;--bg:#fbfbf9;--mut:#6b6b66;--line:#d9d9d2;--card:#fff;--hi:#f2e35a;--ok:#1a7f37;--warn:#9a6700;--bad:#c62828;--side:#f3f3ef"
|
||||
|
|
@ -127,7 +129,7 @@ def page(title: str, body: str) -> bytes:
|
|||
"const f=b.closest('form'),n=f.querySelector('[name=note]'),w=f.querySelector('[data-warn]');"
|
||||
"if(n&&!n.value.trim()&&!b.dataset.warned){ev.preventDefault();b.dataset.warned='1';w.hidden=false;n.focus();}}));"
|
||||
# sidebar: mark where we are; theme switch cycles system -> light -> dark
|
||||
"const p=location.pathname;const alias={'/projects':['/project/','/template/'],'/runbooks':['/pack/','/run/'],'/attribute-sets':['/attribute-set/']};"
|
||||
"const p=location.pathname;const alias={'/projects':['/project/','/template/','/project-run/'],'/runbooks':['/pack/','/run/'],'/attribute-sets':['/attribute-set/']};"
|
||||
"document.querySelectorAll('a[data-nav]').forEach(a=>{const h=a.getAttribute('href');"
|
||||
"if(h==='/'?p==='/':p===h||p.startsWith(h+'/')||(alias[h]||[]).some(x=>p.startsWith(x)))a.setAttribute('aria-current','page')});"
|
||||
"const tb=document.querySelector('[data-theme-toggle]');const cur=()=>{try{return localStorage.getItem('rtut-theme')||'system'}catch(x){return 'system'}};"
|
||||
|
|
@ -186,7 +188,7 @@ class App:
|
|||
f"<p class=note>Running as <b>{label}</b> · <a href='/user'>change</a></p>")
|
||||
|
||||
def activities_html(self, limit: int = 12, level: str = "h2", link: bool = False) -> str:
|
||||
events = ACT.latest(self.packs, limit)
|
||||
events = ACT.latest(self.packs, limit, PRUN.list_runs(self.registry()))
|
||||
if not events:
|
||||
return f"{heading(level, 'Latest activities', '/activity' if link else None)}<p class=note>Nothing has happened yet. Start a run or a project.</p>"
|
||||
rows = "".join(
|
||||
|
|
@ -379,7 +381,8 @@ class App:
|
|||
where = ""
|
||||
if ctx.get("project"):
|
||||
where = (f" · project <a href='/project/{e(ctx['project'])}'>{e(ctx['project'])}</a>"
|
||||
f"{' @' + e(ctx['target']) if ctx.get('target') else ''} · {e(ctx.get('environment', ''))}")
|
||||
f"{' @' + e(ctx['target']) if ctx.get('target') else ''} · {e(ctx.get('environment', ''))}"
|
||||
f"{' · part of <a href=' + chr(39) + '/project-run/' + e(ctx['project_run']) + chr(39) + '>project run ' + e(ctx['project_run']) + '</a>' if ctx.get('project_run') else ''}")
|
||||
out = [f"<h1>{e(d['title'])}</h1><p class=pill>{e(d['exercise_status'])}</p>"
|
||||
f"<p class=note>run {e(rid)} · operator {e(r.data['operator'])}{where}</p>"
|
||||
f"<p class=note>{UI2.times_line(r)}</p>"]
|
||||
|
|
@ -520,6 +523,10 @@ def make_handler(app: App, port_holder: list):
|
|||
return self._send_file(status, body, ctype, dl or ("artefact-" + parts[4] if want_dl and status == 200 else None))
|
||||
if parts[0] == "run":
|
||||
return self._send(app.run_page(int(parts[1]), parts[2], query=parse_qs(urlparse(self.path).query)))
|
||||
if parts[0] == "project-run" and len(parts) == 2:
|
||||
body = UPR.page_for(app, parts[1])
|
||||
if body:
|
||||
return self._send(body)
|
||||
if parts[0] == "project" and len(parts) == 2:
|
||||
body = UP.project_page(app, parts[1])
|
||||
if body:
|
||||
|
|
@ -569,6 +576,8 @@ def make_handler(app: App, port_holder: list):
|
|||
except M.ReviewError as ex:
|
||||
raise ValueError(str(ex))
|
||||
return self._redirect(f"/pack/{i}#maturity")
|
||||
if (len(parts) == 3 and parts[0] == "project" and parts[2] == "runall") or (len(parts) == 3 and parts[0] == "project-run"):
|
||||
return self._redirect(UPR.handle_post(app, parts, form))
|
||||
if len(parts) == 3 and parts[0] == "project" and parts[2] == "edit":
|
||||
from . import project as J
|
||||
doc = app.registry().projects[parts[1]]
|
||||
|
|
@ -623,6 +632,8 @@ def make_handler(app: App, port_holder: list):
|
|||
except (R.RunError, KeyError, ValueError, IndexError) as ex:
|
||||
msg = str(ex)
|
||||
try:
|
||||
if parts[0] == "project-run":
|
||||
return self._send(UPR.page_for(app, parts[1], msg) or page("Error", f"<p class=bad>{e(msg)}</p>"), 400)
|
||||
if parts[0] == "project":
|
||||
return self._send(UP.project_page(app, parts[1], msg) or page("Error", f"<p class=bad>{e(msg)}</p>"), 400)
|
||||
if parts[0] == "template":
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue