fix(web): sort Executions by run id/attempt, not started timestamp

Sorting by started_at let a manually re-run old workflow jump above a
genuinely newer run: the re-run's started_at bumps to now, but its run
number never changes, so the Run column stopped reading strictly
descending. run_id is assigned once at creation and never changes across
attempts, so (run_id, attempt) descending is both correct and simpler --
it also handles a queued run (no started_at yet) for free, no datetime.max
special case needed.
This commit is contained in:
2026-07-25 19:52:49 +02:00
parent 4827cadada
commit 864a8689f8
3 changed files with 38 additions and 8 deletions
+9 -6
View File
@@ -350,15 +350,18 @@ def _cached_runs(
return runs, source, error, auth_failed, refused
def _recency_key(run: Execution) -> tuple[datetime, int]:
def _recency_key(run: Execution) -> tuple[int, int]:
"""Sort key for "newest first" (used with ``sorted(..., reverse=True)``).
A run that has not started yet (queued) is the newest thing there is, so it
sorts above every started run. ``run_id`` breaks ties — Gitea hands ids out
in creation order — which keeps the order stable when several runs share a
timestamp, as matrix jobs routinely do.
Ordered by run id, then attempt -- not by ``started`` timestamp. Gitea
hands ``run_id`` out in creation order and it never changes across
re-runs, unlike ``started``: manually re-running an old workflow bumps
its ``started_at`` to now, which would sort that stale run above a
genuinely newer one under a time-based key. This also handles a queued
run (no ``started`` yet) for free -- it already has the highest id of
anything currently listed, no ``datetime.max`` special case needed.
"""
return (run.started or datetime.max.replace(tzinfo=UTC), run.run_id)
return (run.run_id, run.attempt)
def _attach_vms(