org page: show each repo's latest CI status at a glance

5474d55 · gmorell · 2026-06-26 00:55

2 files +35 -1
Message
{commit_body(@commit)}

Files changed

modified lib/git_gud/workflows.ex
+16 −0
@@ -302,6 +302,22 @@ defmodule GitGud.Workflows do
302 302 |> Repo.one()
303 303 end
304 304
305 + @doc """
306 + Latest CI run status for each of `repo_ids`, as
307 + `%{repository_id => status}`. One queryfor repo-list views that want
308 + a CI status glance without an N+1. Repos with no runs are absent.
309 + """
310 + def latest_run_status_by_repo(repo_ids) when is_list(repo_ids) do
311 + from(r in WorkflowRun,
312 + where: r.repository_id in ^repo_ids,
313 + order_by: [asc: r.repository_id, desc: r.inserted_at],
314 + distinct: r.repository_id,
315 + select: {r.repository_id, r.status}
316 + )
317 + |> Repo.all()
318 + |> Map.new()
319 + end
320 +
305 321 @doc """
306 322 List artifacts attached to a run. Returns a list of
307 323 `%WorkflowArtifact{}` ordered by upload time.
modified lib/git_gud_web/live/org_live/show.ex
+19 −1
@@ -5,6 +5,7 @@ defmodule GitGudWeb.OrgLive.Show do
5 5 alias GitGud.Organizations.Team
6 6 alias GitGud.Profiles
7 7 alias GitGud.Repositories
8 + alias GitGud.Workflows
8 9
9 10 @impl true
10 11 def mount(%{"handle" => handle}, _session, socket) do
@@ -19,6 +20,8 @@ defmodule GitGudWeb.OrgLive.Show do
19 20 |> push_navigate(to: ~p"/")}
20 21
21 22 true ->
23 + repos = visible_repos(org, user)
24 +
22 25 {:ok,
23 26 socket
24 27 |> assign(:org, org)
@@ -26,7 +29,8 @@ defmodule GitGudWeb.OrgLive.Show do
26 29 |> assign(:is_admin?, user && Organizations.org_admin?(org, user))
27 30 |> assign(:is_member?, user && Organizations.member?(org, user))
28 31 |> assign(:teams, Organizations.list_teams(org))
29 |> assign(:repos, visible_repos(org, user))
32 + |> assign(:repos, repos)
33 + |> assign(:ci_status, Workflows.latest_run_status_by_repo(Enum.map(repos, & &1.id)))
30 34 |> assign(:pins, Profiles.list_pins(org))
31 35 |> assign(:readme, Repositories.get_profile_readme(org, theme: socket.assigns[:editor_theme]))
32 36 |> assign_team_form(Team.changeset(%Team{}, %{}))
@@ -34,6 +38,12 @@ defmodule GitGudWeb.OrgLive.Show do
34 38 end
35 39 end
36 40
41 + # daisyUI badge class for a repo's latest CI status.
42 + defp ci_badge("success"), do: "badge-success"
43 + defp ci_badge("failure"), do: "badge-error"
44 + defp ci_badge(s) when s in ~w(running queued blocked), do: "badge-warning"
45 + defp ci_badge(_), do: "badge-ghost"
46 +
37 47 defp can_view?(%{visibility: "public"}, _user), do: true
38 48 defp can_view?(%{visibility: "internal"}, %{} = _user), do: true
39 49 defp can_view?(%{visibility: "internal"}, nil), do: false
@@ -178,6 +188,14 @@ defmodule GitGudWeb.OrgLive.Show do
178 188 >
179 189 {repo.name}
180 190 </.link>
191 + <.link
192 + :if={@ci_status[repo.id]}
193 + navigate={~p"/r/#{@org.handle}/#{repo.name}/actions"}
194 + class={["badge badge-xs ml-1 not-italic", ci_badge(@ci_status[repo.id])]}
195 + title={"latest CI run: #{@ci_status[repo.id]}"}
196 + >
197 + {@ci_status[repo.id]}
198 + </.link>
181 199 <span
182 200 :if={Repositories.profile_repo?(repo, @org.handle)}
183 201 class="badge badge-xs badge-ghost ml-1 not-italic"

Parents: 1fdbefa