Packages

A Mix Task to upload Distillery releases to GitHub.

Current section

Files

Jump to
duckduck lib duck_duck transform.ex
Raw

lib/duck_duck/transform.ex

defmodule DuckDuck.Transform do
alias DuckDuck.UploadCommand, as: Command
@moduledoc """
Describes a series of transformations to iteratively build an UploadCommand.
"""
@effects Application.get_env(:duckduck, :effects_client, DuckDuck.Effects)
@switches [tag: :string, path: :string, yes: :boolean]
@aliases [t: :tag, f: :path, y: :yes]
@doc "Transform an argument list into a command"
@spec parse([String.t()]) :: Command.t()
def parse(argv) do
{parsed, _rest} =
OptionParser.parse!(argv, switches: @switches, aliases: @aliases)
params =
parsed
|> Enum.map(&translate/1)
|> Enum.into(%{})
Command.transform(%Command{}, params)
end
# translate to the keys of the UploadCommand schema
@spec translate({atom(), any()}) :: {atom(), any()}
defp translate({:yes, accepted?}), do: {:accept?, accepted?}
defp translate({:file, path}), do: {:path, path}
defp translate(pair), do: pair
@doc """
Put the owner in the command if not already there
"""
@spec owner(Command.t()) :: Command.t() | {:error, String.t()}
def owner(command) do
case @effects.fetch_env(:duckduck, :owner) do
{:ok, owner} ->
Command.transform(command, %{owner: owner})
:error ->
{:error, "Couldn't find repo owner in config"}
end
end
@doc """
Put the repo in the command if not already there
"""
@spec repo(Command.t()) :: Command.t() | {:error, String.t()}
def repo(command) do
case @effects.fetch_env(:duckduck, :repo) do
{:ok, repo} ->
Command.transform(command, %{repo: repo})
:error ->
{:error, "Couldn't find repo name in config"}
end
end
@doc """
Put the tag in a command if not already there
"""
@spec tag(Command.t()) :: Command.t()
def tag(command) do
Command.transform(command, %{tag: @effects.get_tag()})
end
@doc """
Put the path to the upload file in the command if not already present
"""
@spec path(Command.t()) :: Command.t() | {:error, String.t()}
def path(%Command{tag: tag} = command) do
case DuckDuck.find_release_file(tag) do
{:ok, file} ->
Command.transform(command, %{path: file})
{:error, _reason} = e ->
e
end
end
@doc """
Put the api token in the command if not already there.
"""
@spec api_token(Command.t()) :: Command.t() | {:error, String.t()}
def api_token(command) do
case @effects.read_api_token() do
{:ok, token} ->
Command.transform(command, %{api_token: token})
{:error, _reason} = e ->
e
end
end
@doc """
Ask the user if they're ok with the upload plan
"""
@spec accept?(Command.t()) :: Command.t()
def accept?(%Command{tag: tag, path: path} = command) do
Command.transform(command, %{accept?: DuckDuck.confirm(path, tag)})
end
@doc """
Find the upload url and put it in the command.
For uploading assets, you need to ask GitHub where to put them through
their API. Interestingly, you can't upload assets to a tag. Only a release
may have assets. So when you want to upload to a tag, you must also create
the release from the tag. You can do this with a single API call.
"""
@spec upload_url(Command.t()) :: Command.t() | {:error, String.t()}
def upload_url(
%Command{api_token: token, owner: owner, repo: repo, tag: tag} = command
) do
if Enum.any?([token, owner, repo, tag], &is_nil/1) do
{:error,
"""
Couldn't find the upload url because I didn't know at least one of
- api token
- repo owner
- repo name
- tag
"""}
else
Command.transform(command, %{
upload_url: DuckDuck.find_upload_url(token, owner, repo, tag)
})
end
end
@doc """
Try uploading the tarball given the information in the command.
"""
@spec upload(Command.t()) :: IO.chardata()
def upload(%Command{path: path, api_token: api_token, upload_url: url}) do
IO.puts("Please wait. Uploading #{path}...")
case DuckDuck.upload(path, api_token, url) do
:ok ->
[:green, "Release successfully uploaded", :reset, "."]
{:error, reason} ->
[:red, reason]
end
end
def upload(%Command{}) do
["Release upload ", :red, "failed", :reset, ".\n"]
end
end