Packages

A Semver manager for making updates to your `mix.exs` versions.

Current section

Files

Jump to
semver_mgr lib mix tasks semver.increment.ex
Raw

lib/mix/tasks/semver.increment.ex

defmodule Mix.Tasks.Semver.Increment do
@moduledoc """
The semver mix task: `mix semver.increment`
"""
use Mix.Task
@semver_line_rx ~r/version: "(\d+)\.(\d+)\.(\d+)(-.*)?",/
@semver_field_pos 0
@semver_field_default "patch"
@increment_amount_pos 1
@increment_amount_default 1
@valid_semver_fields ["major", "minor", "patch"]
@shortdoc "Default task will increment the semver patch version"
@doc """
Increment the appropriate field on your project version using Semantic Versioning.
MAJOR.MINOR.PATCH
Incrementing a greater value will reset the lower fields unless the --preserve-lower flag is set.
## Options
* `patch` (default)
* `minor`
* `major`
* --preserve [optional csv separated list to preserve (currently only `patch` and `minor`)]
Used on its own, only the specified field will be incremented.
## Examples
$ mix semver.increment
Incremented 0.1.0 -> 0.1.1
$ mix semver.increment patch
Incremented 0.1.1 -> 0.1.2
$ mix semver.increment minor 3
Incremented 0.1.2 -> 0.4.0
$ mix semver.increment patch 2
Incremented 0.4.0 -> 0.4.2
$ mix semver.increment major --preserve patch
Incremented 0.4.2 -> 1.0.2
"""
def run(args) do
{field, amt, opts} = parse_semver_args(args) |> dbg()
{:ok, mix_data} = File.read("mix.exs")
semver = get_semver(mix_data)
next_semver = increment(semver, field, amt, opts)
new_mix_data =
String.replace(mix_data, @semver_line_rx, "version: \"#{Version.to_string(next_semver)}\",")
IO.puts("Incremented #{semver} -> #{next_semver}")
File.write!("mix.exs", new_mix_data)
:ok
end
@spec increment(Version.t(), atom(), integer(), keyword()) :: Version.t()
defp increment(semver, field, amount, opts)
defp increment(semver = %Version{}, :patch, amount, _opts) do
%Version{semver | patch: semver.patch + amount}
end
defp increment(semver = %Version{}, :minor, amount, opts) do
next_patch = maybe_reset(semver, :patch, Keyword.get(opts, :preserve))
next_minor = semver.minor + amount
%Version{semver | minor: next_minor, patch: next_patch}
end
defp increment(semver = %Version{}, :major, amount, opts) do
next_patch = maybe_reset(semver, :patch, Keyword.get(opts, :preserve))
next_minor = maybe_reset(semver, :minor, Keyword.get(opts, :preserve))
next_major = semver.major + amount
%Version{semver | major: next_major, patch: next_patch, minor: next_minor}
end
defp coerce_custom_opts({p, nil}) when p in ["--preserve", "-p"],
do: [preserve: [:patch, :minor]]
defp coerce_custom_opts({p, fields}) when p in ["--preserve", "-p"] do
IO.puts("handling custom")
fields =
fields
|> String.split(",", trim: true)
|> Enum.map(&String.downcase/1)
|> Enum.map(&String.to_atom/1)
[preserve: fields]
end
defp coerce_custom_opts(opt), do: opt
defp get_semver(mix_data) do
[semver_line | _] = Regex.run(@semver_line_rx, mix_data)
semver_line
|> String.replace("version: ", "")
|> String.replace("\"", "")
|> String.replace(",", "")
|> Version.parse!()
end
defp maybe_reset(_semver, _field, nil), do: 0
defp maybe_reset(semver, field, preserve_list) do
if field in preserve_list, do: Map.get(semver, field), else: 0
end
defp parse_semver_args(args) do
{parsed, args, invalid} =
OptionParser.parse(args,
strict: [preserve: :string],
aliases: [p: :preserve]
)
opts =
invalid
|> Enum.map(&coerce_custom_opts/1)
|> Enum.reduce(parsed, &Keyword.merge/2)
|> Keyword.update!(:preserve, fn
nil ->
[]
fields when is_binary(fields) ->
fields
|> String.downcase()
|> String.split(",", trim: true)
|> Enum.map(&String.to_atom/1)
fields when is_list(fields) ->
Enum.map(fields, fn
s when is_binary(s) -> s |> String.downcase() |> String.to_atom()
a when is_atom(a) -> a
end)
end)
field =
case Enum.at(args, @semver_field_pos, @semver_field_default) do
field when field in @valid_semver_fields ->
String.to_atom(field)
_ ->
raise("Invalid semver field selected, must be one of #{inspect(@valid_semver_fields)}")
end
amt =
case Enum.at(args, @increment_amount_pos) do
delta when is_binary(delta) -> String.to_integer(delta)
_ -> @increment_amount_default
end
{field, amt, opts}
end
end