I would like to load my app version from an external VERSION file (so that other CI/CD tools have easy access to the value). I’ve updated my
mix.exs to load the value at compile time (see below), which works great, however
mix.exs doesn’t get recompiled when I modify my external
VERSION file. I’ve added it as an
@external_resource but maybe that module attribute has no effect in
.exs files (I’m not entirely sure if a
.exs script is considered a module) ?
@external_resource version_file = "VERSION"
@version File.read!(version_file) |> String.trim
def project do
I can workaround it by also doing a
touch mix.exs whenever I bump the version number, but was looking to see if there’s anyway to get the
@external_resource attribute to work inside
Hello and welcome,
exs files are for scripting, and are not compiled.
Hmmm, I’m pretty sure that
mix.exs is “compiled” every single time you run a mix command. But what doesn’t happen is that the rest of the project is not recompiled when you change your
VERSION file. Judging from the docs on @external_resource:
Specifies an external resource for the current module.
Sometimes a module embeds information from an external file. This attribute allows the module to annotate which external resources have been used.
Tools like Mix may use this information to ensure the module is recompiled in case any of the external resources change.
I think that what is happening is that since mix.exs is an
.exs file it always needs to be recompiled so adding
@external_resource does not change which files need to be recompiled. And the behavior that you see after a
touch mix.exs is (I think) special behavior specifically for
mix.exs and configuration files. I could be wrong on some of these details, and if so I would be glad for someone to correct me.
Are there other specific files that you want to be recompiled when your
VERSION file changes? One potential solution is to add the
@extrernal_resource to those files directly. Or if you have some other goal then it would be helpful to know exactly what you’re trying to do. If it’s an issue in CI/CD environment maybe it’s best to not cache parts of the build. But it’s a little hard to say without more details.
It is considered a module if there is a
defmodule contained within. But there are a few different behaviors of
.exs file vs
.ex files, for example the
.beam files for
.exs files are not persisted to disk (although they are loaded in memory when the beam is running).
Thanks, that’s a lot of useful info.
My test case was that I compiled and ran
iex -S mix to verify the value for
Application.spec(:my_app)[:vsn] and ensure it contained the value from my
VERSION file - which it did. I then update the external
VERSION file - without touching any other source code - and rerun
iex -S mix - and the value for
Application.spec(:my_app)[:vsn] still contained the previous version. I guess I was expecting the @external_resource attribute to cause mix to recompile whatever it is that generates the
Application.spec(:my_app) keyword list.
I realize as I write this out this probably won’t be a real issue in the long run because - as you mention - once I setup a full CI pipeline I’ll probably be doing a full clean compile each time so this might just be a non-problem in the long run.
Update: I just noticed that if I look at the value of
IEx) then that value does reflect the latest correct value from the
VERSION file. So assuming that
iex -S mix runs the
mix.exs file each time that would make sense…
So I guess that just leaves me a bit confused about the difference between
Application.spec(:my_app, :vsn). The former is updated whenever I touch the
VERSION file but the latter is only updated if (something) gets recompiled.
Can you share the full code where you verified that the version did not change? Perhaps you used a place that was compile time evaluated?
The verification happened in IEx so it was a run time check calling
Dumb question, but have you restarted your
iex after changing the
Yes, that’s what I’m doing - I quit IEx, modify the version file, then start fresh
iex -S mix and inspect both
Application.spec(:my_app, :vsn) - the former shows the updated new VERSION while the latter shows the outdated previous VERSION.