8
votes

I would like to run a mix task from within a custom mix task.

Something like

def run(_) do
  Mix.Shell.cmd("mix edeliver build release")
  #do other stuff

But I cannot figure out how to execute a shell command. If there is any easier way (besides just making a bash script), let me know.

4

4 Answers

13
votes

Shell is the redundant link here. If you want to run edeliver task, run Mix.Tasks.Edeliver#run:

def run(_) do
  Mix.Tasks.Edeliver.run(~w|build release|)
  # do other stuff
3
votes

Mix.Task.run("edeliver build release") works

1
votes

For executing shell comand you can use Loki. You can find functions for shell execution execute/1.

And example how I used in Mix.Task for executing other mix tasks:

defmodule Mix.Tasks.Sesamex.Gen.Auth do
  use Mix.Task

  import Loki.Cmd
  import Loki.Shell

  @spec run(List.t) :: none()
  def run([singular, plural]) do

    execute("mix sesamex.gen.model #{singular} #{plural}")
    execute("mix sesamex.gen.controllers #{singular}")
    execute("mix sesamex.gen.views #{singular}")
    execute("mix sesamex.gen.templates #{singular}")
    execute("mix sesamex.gen.routes #{singular}")

    # ...
  end
end

Or just look how it execute command:

@spec execute(String.t, list(Keyword.t)) :: {Collectable.t, exit_status :: non_neg_integer}
def execute(string, opts) when is_bitstring(string) and is_list(opts) do
  [command | args] = String.split(string)
  say IO.ANSI.format [:green, " *   execute ", :reset, string]
  System.cmd(command, args, env: opts)
end

Hope it help you.

0
votes

While I never tried running a mix task via Mix.shell.cmd from inside another mix task and I'm not sure if it's best practice, it seems like something like what you're aiming for would work:

def run(args) do
  Mix.Shell.cmd("mix test", fn(output) -> IO.write(output) end)
  # (...)
end

The above code does run the tests via mix test and prints out their output. Note: the above code is based on Mix 1.3.4, the interface is slightly different in 1.4.0.

What might be a more elegant approach though would be creating a mix alias for a "composite" task, consisting of the tasks you depend on and your custom one:

# inside mix.exs
def project do
  [
    # (...)
    aliases: [
      "composite.task": [
        "test",
        "edeliver build release",
        "my.custom.task",
      ] 
    ]
  ]
end

Now running mix composite.task should run the two other tasks before my.custom.task.