Current section

Files

Jump to
medic lib medic cmd.ex
Raw

lib/medic/cmd.ex

defmodule Medic.Cmd do
@moduledoc """
Helpers for running commands.
"""
alias Medic.UI
defmodule SystemCmd do
@moduledoc false
@callback cmd(command :: binary(), args :: list(binary()), opts :: list(binary())) :: {Collectable.t(), non_neg_integer()}
def cmd(command, args, opts \\ []), do: impl().cmd(command, args, opts)
defp impl, do: Application.get_env(:medic, :system_cmd, Medic.Cmd.RealSystemCmd)
end
defmodule RealSystemCmd do
@moduledoc false
@behaviour Medic.Cmd.SystemCmd
@impl Medic.Cmd.SystemCmd
def cmd(command, args, opts \\ []), do: System.cmd(command, args, opts)
end
@doc """
Run a command, writing the description, command, and output to stdout.
Returns `:ok` or raises if the command fails.
"""
@spec run!(binary(), binary(), list(binary()), list()) :: :ok
def run!(description, command, args, opts \\ []) do
case run(description, command, args, opts) do
{_output, 0} -> :ok
{_output, status_code} -> raise error_message(command, args, status_code)
end
end
@doc """
Run a command, writing the description, command, and output to stdout.
Returns result of `System.cmd`.
"""
@spec run(binary(), binary(), list(binary()), list()) :: {binary(), integer()}
def run(description, command, args, opts \\ []) do
check = Keyword.get(opts, :check)
if check do
case Medic.Check.run(check) do
{:error, _output, _remedy} ->
IO.puts("")
run(description, command, args, [])
_check ->
IO.puts("")
UI.heading(description, [command | args])
IO.puts("")
{"Already compiled", 0}
end
else
UI.heading(description, [command | args])
result = System.cmd(command, args, opts |> Keyword.merge(into: IO.stream(:stdio, 1)))
IO.puts("")
result
end
end
@doc """
Run a command via `System.cmd`, returning trimmed output and status code
"""
@spec exec(binary(), list(binary()), list(binary())) :: {binary(), non_neg_integer()}
def exec(command, args, opts \\ []) do
{output, status_code} = SystemCmd.cmd(command, args, opts)
{String.trim(output), status_code}
end
@doc """
Run a command via `System.cmd`, returning trimmed output, or raising if the command fails.
"""
@spec exec!(binary(), list(binary()), list(binary())) :: binary()
def exec!(command, args, opts \\ []) do
case exec(command, args, opts) do
{output, 0} -> output
{output, status_code} -> raise error_message(command, args, status_code, output)
end
end
defp error_message(command, args, status_code, output \\ nil) do
maybe_output = if output, do: [":", output], else: nil
["command", command, args, "failed with status code", status_code, maybe_output]
|> Enum.reject(&is_nil(&1))
|> Enum.join(" ")
end
end