Add publisher dashboard routes
All checks were successful
buildbot/nix-eval Build done.
buildbot/nix-build Build done.
buildbot/nix-effects Build done.

This commit is contained in:
Abel Luck 2026-06-02 10:18:59 +02:00
parent 96551c2788
commit e4a5246ab3
31 changed files with 1603 additions and 516 deletions

View file

@ -668,6 +668,7 @@ def load_runs_view(
now: datetime | None = None,
completed_page: int = 1,
completed_page_size: int = COMPLETED_EXECUTION_PAGE_SIZE,
path_prefix: str = "",
) -> RunsView:
reference_time = now or datetime.now(UTC)
resolved_log_dir = Path(log_dir)
@ -727,6 +728,7 @@ def load_runs_view(
execution,
resolved_log_dir,
reference_time,
path_prefix=path_prefix,
queued_follow_up=queued_by_job.get(
_job_id(cast(Job, execution.job))
),
@ -739,6 +741,7 @@ def load_runs_view(
reference_time,
position=position,
total_count=len(queued_executions),
path_prefix=path_prefix,
)
for position, execution in enumerate(queued_executions, start=1)
),
@ -748,12 +751,16 @@ def load_runs_view(
running_by_job.get(job.id),
queued_by_job.get(job.id),
reference_time,
path_prefix=path_prefix,
)
for job in jobs
),
"completed": tuple(
_project_completed_execution(
execution, resolved_log_dir, reference_time
execution,
resolved_log_dir,
reference_time,
path_prefix=path_prefix,
)
for execution in completed_executions
),
@ -801,10 +808,14 @@ def clear_completed_executions(*, log_dir: str | Path) -> int:
def load_dashboard_view(
*, log_dir: str | Path, now: datetime | None = None
*, log_dir: str | Path, now: datetime | None = None, path_prefix: str = ""
) -> dict[str, object]:
reference_time = now or datetime.now(UTC)
runs_view = load_runs_view(log_dir=log_dir, now=reference_time)
runs_view = load_runs_view(
log_dir=log_dir,
now=reference_time,
path_prefix=path_prefix,
)
output_dir = Path(log_dir).parent
running_by_job_id = {
int(cast(int, execution["job_id"])): execution
@ -842,6 +853,7 @@ def load_dashboard_view(
running_execution=running_by_job_id.get(_job_id(cast(Job, job))),
queued_execution=queued_by_job_id.get(_job_id(cast(Job, job))),
upcoming_job=upcoming_by_job_id.get(_job_id(cast(Job, job))),
path_prefix=path_prefix,
)
for job in jobs
),
@ -855,9 +867,9 @@ def load_dashboard_view(
def load_execution_log_view(
*, log_dir: str | Path, job_id: int, execution_id: int
*, log_dir: str | Path, job_id: int, execution_id: int, path_prefix: str = ""
) -> ExecutionLogView:
route = f"/job/{job_id}/execution/{execution_id}/logs"
route = _path(path_prefix, f"/job/{job_id}/execution/{execution_id}/logs")
with database.reader():
execution_primary_key = getattr(JobExecution, "_meta").primary_key
execution = (
@ -924,11 +936,16 @@ def _scheduler_job_id(job_id: int) -> str:
return f"{SCHEDULER_JOB_PREFIX}{job_id}"
def _path(prefix: str, path: str) -> str:
return f"{prefix.rstrip('/')}{path}" if prefix else path
def _project_running_execution(
execution: JobExecution,
log_dir: Path,
reference_time: datetime,
*,
path_prefix: str,
queued_follow_up: JobExecution | None = None,
) -> dict[str, object]:
job = cast(Job, execution.job)
@ -957,13 +974,16 @@ def _project_running_execution(
if execution.stop_requested_at
else "streaming stats from worker"
),
"log_href": f"/job/{job_id}/execution/{execution_id}/logs",
"log_href": _path(path_prefix, f"/job/{job_id}/execution/{execution_id}/logs"),
"log_exists": artifacts.log_path.exists(),
"cancel_label": "Cancel" if queued_follow_up is not None else "Stop",
"cancel_post_path": (
f"/actions/queued-executions/{_execution_id(queued_follow_up)}/cancel"
_path(
path_prefix,
f"/actions/queued-executions/{_execution_id(queued_follow_up)}/cancel",
)
if queued_follow_up is not None
else f"/actions/executions/{execution_id}/cancel"
else _path(path_prefix, f"/actions/executions/{execution_id}/cancel")
),
}
@ -974,6 +994,7 @@ def _project_queued_execution(
*,
position: int,
total_count: int,
path_prefix: str,
) -> dict[str, object]:
job = cast(Job, execution.job)
queued_at = _coerce_datetime(cast(datetime | str, execution.created_at))
@ -990,19 +1011,28 @@ def _project_queued_execution(
"status_tone": "idle",
"run_label": "Queued",
"run_disabled": True,
"run_post_path": f"/actions/jobs/{_job_id(job)}/run-now",
"cancel_post_path": (f"/actions/queued-executions/{execution_id}/cancel"),
"run_post_path": _path(path_prefix, f"/actions/jobs/{_job_id(job)}/run-now"),
"cancel_post_path": _path(
path_prefix,
f"/actions/queued-executions/{execution_id}/cancel",
),
"move_up_disabled": position == 1,
"move_up_post_path": (
None
if position == 1
else f"/actions/queued-executions/{execution_id}/move-up"
else _path(
path_prefix,
f"/actions/queued-executions/{execution_id}/move-up",
)
),
"move_down_disabled": position == total_count,
"move_down_post_path": (
None
if position == total_count
else f"/actions/queued-executions/{execution_id}/move-down"
else _path(
path_prefix,
f"/actions/queued-executions/{execution_id}/move-down",
)
),
}
@ -1012,6 +1042,8 @@ def _project_upcoming_job(
running_execution: JobExecution | None,
queued_execution: JobExecution | None,
reference_time: datetime,
*,
path_prefix: str,
) -> dict[str, object]:
job_id = _job_id(job)
trigger = _job_trigger(job)
@ -1051,14 +1083,21 @@ def _project_upcoming_job(
"run_reason": run_reason,
"toggle_label": "Disable" if job.enabled else "Enable",
"toggle_enabled": not job.enabled,
"run_post_path": f"/actions/jobs/{job_id}/run-now",
"toggle_post_path": f"/actions/jobs/{job_id}/toggle-enabled",
"delete_post_path": f"/actions/jobs/{job_id}/delete",
"run_post_path": _path(path_prefix, f"/actions/jobs/{job_id}/run-now"),
"toggle_post_path": _path(
path_prefix,
f"/actions/jobs/{job_id}/toggle-enabled",
),
"delete_post_path": _path(path_prefix, f"/actions/jobs/{job_id}/delete"),
}
def _project_completed_execution(
execution: JobExecution, log_dir: Path, reference_time: datetime
execution: JobExecution,
log_dir: Path,
reference_time: datetime,
*,
path_prefix: str,
) -> dict[str, object]:
job = cast(Job, execution.job)
job_id = _job_id(job)
@ -1100,7 +1139,7 @@ def _project_completed_execution(
else "Worker exited with failure"
)
),
"log_href": f"/job/{job_id}/execution/{execution_id}/logs",
"log_href": _path(path_prefix, f"/job/{job_id}/execution/{execution_id}/logs"),
"log_exists": artifacts.log_path.exists(),
}
@ -1113,6 +1152,7 @@ def _project_source_feed(
running_execution: dict[str, object] | None = None,
queued_execution: dict[str, object] | None = None,
upcoming_job: dict[str, object] | None = None,
path_prefix: str,
) -> dict[str, object]:
source = cast(Source, job.source)
source_slug = str(source.slug)
@ -1163,7 +1203,7 @@ def _project_source_feed(
"run_post_path": (
str(upcoming_job["run_post_path"])
if upcoming_job is not None
else f"/actions/jobs/{_job_id(job)}/run-now"
else _path(path_prefix, f"/actions/jobs/{_job_id(job)}/run-now")
),
"artifact_footprint": _format_bytes(_directory_size(source_dir)),
}