defmodule GitGudWeb.OrgLive.Actions do
@moduledoc """
Org-wide CI "single pane of glass": every workflow run across the
repositories this org owns that the viewer is allowed to see.
Live-updates by re-querying its (visibility-scoped) list whenever any
run changes status anywhere — see `Workflows.ci_topic/0`.
"""
use GitGudWeb, :live_view
import GitGudWeb.CiComponents
alias GitGud.Organizations
alias GitGud.Repositories
alias GitGud.Workflows
@impl true
def mount(%{"handle" => handle}, _session, socket) do
org = Organizations.get_organization_by_handle!(handle)
user = socket.assigns.current_scope && socket.assigns.current_scope.user
if can_view?(org, user) do
if connected?(socket),
do: Phoenix.PubSub.subscribe(GitGud.PubSub, Workflows.ci_topic())
repo_ids = Repositories.visible_repo_ids_for_org(org, user)
{:ok,
socket
|> assign(:org, org)
|> assign(:viewer, user)
|> assign(:is_admin?, user && Organizations.org_admin?(org, user))
|> assign(:repo_ids, repo_ids)
|> assign(:filter, :active)
|> assign(:page_title, "CI — @#{org.handle}")
|> load_runs()}
else
{:ok,
socket
|> put_flash(:error, "You're not authorized to view this organization.")
|> push_navigate(to: ~p"/")}
end
end
defp can_view?(%{visibility: "public"}, _user), do: true
defp can_view?(%{visibility: "internal"}, %{} = _user), do: true
defp can_view?(%{visibility: "internal"}, nil), do: false
defp can_view?(%{visibility: "private"} = org, %{} = user), do: Organizations.member?(org, user)
defp can_view?(_, _), do: false
defp load_runs(socket) do
repo_ids = socket.assigns.repo_ids
active = Workflows.list_runs_for_repos(repo_ids, statuses: Workflows.active_statuses())
runs =
case socket.assigns.filter do
:active -> active
:all -> Workflows.list_runs_for_repos(repo_ids, limit: 100)
end
socket
|> assign(:active_count, length(active))
|> assign(:runs, runs)
end
@impl true
def handle_event("filter", %{"scope" => scope}, socket) do
filter = if scope == "all", do: :all, else: :active
{:noreply, socket |> assign(:filter, filter) |> load_runs()}
end
@impl true
def handle_info({:ci_run_changed, _run_id}, socket) do
{:noreply, load_runs(socket)}
end
@impl true
def render(assigns) do
~H"""
<Layouts.app flash={@flash} current_scope={@current_scope}>
<div class="space-y-4">
<header class="flex items-baseline justify-between">
<h1 class="text-xl font-semibold">
CI <span class="opacity-50">—</span>
<.link navigate={~p"/orgs/#{@org.handle}"} class="link link-hover font-mono">
@{@org.handle}
</.link>
</h1>
<.filter_tabs current={@filter} active_count={@active_count} />
</header>
<ul class="divide-y divide-base-300">
<li :if={@runs == []} class="py-6 opacity-60 text-sm">
{if @filter == :active, do: "No runs in flight.", else: "No workflow runs yet."}
</li>
<.run_row :for={run <- @runs} run={run} show_actor?={true} />
</ul>
</div>
</Layouts.app>
"""
end
end
3.2 KiB · text
5af55d9