defmodule GitGud.Packages.Zot do
@moduledoc """
The small part of zot's API this forge calls *out* to.
Everything else about the OCI registry is inbound — zot asks us for a
token, zot tells us what was pushed. Pruning is the exception: an OCI
version's bytes are zot's, so deleting our mirror row frees nothing
unless zot is told as well.
Auth is the same RS256 token zot already trusts, minted here with a
`delete` scope for the one repository being pruned. The pruner is
server-side code acting as the instance, so it signs its own token
rather than borrowing a user's.
`zot` reclaims the blobs on its own schedule (its GC), so a
successful delete here means "the manifest is gone and the bytes are
now unreferenced", not "the disk is smaller already".
"""
alias GitGud.Packages.Token
alias GitGud.Repositories.Repository
alias GitGud.Repositories.Storage
@doc """
Delete one manifest by digest.
`{:error, :no_registry}` when no OCI registry is configured, which is
a perfectly normal deployment — the pruner treats it as "nothing to
do here" rather than a failure.
"""
@spec delete_manifest(Repository.t(), String.t()) :: :ok | {:error, term()}
def delete_manifest(%Repository{} = repo, digest) when is_binary(digest) do
with {:ok, host} <- registry_host(),
{:ok, path} <- repo_path(repo) do
url = "https://#{host}/v2/#{path}/manifests/#{digest}"
token = Token.issue("gitgud-retention", ["repository:#{path}:delete"])
case Req.delete(url,
headers: [{"authorization", "Bearer #{token}"}],
receive_timeout: 30_000
) do
# 202 is the spec's answer; 404 means it is already gone, which
# is the state we wanted.
{:ok, %{status: status}} when status in [200, 202, 404] ->
:ok
{:ok, %{status: status, body: body}} ->
{:error, {:zot_status, status, truncate(body)}}
{:error, reason} ->
{:error, {:zot_unreachable, reason}}
end
end
end
defp registry_host do
case Application.get_env(:git_gud, :registry_host) do
host when is_binary(host) and host != "" -> {:ok, host}
_ -> {:error, :no_registry}
end
end
defp repo_path(%Repository{} = repo) do
repo = GitGud.Repo.preload(repo, [:owner, :organization])
{:ok, "#{Storage.repo_handle(repo)}/#{repo.name}"}
rescue
_ -> {:error, :no_namespace}
end
defp truncate(body) when is_binary(body), do: String.slice(body, 0, 200)
defp truncate(body), do: body |> inspect() |> String.slice(0, 200)
end
neiam
/gitgud
Git Gud
public · Issues · Pulls · Labels · Forks · Compare · Actions success · Badges · Packages
⭐
1
Log in to mark this repository.
2.5 KiB · text
History
b868907