4.2 KiB · text 5af55d9
defmodule GitGudWeb.BranchProtectionLive.Index do
use GitGudWeb, :live_view
alias GitGud.BranchProtections
alias GitGud.BranchProtections.BranchProtection
alias GitGud.Repositories
alias GitGud.Repositories.Storage
@impl true
def mount(%{"owner" => owner, "name" => name}, _session, socket) do
repo =
Repositories.get_repository_by_path!(owner, name)
|> GitGud.Repo.preload([:owner, :organization])
{:ok,
socket
|> assign(:repo, repo)
|> assign(:handle, Storage.repo_handle(repo))
|> GitGudWeb.RepoLive.Header.assign_chrome(repo)
|> stream(:rules, BranchProtections.list_rules(repo))
|> assign_form(BranchProtection.changeset(%BranchProtection{}, %{}))
|> assign(:page_title, "Branch protection — #{owner}/#{name}")}
end
defp assign_form(socket, cs), do: assign(socket, :form, to_form(cs))
@impl true
def handle_event("validate", %{"branch_protection" => attrs}, socket) do
cs = %BranchProtection{} |> BranchProtection.changeset(attrs) |> Map.put(:action, :validate)
{:noreply, assign_form(socket, cs)}
end
def handle_event("add", %{"branch_protection" => attrs}, socket) do
case BranchProtections.create_rule(socket.assigns.repo, attrs) do
{:ok, rule} ->
{:noreply,
socket
|> stream_insert(:rules, rule)
|> assign_form(BranchProtection.changeset(%BranchProtection{}, %{}))
|> put_flash(:info, "Rule added.")}
{:error, cs} ->
{:noreply, assign_form(socket, cs)}
end
end
def handle_event("delete", %{"id" => id}, socket) do
rule = BranchProtections.get_rule!(id)
if rule.repository_id == socket.assigns.repo.id do
{:ok, _} = BranchProtections.delete_rule(rule)
{:noreply, socket |> stream_delete(:rules, rule) |> put_flash(:info, "Rule removed.")}
else
{:noreply, socket}
end
end
@impl true
def render(assigns) do
~H"""
<Layouts.app flash={@flash} current_scope={@current_scope}>
<div class="space-y-6">
<GitGudWeb.RepoLive.Header.header
repo={@repo}
handle={@handle}
current_scope={@current_scope}
has_packages?={@has_packages?}
can_admin?={@can_admin?}
latest_run={@latest_run}
/>
<.repo_settings_header handle={@handle} repo={@repo} current={:branches} />
<.form for={@form} id="bp-form" phx-change="validate" phx-submit="add" class="space-y-3 max-w-2xl">
<.input
field={@form[:pattern]}
label="Pattern (glob)"
required
placeholder="main, release/*"
/>
<.input field={@form[:required_approvals]} label="Required approving reviews" type="number" />
<.input field={@form[:allow_force_push]} type="checkbox" label="Allow force push" />
<.input field={@form[:allow_deletions]} type="checkbox" label="Allow deletion" />
<button type="submit" class="btn btn-sm btn-primary">Add rule</button>
</.form>
<ul id="bp-rules" phx-update="stream" class="divide-y divide-base-300 max-w-2xl">
<li class="hidden only:block py-6 opacity-60 text-sm">No protection rules.</li>
<li :for={{id, rule} <- @streams.rules} id={id} class="py-3">
<div class="flex items-center gap-2">
<span class="badge badge-primary font-mono">{rule.pattern}</span>
<span class="text-xs opacity-70 flex-1">
{rule.required_approvals} approval{plural(rule.required_approvals)} ·
force-push {bool_label(rule.allow_force_push)} ·
delete {bool_label(rule.allow_deletions)}
</span>
<button
type="button"
class="btn btn-xs btn-ghost"
phx-click="delete"
phx-value-id={rule.id}
data-confirm="Delete this rule?"
>
<.icon name="hero-trash" class="size-4" />
</button>
</div>
</li>
</ul>
</div>
</Layouts.app>
"""
end
defp plural(1), do: ""
defp plural(_), do: "s"
defp bool_label(true), do: "allowed"
defp bool_label(false), do: "blocked"
end