defmodule GitGudWeb.HookController do
@moduledoc """
Receives post-receive payloads from the on-disk git hook.
See `priv/scripts/gitgud-hook`. The request body is the raw stdin of
the hook (`<old> <new> <ref>\\n…`). The repo is identified via the
`x-repo-path: <absolute path>` header.
Loopback-only — bind to 127.0.0.1 in production.
"""
use GitGudWeb, :controller
import Ecto.Query, warn: false
alias GitGud.Repo
alias GitGud.Repositories.HookReceiver
alias GitGud.Repositories.Repository
def post_receive(conn, _params) do
case Plug.Conn.get_req_header(conn, "x-repo-path") do
[path | _] ->
{:ok, body, conn} = Plug.Conn.read_body(conn, length: 1_000_000)
case find_repo(path) do
%Repository{id: rid} ->
_ = HookReceiver.receive(rid, body, pusher_id(conn))
send_resp(conn, 200, "ok\n")
_ ->
send_resp(conn, 404, "unknown repo\n")
end
_ ->
send_resp(conn, 400, "missing x-repo-path\n")
end
end
defp find_repo(path) do
Repo.one(from r in Repository, where: r.disk_path == ^path)
end
# The transport (SSH channel / smart-HTTP CGI) sets GITGUD_PUSHER_ID
# in the git hook environment; `gitgud-hook` forwards it as a header.
# Absent / non-numeric → nil (unattributed push).
defp pusher_id(conn) do
with [val | _] <- Plug.Conn.get_req_header(conn, "x-pusher-id"),
{id, ""} <- Integer.parse(val) do
id
else
_ -> nil
end
end
end
1.5 KiB · text
5af55d9