CI: finalize lingering steps when a job reaches a terminal state
28faa73 · gmorell · 2026-06-25 18:26
Message
{commit_body(@commit)}
Files changed
modified
lib/git_gud/workflows.ex
+36
−0
@@ -763,6 +763,7 @@ defmodule GitGud.Workflows do
| 763 | 763 | broadcast({:job_status, job.workflow_run_id, updated}) |
| 764 | 764 | |
| 765 | 765 | if WorkflowJob.terminal?(updated.status) do |
| 766 | + finalize_steps(updated) | |
| 766 | 767 | resolve_dependents(updated) |
| 767 | 768 | end |
| 768 | 769 |
@@ -770,6 +771,41 @@ defmodule GitGud.Workflows do
| 770 | 771 | {:ok, updated} |
| 771 | 772 | end |
| 772 | 773 | |
| 774 | + # When a job reaches a terminal state the runner doesn't reliably send | |
| 775 | + # a final per-step report — a failed job skips its remaining steps, and | |
| 776 | + # the terminal UpdateTask often carries only the job-level result — so | |
| 777 | + # `apply_step_states/2` never moves those rows off `pending`/`running`. | |
| 778 | + # Sweep the stragglers here so a finished job never shows in-flight | |
| 779 | + # steps: | |
| 780 | + # * job succeeded → step succeeded | |
| 781 | + # * step never started → skipped | |
| 782 | + # * step was in flight → inherit the job's terminal status | |
| 783 | + defp finalize_steps(%WorkflowJob{id: jid, status: job_status}) do | |
| 784 | + now = DateTime.utc_now() |> DateTime.truncate(:second) | |
| 785 | + | |
| 786 | + from(s in WorkflowStep, | |
| 787 | + where: s.workflow_job_id == ^jid and s.status in ["pending", "running"] | |
| 788 | + ) | |
| 789 | + |> Repo.all() | |
| 790 | + |> Enum.each(fn step -> | |
| 791 | + status = | |
| 792 | + cond do | |
| 793 | + job_status == "success" -> "success" | |
| 794 | + step.status == "pending" -> "skipped" | |
| 795 | + job_status in ["failure", "cancelled"] -> job_status | |
| 796 | + true -> "failure" | |
| 797 | + end | |
| 798 | + | |
| 799 | + changes = | |
| 800 | + %{status: status} | |
| 801 | + |> maybe_put(:conclusion, step_status_conclusion(status)) | |
| 802 | + |> maybe_put(:completed_at, step.completed_at || now) | |
| 803 | + | |
| 804 | + {:ok, updated} = step |> Ecto.Changeset.change(changes) |> Repo.update() | |
| 805 | + broadcast({:step_status, jid, updated}) | |
| 806 | + end) | |
| 807 | + end | |
| 808 | + | |
| 773 | 809 | @doc """ |
| 774 | 810 | After a job reaches a terminal state, walk every sibling job in the |
| 775 | 811 | same run whose `needs:` array contains this job's `job_id` and |
Parents: 7370f38