Packages

An Elixir library for easily downloading and extracting Git repositories from various hosting services.

Current section

Files

Jump to
gitly lib gitly.ex
Raw

lib/gitly.ex

defmodule Gitly do
@moduledoc """
A module for downloading and extracting Git repositories.
This module provides functionality to download repositories from various Git hosting services,
extract them, and manage the process with various options for caching, retrying, and formatting.
"""
alias Gitly.Parser
alias Gitly.Utils.Archive, as: ArchiveUtils
alias Gitly.Utils.Git, as: GitUtils
@type retry_fun :: (Req.Request.t(), Req.Response.t() | Exception.t() ->
boolean() | {:delay, non_neg_integer()} | nil)
@type retry_delay_fun :: (non_neg_integer() -> non_neg_integer())
@type opts() :: [
force: boolean(),
cache: boolean(),
overwrite: boolean(),
retry: :safe_transient | :transient | retry_fun() | false,
retry_delay: retry_delay_fun(),
retry_log_level: atom() | false,
max_retries: non_neg_integer(),
ref: String.t(),
root: Path.t(),
format: :zip | :tar | :tar_gz | :tgz
]
@doc """
Downloads a repository and returns the path to the extracted archive.
## Parameters
* `repository` - The repository identifier (e.g., "username/repo").
* `opts` - A keyword list of options (see "Options" section).
## Options
* `:force` - If true, force the download of the archive.
* `:cache` - If true, use the local cache to download the archive.
* `:overwrite` - If true, overwrite existing files when extracting the archive.
* `:retry` - Retry options for Req. See https://hexdocs.pm/req/Req.Steps.html#retry/1-request-options
* `:retry_delay` - Function to determine delay between retries. Default is exponential backoff.
* `:retry_log_level` - The log level for retry logs. Set to false to disable logging.
* `:max_retries` - The maximum number of retries before giving up.
* `:ref` - The ref to download from the repository.
* `:root` - The root path to store the archive.
* `:format` - The format of the archive (:zip, :tar, :tar_gz, or :tgz).
## Returns
* `{:ok, path}` where `path` is the location of the extracted repository.
* `{:error, reason}` if the operation fails.
## Examples
iex> Gitly.gitly("iwatakeshi/gitly")
{:ok, "/path/to/extracted/repo"}
"""
@spec gitly(binary(), opts()) :: {:ok, Path.t()} | {:error, String.t()}
def gitly(repository, opts \\ []) do
with {:ok, source} <- download(repository, opts),
{:ok, dest} <- extract(source, opts) do
{:ok, dest}
with {:ok, result} <- File.ls(dest) do
case result do
[dir] -> {:ok, Path.join(dest, dir)}
_ -> {:ok, dest}
end
else
# coveralls-ignore-next-line
{:error, reason} -> {:error, reason}
end
else
# coveralls-ignore-next-line
{:error, reason} -> {:error, reason}
end
end
@doc """
Downloads a repository and returns the path to the archive.
## Parameters
* `repository` - The repository identifier (e.g., "username/repo").
* `opts` - A keyword list of options (see "Options" section in `gitly/2`).
## Returns
* `{:ok, path}` where `path` is the location of the downloaded archive.
* `{:error, reason}` if the download fails.
## Examples
iex> Gitly.download("iwatakeshi/gitly")
{:ok, "/path/to/downloaded/archive.zip"}
"""
@spec download(binary(), opts()) :: {:error, any()} | {:ok, binary()}
def download(repository, opts \\ []) do
with {:ok, result} <- Parser.parse(repository, Enum.into(opts, %{})),
url <- ArchiveUtils.build_archive_url(result),
path <- build_archive_path(result, opts),
task_opts <- Keyword.merge([url: url, path: path], opts),
tasks <- Gitly.Task.Builder.build_task_order(task_opts),
result <- Gitly.Task.run_until_success(tasks) do
case result do
%{error: [], result: [_]} -> {:ok, path}
%{error: [_], result: [_]} -> {:ok, path}
%{error: errors, result: []} -> {:error, errors}
end
else
# coveralls-ignore-next-line
{:error, reason} -> {:error, reason}
end
end
@doc """
Extracts an archive to a given destination.
## Parameters
* `path` - The path to the archive file.
* `opts` - A keyword list of options (see "Options" section in `gitly/2`).
## Returns
* `{:ok, path}` where `path` is the location of the extracted contents.
* `{:error, reason}` if the extraction fails.
## Examples
iex> Gitly.extract("/path/to/archive.zip")
{:ok, "/path/to/extracted/contents"}
"""
@spec extract(Path.t(), opts()) :: {:ok, Path.t()} | {:error, String.t()}
def extract(path, opts) when is_binary(path) and is_list(opts),
# coveralls-ignore-next-line
do: ArchiveUtils.extract(path, ArchiveUtils.Type.trim_extension(path), opts)
@doc """
Extracts an archive to a specified destination.
## Parameters
* `path` - The path to the archive file.
* `dest` - The destination path for extraction.
* `opts` - A keyword list of options (see "Options" section in `gitly/2`).
## Returns
* `{:ok, path}` where `path` is the location of the extracted contents.
* `{:error, reason}` if the extraction fails.
## Examples
iex> Gitly.extract("/path/to/archive.zip", "/path/to/destination")
{:ok, "/path/to/destination"}
"""
@spec extract(Path.t(), Path.t(), opts()) :: {:ok, Path.t()} | {:error, String.t()}
def extract(path, dest, opts),
# coveralls-ignore-next-line
do: ArchiveUtils.extract(path, dest, opts)
@doc false
@spec build_archive_path(map(), opts()) :: Path.t()
defp build_archive_path(input, opts),
do:
input
|> GitUtils.create_repo_url()
|> URI.parse()
|> Map.merge(input)
|> ArchiveUtils.create_archive_path(opts)
end