Initial commit on Forgejo
All checks were successful
CI and deploy / Test (push) Successful in 4m52s
CI and deploy / Deploy production (push) Successful in 23s

Fresh repository history for elektrine/tarakan hosted at
https://git.elektrine.com/elektrine/tarakan.
This commit is contained in:
Maxfield Luke 2026-07-29 04:43:40 -04:00
commit af6077b9c3
455 changed files with 78366 additions and 0 deletions

View file

@ -0,0 +1,322 @@
defmodule TarakanWeb.RepositoryCommitsLive do
@moduledoc """
Commit history of a repository's branches, plus a per-commit detail view
with the full message and patch.
Every listed commit is reachable from a published branch tip, which is
exactly the set `RepositoryCode.authorize_public_commit/2` allows in the
code browser - so each commit links straight into the pinned source view.
"""
use TarakanWeb, :live_view
alias Tarakan.Repositories
alias Tarakan.Repositories.Repository
alias Tarakan.RepositoryCode
alias Tarakan.RepositoryCode.Commit
alias TarakanWeb.RepositoryPaths
@page_size 50
@max_diff_lines 5_000
@commit_sha_pattern ~r/^[0-9a-f]{40}$/
@impl true
def mount(params, _session, socket) do
case repository_from_params(params, socket) do
%Repository{} = repository ->
if connected?(socket) do
Repositories.subscribe()
end
{:ok,
socket
|> assign(:repository, repository)
|> assign(:view_state, :loading)
|> assign(:error_kind, nil)
|> assign(:limit, @page_size)
|> assign(:has_more?, false)
|> assign(:branch_options, [])
|> assign(:selected_branch, repository.default_branch)
|> assign(:commit_sha, nil)
|> assign(:commit, nil)
|> maybe_load_branch_options()
|> stream_configure(:commits, dom_id: &"commit-#{&1.sha}")
|> stream(:commits, [])}
nil ->
raise Ecto.NoResultsError, queryable: Repository
end
end
@impl true
def handle_params(params, _uri, socket) do
{:noreply, apply_action(socket, socket.assigns.live_action, params)}
end
@impl true
def handle_event("retry", _params, socket) do
{:noreply,
socket
|> assign(:view_state, :loading)
|> assign(:error_kind, nil)
|> maybe_load()}
end
def handle_event("load_more", _params, socket) do
{:noreply,
socket
|> assign(:limit, socket.assigns.limit + @page_size)
|> assign(:view_state, :loading)
|> maybe_load_commits()}
end
def handle_event("select_branch", %{"branch" => branch}, socket) do
branch = String.trim(to_string(branch || ""))
if socket.assigns.live_action == :index and branch != "" do
{:noreply,
socket
|> assign(:selected_branch, branch)
|> assign(:limit, @page_size)
|> assign(:view_state, :loading)
|> maybe_load_commits()}
else
{:noreply, socket}
end
end
@impl true
def handle_async(:commits, {:ok, result}, socket) do
case result do
{:ok, %{commits: commits}} ->
{:noreply,
socket
|> assign(:view_state, :ready)
|> assign(:error_kind, nil)
|> assign(:has_more?, length(commits) >= socket.assigns.limit)
|> stream(:commits, commits, reset: true)}
{:error, reason} ->
{:noreply,
socket
|> assign(:view_state, :error)
|> assign(:error_kind, reason)
|> stream(:commits, [], reset: true)}
end
end
def handle_async(:commits, {:exit, _reason}, socket) do
{:noreply,
socket
|> assign(:view_state, :error)
|> assign(:error_kind, :unavailable)}
end
def handle_async(:commit, {:ok, result}, socket) do
case result do
{:ok, %Commit{} = commit} ->
{:noreply,
socket
|> assign(:view_state, :ready)
|> assign(:error_kind, nil)
|> assign(:commit, commit)
|> assign(
:page_title,
"#{commit.subject} · #{socket.assigns.repository.owner}/#{socket.assigns.repository.name}"
)}
{:error, reason} ->
{:noreply,
socket
|> assign(:view_state, :error)
|> assign(:error_kind, reason)
|> assign(:commit, nil)}
end
end
def handle_async(:commit, {:exit, _reason}, socket) do
{:noreply,
socket
|> assign(:view_state, :error)
|> assign(:error_kind, :unavailable)}
end
@impl true
def handle_info(
{:repository_record_updated, %Repository{id: repository_id}},
%{assigns: %{repository: %Repository{id: repository_id} = repository}} = socket
) do
if Repositories.get_visible_repository(
repository.host,
repository.owner,
repository.name,
socket.assigns.current_scope
) do
{:noreply, socket}
else
{:noreply, push_navigate(socket, to: "/")}
end
end
def handle_info({event, %Repository{}}, socket)
when event in [:repository_registered, :repository_record_updated],
do: {:noreply, socket}
defp apply_action(socket, :index, _params) do
socket
|> assign(
:page_title,
"Commits · #{socket.assigns.repository.owner}/#{socket.assigns.repository.name}"
)
|> assign(:view_state, :loading)
|> assign(:error_kind, nil)
|> assign(:commit_sha, nil)
|> assign(:commit, nil)
|> maybe_load_commits()
end
defp apply_action(socket, :show, %{"commit_sha" => commit_sha}) do
if Regex.match?(@commit_sha_pattern, commit_sha) do
socket
|> assign(:view_state, :loading)
|> assign(:error_kind, nil)
|> assign(:commit_sha, commit_sha)
|> assign(:commit, nil)
|> stream(:commits, [], reset: true)
|> maybe_load_commit()
else
socket
|> assign(:view_state, :error)
|> assign(:error_kind, :not_found)
|> assign(:commit_sha, nil)
|> assign(:commit, nil)
end
end
defp maybe_load(socket) do
case socket.assigns.live_action do
:index -> maybe_load_commits(socket)
:show -> maybe_load_commit(socket)
end
end
defp maybe_load_commits(socket) do
if connected?(socket) do
repository = socket.assigns.repository
limit = socket.assigns.limit
branch = selected_branch(socket)
start_async(socket, :commits, fn ->
RepositoryCode.list_commits(repository, limit, branch: branch)
end)
else
socket
end
end
defp maybe_load_commit(socket) do
if connected?(socket) and is_binary(socket.assigns.commit_sha) do
repository = socket.assigns.repository
commit_sha = socket.assigns.commit_sha
start_async(socket, :commit, fn -> RepositoryCode.show_commit(repository, commit_sha) end)
else
socket
end
end
# The default branch goes through the default-tip resolution; any other
# picker choice names the branch explicitly.
defp selected_branch(socket) do
case socket.assigns.selected_branch do
branch when is_binary(branch) and branch != "" -> branch
_other -> nil
end
end
defp maybe_load_branch_options(socket) do
if connected?(socket) do
case RepositoryCode.list_branches(socket.assigns.repository) do
{:ok, branches} -> assign(socket, :branch_options, branches)
{:error, _reason} -> socket
end
else
socket
end
end
defp repository_from_params(params, socket) do
with {:ok, host, owner, name} <- normalize_params(params),
%Repository{} = repository <-
Repositories.get_visible_repository(host, owner, name, socket.assigns.current_scope) do
repository
else
_not_visible -> nil
end
end
defp normalize_params(%{"host" => slug, "owner" => owner, "name" => name}) do
with {:ok, host} <- Tarakan.Hosts.host_for_slug(slug) do
{:ok, host, owner, name}
end
end
# Bare GitHub-style route (/owner/name/commits...). A host-like first
# segment means the bare pattern swallowed a remote path - a repository
# literally named "commits" - so shift the params, same corner as
# "code"/"security".
defp normalize_params(%{"owner" => owner, "name" => name}) do
if Tarakan.Hosts.host_segment?(owner) do
normalize_params(%{"host" => owner, "owner" => name, "name" => "commits"})
else
{:ok, Repository.hosted_host(), owner, name}
end
end
defp normalize_params(_params), do: :error
defp short_sha(sha) when is_binary(sha), do: binary_part(sha, 0, 7)
defp commit_datetime(%DateTime{} = datetime),
do: Calendar.strftime(datetime, "%Y-%m-%d %H:%M UTC")
defp commit_datetime(_other), do: "unknown date"
defp diff_lines(nil), do: []
defp diff_lines(patch) do
patch
|> String.split("\n")
|> Enum.take(@max_diff_lines)
|> Enum.map(&{diff_line_type(&1), &1})
end
defp diff_line_over_limit?(nil), do: false
defp diff_line_over_limit?(patch), do: length(String.split(patch, "\n")) > @max_diff_lines
defp diff_line_type("+++" <> _rest), do: :meta
defp diff_line_type("---" <> _rest), do: :meta
defp diff_line_type("+" <> _rest), do: :add
defp diff_line_type("-" <> _rest), do: :del
defp diff_line_type("@@" <> _rest), do: :hunk
defp diff_line_type(_line), do: :context
defp diff_line_class(:add), do: "text-quote"
defp diff_line_class(:del), do: "text-signal"
defp diff_line_class(:hunk), do: "text-ink-faint"
defp diff_line_class(:meta), do: "text-ink-faint"
defp diff_line_class(:context), do: "text-ink"
defp error_title(:empty_repository), do: "No commits yet"
defp error_title(:not_found), do: "Commit not found"
defp error_title(_other), do: "History unavailable"
defp error_message(:empty_repository),
do: "Nothing has been pushed to this repository yet."
defp error_message(:not_found),
do: "This commit is not reachable from any published branch."
defp error_message(_other),
do: "The commit history could not be read right now. Try again in a moment."
end