Is there an elegant way to ensure that a Mix.Task is compiled only in a specific environment?

Hello everyone,

I wrote a Mix.Task that’s supposed to be run in a specific environment (:test) and nowhere else.
The task uses dependencies that are present only in the test environment.

I can set preferred_cli_env to make sure that the task automatically runs in the test environment when I invoke it, so that’s good.

However, when I compile my app in another environment (e.g. :dev) mix will try to compile that task, resulting in errors or at the very least compiler’s warnings due to the missing dependencies.

Is there an elegant way to solve this problem? I can think of a few options:

  • Make the test-only dependencies used by the task available in all environments
  • Enclose the code in my task in a huge if Mix.env() == :test do ... end clause
  • Split tasks into directories: (i.e. task/test, task/dev etc.) and play around with the elixirc_paths option to disable compilation in specific environments

The last option is the only one appealing to me, but I think elixirc_paths only accepts a list of directories, and since lib is already in there, the tasks would have be moved out of lib. I wish there was a way to selectively exclude a directory, or even better a single file, but I believe that’s not possible.

Any ideas? Thank you very much in advance! :slight_smile:

2 Likes

You can always wrap the entire module code? :man_shrugging:t3:

if Mix.env() == :dev do

defmodule XYZ do
  # ...
end

end
1 Like

Option 4: Make the mix task not fail in other envs by just printing an error message instead of doing what it normally does.

1 Like

@LostKobrakai thanks! The problem is getting rid of the compile-time warnings caused by the non-existent dependencies.

Yep, that’s probably the best solution so far. I find it ugly, but I guess I’ll go for it :slight_smile: thanks

I was wondering if there is a way to configure the directory where tasks should reside. It looks like it has to be “lib/mix/tasks”.

That would be an elegant solution: having different task folders.

  • “lib/mix/tasks”
  • “test/mix/tasks”

That way the test tasks would only be compiled in the test environment

A cleaner solution could be to use the elixirc_paths option from mix.exs:

elixirc_paths: elixirc_paths(Mix.env())

And then further below:

defp elixirc_paths(:staging), do: ["lib", "test/support", "test/integration"]
defp elixirc_paths(:test), do: ["lib", "test/support"]
defp elixirc_paths(_), do: ["lib"]

That way you can encapsulate functionality in directories and wall it.

I mentioned this in my first message.

The problem is that you can’t easily exclude/include subfolders of lib (where the tasks are located). You either compile everything under lib, or nothing.

And moving tasks to different directories doesn’t appear to be supported

Yeah, if you can encapsulate then you can use this but if you can’t, it’s best if you go for good old if Mix.env() == ... :slight_smile:

yes good old if is always the solution to most of our problems! :laughing:

This should work. Elixir generally does not care which files define a module.

2 Likes

Yep it does. I had tried it before and it didn’t work as soon as I’d move the tasks outside of the lib folder (for example under test/tasks), but that was because I had simply forgotten to add the new tasks directory to the elixirc_paths for the test environment.

Cool, so now I can put the test tasks under test/tasks and everything’s nice and clean again.

Thanks for the hint!