How to run custom tasks in production by manually calling them?

I compile and deploy my Phoenix projects manually, with no third-party library such distillery, and with a help of the commands “mix compile” and “mix release”, basically.

I need a way to run custom commands or tasks on a server in the production mode. It’d be similar to running migrations. But I’ve bewildered as for how it’s done. A project itself may or may not be up and running when a command or task is being executed. And I need to be able to pass some arguments to a said command-task.

How to do it?

  1. I’m aware of

defmodule Mix.Tasks.MyTask1 do .... but how would I do it in production, on a server? Is this even a proper and recommended way to run them on a server in production?

  1. I’m aware of bin/my_app eval 'MyProject.MyTask1' but why does it exist if there’s the other abovementioned approach? Again, is this one a proper and recommended way to run them on a server in production?

  2. Are there other approaches?

I’m bewildered. Which one to use, in what circumstances? How to use them properly?

Your second option (/bin/app eval …) looks right. Mix is a build tool and is not a part of your release. There’s a nice example of how this can be done in the Phoenix “Deploying with releases” docs.

For passing arguments, you should be able to just pass them as arguments to the command you’re running — the mix release docs have the example of bin/RELEASE_NAME eval "IO.puts(:hello)".

That doesn’t answer the question of “how to pass an argument to my custom function”

bin/RELEASE_NAME eval "IO.puts(:hello)"

because it passes some code to eval

For instance:

``

defmodule MyApp.Release do
  @app :my_app

  def abc(a1, b1, c1) do
    # [.......]
  end

How to pass a1, b1, c1 to abc() in

$ _build/prod/rel/my_app/bin/my_app eval "MyApp.Release.abc ???"

?

Just pass them? Eg

$ _build/prod/rel/my_app/bin/my_app eval "MyApp.Release.abc(1, \"str\", :atom)"

You’re basically calling a function the same way as you would in IEx, so you can pass any elixir data type.

1 Like

Add-on to this which is not super clear from this section of the docs:

I was trying to forward command-line arguments directly to my Elixir code being run. Doing it the naive way doesn’t work for arbitrary unexpected numbers of arguments and other reasons:

$ _build/prod/rel/my_app/bin/my_app eval "MyApp.Release.abc(\"$1\", \"$2\")" # this doesn't even properly escape double quotes, and ways to do that within bash are not consistent across systems

There is a supported way to forward the command line arguments though:

$ _build/prod/rel/my_app/bin/my_app eval "MyApp.Release.abc" "$@"

The command line arguments can then be accessed in your Elixir code using System.argv(). Hope this helps anyone facing the same problem. This seems like a better solution to me for anything more complex than passing hard-coded values from the shell script.