As part of automating my release handling, I’ve been fiddling with ways of pushing the git tag into the version used in mix.exs and wondered if anybody else has come up with a better / nicer way:
defmodule Foo.MixProject do
use Mix.Project
def project() do
[version: set_version(),
...]
end
# stash the tag so that it's rolled into the next commit and therefore
# available in hex packages when git tag info may not be present
# alternatively we *could* abuse hex_metadata.config for that?
defp set_version() do
v = get_version()
File.write!(".version", v)
v
end
defp get_version() do
# get version from closest git tag, last saved tag, or assume 0.0.0-alpha
get_version(File.read(".version"), File.dir?(".git"), System.find_executable("git"))
|> String.replace_prefix("v", "")
|> String.trim_trailing()
end
# fallback when there is no actual error, just missing information
defp get_version(:missing), do: "0.0.0-alpha"
# no .version file, must be first run: assume lowest possible version
defp get_version({:error, _}, _, _), do: get_version(:missing)
# .version exists, but no .git dir, probably inside hex package
defp get_version({:ok, v}, false, _), do: v
# .version exists, and we can read git tags
defp get_version({:ok, _}, true, git) when is_binary(git) do
case System.cmd("git", ~w[describe --dirty --abbrev=0 --tags --first-parent],
stderr_to_stdout: true
) do
{v, 0} -> v
_ -> get_version(:missing)
end
end
# something is very wrong so we give up and hex publishing will fail
defp get_version(_, _, _), do: "unknown"
Nice. I really miss it when having to work in Elixir and think mix should have this built in for both applications and releases like rebar3 does.
The only “issue” I know some have pointed out, and may be part of why they don’t want to support it directly, is that when not directly on a tag the versions are not proper “semver”… but now that I check the semver.org page I don’t think that is actually the case.
I remember someone saying that the +build that rebar3 adds wasn’t proper because it was put on the last tag while the correct semver, since the code being built is now beyond the version of the tag, would be a higher version and +buildbeing a prerelease of it.
But according the semver.org the build metadata is not related to prereleases and is to not be taken into account for precedence of versions.
Yeah IMO this should be built in. I added support for this to erlang.mk years ago.
I have 2 things I wanted to do differently, in the App Spec http://erlang.org/doc/man/app.html the id field is not actually used by OTP, and we could put the full description in there. The only thing stopping me is I can’t find how to get that data at runtime - I need to put in various places the particular version I’m using so we can see them in rabbitmq & http headers for traceability. If I could get that working, I’d leave the semver-ish version alone.
Everybody who has lived in a non-Latin alphabet country has already given up on lexicographic sorting ;-).
defp version do
case System.cmd(
"git",
~w[describe --dirty=+dirty],
stderr_to_stdout: true
) do
{version, 0} ->
version
|> Version.parse()
|> bump_version()
|> to_string()
_ ->
"0.0.0-dev"
end
end
defp bump_version(%Version{pre: []} = version), do: version
defp bump_version(%Version{patch: p} = version),
do: struct(version, patch: p + 1)
This will provide proper SemVer package (removing v prefix should be easier than in your version) and will also bump version if it isn’t on the tag.