Today, if you want to define and use a Mix task in your project itself, you need to make sure to call "compile" or "app.start" before. The issue with this approach is that, if you update the task itself, calling "compile" will re-compile the task but not update the version currently running.
In other words, this is not enough:
defmodule Mix.Tasks.MyTask do
use Mix.Task
def run(argv) do
Mix.Task.run("compile")
# task code
end
end
To fix it, you need to do:
defmodule Mix.Tasks.MyTask do
use Mix.Task
def run(argv) do
Mix.Task.run("compile")
__MODULE__.compiled_run(args)
end
def compiled_run(args) do
# task code
end
end
To make this easier, my suggestion is to add a @requirements annotation of tasks that should be run before the current one. So the outcome is this:
defmodule Mix.Tasks.MyTask do
use Mix.Task
@requirements "compile"
# or: @requirements ["compile", "app.start"]
def run(argv) do
# task code
end
end
It is a small amount of indirection but it will yield actual improvements to those writing their own tasks.
The issue with this approach is that, if you update the task itself, calling "compile" will re-compile the task but not update the version currently running
Oh, I shot myself in the foot because of this several times before, thank you for the workaround 👍
So the outcome is this
What if I wanted to run a mix task with arguments as a requirement, how would I be supposed to specify it? I see several options:
"compile --warnings-as-errors" — need to split this string to separate the task name from the arguments, probably need to deal with shell quoting/escaping while splitting too.["compile", "--warnings-as-errors"] — this would conflict with the suggested way of specifying multiple requirements.Register @requirements as accumulative, and specify a single task requirement (with optional arguments) at a time:
@requirements ["compile", "--warnings-as-errors"]
@requirements "app.start"
This looks kinda verbose and error-prone.
{"compile", ["--warnings-as-errors"]} — my favorite so far.
"compile --warnings-as-errors"— need to split this string to separate the task name from the arguments, probably need to deal with shell quoting/escaping while splitting too.
this syntax is the same as in aliases:
defp aliases() do
[
test: ["compile --warnings-as-errors", "test"]
]
end
so that would be my first choice but option 4. sounds good to me too.
this syntax is the same as in aliases
Good point, I've looked up how Mix handles aliases and it uses OptionParser.split/1 under the hood, so splitting is already covered. Given that, I now think it makes sense to use the first option too.
I vote for option 1 too, I think it's the most intuitive since it's already present in the Elixir landscape. Thanks for bringing up those points @smaximov! 💟
Most helpful comment
this syntax is the same as in aliases:
so that would be my first choice but option 4. sounds good to me too.