defmodule Cmder do
@moduledoc """
Cmder is a runner for generic commands.
## Profiles
You can define multiple cmder profiles.
config :cmder,
tailwindcss: [
cmd: "tailwindcss"
path: "/usr/bin",
args: ~w(--input=css/app.css --output=../priv/static/assets/app.css --postcss),
cd: Path.expand("../assets", __DIR__),
env: %{"NODE_ENV" => "development"}
]
If no cmd is given the profile name is being used as command.
"""
use Application
require Logger
@doc false
def start(_, _) do
Supervisor.start_link([], strategy: :one_for_one)
end
@doc """
Runs the given profile with `args`.
The given args will be appended to the configured args.
The task output will be streamed directly to stdio. It
returns the status of the underlying call.
"""
def run(profile, extra_args) when is_atom(profile) and is_list(extra_args) do
config = config_for!(profile)
cmd = config[:cmd] || Atom.to_string(profile)
args = config[:args] || []
opts = [
cd: config[:cd] || File.cwd!(),
env: config[:env] || %{},
into: IO.stream(:stdio, :line),
stderr_to_stdout: true
]
cmd
|> bin_path(config)
|> System.cmd(args ++ extra_args, opts)
|> elem(1)
end
defp config_for!(profile) when is_atom(profile) do
Application.get_env(:cmder, profile) ||
raise ArgumentError, """
unknown cmder profile. Make sure the profile is defined in your config/config.exs file, such as:
config :cmder,
#{profile}: [
cmd: "#{profile}"
path: "/usr/bin",
args: ~w(--input=css/app.css --output=../priv/static/assets/app.css --postcss),
cd: Path.expand("../assets", __DIR__),
env: %{"NODE_ENV" => "development"}
]
"""
end
defp bin_path(cmd, config) do
path = config[:path] || ""
Path.join(path, cmd)
end
end