Current section
Files
Jump to
Current section
Files
lib/vsntool.ex
defmodule Vsntool do
import Vsntool.Util
alias Vsntool.Plugin
@vsntool_version Vsntool.MixProject.project()[:version]
@options %{
"i" => "init",
"bm" => "bump_major",
"bi" => "bump_minor",
"bp" => "bump_patch",
"br" => "bump_rc",
"r" => "release",
"c" => "current",
"l" => "last",
"h" => "help",
"v" => "--version"
}
@shortcuts Map.keys(@options)
@next_commands ~w(next_major next_minor next_patch next_rc)
@commands Map.values(@options) ++ @next_commands
@kinds ~w(major minor patch)
@env Mix.env()
def main(args) do
execute(args)
rescue
e in Vsntool.Flunk ->
IO.write(:stderr, Exception.message(e) <> "\n")
if @env != :test do
System.halt(1)
end
end
def execute([]), do: execute("current", [])
def execute([command | args]) when command in @commands do
execute(command, args)
end
def execute([shortcut | rest]) when shortcut in @shortcuts do
execute([@options[shortcut] | rest])
end
def execute(_), do: execute("usage", [])
def execute("current", []) do
vsn =
case version_from_file!() do
%{pre: ["dev"]} = vsn ->
%{vsn | pre: [hash()]}
_ ->
version_from_git()
end
IO.puts(vsn)
end
def execute("bump_" <> kind, ["--dev"]) when kind in @kinds do
do_bump(kind, ["dev"])
end
def execute("bump_" <> kind, ["--rc"]) when kind in @kinds do
do_bump(kind, ["rc.0"])
end
def execute("bump_" <> kind, []) when kind in @kinds do
do_bump(kind, [])
end
def execute("next_" <> kind, ["--dev"]) when kind in @kinds do
do_next(kind, ["dev"])
end
def execute("next_" <> kind, ["--rc"]) when kind in @kinds do
do_next(kind, ["rc.0"])
end
def execute("next_" <> kind, []) when kind in @kinds do
do_next(kind, [])
end
def execute("next_rc", []) do
assert_release_branch()
vsn = version_from_file!()
pre =
case vsn.pre do
["dev"] ->
["rc", 0]
_other ->
n = rc_number(vsn)
if n == nil do
flunk("Need to be on a RC version number to bump, currently on #{vsn}")
end
["rc", n + 1]
end
IO.puts(%{vsn | pre: pre})
end
def execute("last", []) do
IO.puts(version_from_file!())
end
def execute("bump_rc", []) do
assert_release_branch()
vsn = version_from_file!()
pre =
case vsn.pre do
["dev"] ->
["rc", 0]
_other ->
n = rc_number(vsn)
if n == nil do
flunk("Need to be on a RC version number to bump, currently on #{vsn}")
end
["rc", n + 1]
end
persist_version(%{vsn | pre: pre})
end
def execute("release", []) do
assert_release_branch()
vsn = version_from_file!()
if vsn.pre == [] do
flunk("Not on a pre-release: #{vsn}")
end
vsn = %{vsn | pre: []}
persist_version(vsn)
end
def execute("--version", []) do
IO.puts(@vsntool_version)
end
def execute("help", []) do
help_message = """
Usage: vsntool <command> [options]
Options:
i, init Create a new VERSION file and initialize a git repository
bm, bump_major Bump major version (with --dev option)
bi, bump_minor Bump minor version (with --dev option)
bp, bump_patch Bump patch version (with --dev option)
next_major Print next major version after bump (with --dev or --rc)
next_minor Print next minor version after bump (with --dev or --rc)
next_patch Print next patch version after bump (with --dev or --rc)
next_rc Print next RC version after bump_rc
c, current Current version
l, last Last released version
h, help Display this help
v, --version Display vsntool version
"""
IO.puts(help_message)
end
def execute("usage", []) do
flunk(
"Usage: vsntool (init|bump_major|bump_minor|bump_patch|next_major|next_minor|next_patch|next_rc|current|last|help)"
)
end
def execute("init", []) do
execute("init", [System.get_env("VERSION", "0.0.1")])
end
def execute("init", [initial_version]) do
if File.exists?("VERSION") do
flunk("This project already has a VERSION file")
end
if !File.exists?(".git") do
IO.puts("Initialized git repository")
shell("git init")
end
persist_version(Version.parse!(initial_version))
end
def execute(command, args) do
flunk("Invalid args given to #{command}: #{inspect(args)}")
end
def persist_version(vsn) do
vsn_str = to_string(vsn)
case shell("git tag -l #{vsn_str}") do
"" ->
do_persist_version(vsn)
^vsn_str ->
if commit_for_ref(vsn_str) == head_commit() do
shell("git push")
shell("git push --tags")
IO.puts("Version bump to #{vsn} OK.")
else
flunk("There is already a git tag called #{vsn}")
end
end
end
defp do_persist_version(vsn) do
call_hook("pre_persist", [to_string(vsn)])
File.write!("VERSION", to_string(vsn) <> "\n")
Plugin.discover()
|> Enum.map(fn {plugin, file} ->
IO.puts("* plugin: #{inspect(plugin)} → #{file}")
plugin.persist_version(vsn, file)
end)
shell("git add VERSION")
shell("git commit -n -m 'Bump version to #{vsn}'")
if vsn.pre != ["dev"] do
shell("git tag -a '#{vsn_prefix()}#{vsn}' -m 'Tagged version #{vsn}'")
end
shell("git push")
shell("git push --tags")
IO.puts("Version bump to #{vsn} OK.")
end
defp do_bump(kind, pre) do
assert_release_branch()
vsn = version_from_file!()
if vsn.pre != [] do
flunk("Cannot bump when on prerelease (#{vsn})")
end
version = bump(String.to_atom(kind), vsn)
version = %{version | pre: pre}
persist_version(version)
end
defp do_next(kind, pre) do
assert_release_branch()
vsn = version_from_file!()
if vsn.pre != [] do
flunk("Cannot bump when on prerelease (#{vsn})")
end
version = bump(String.to_atom(kind), vsn)
version = %{version | pre: pre}
IO.puts(version)
end
defp assert_release_branch() do
if branch() not in vsn_branches() do
flunk(
"You need to be on branch #{Enum.join(vsn_branches(), " or ")} to bump versions (currently on #{branch()})"
)
end
end
defp call_hook(name, args) do
hookfile = Path.join([File.cwd!(), ".vsntool/hooks", name])
if File.exists?(hookfile) do
{_, exitcode} = System.cmd(hookfile, args, into: IO.stream())
if exitcode != 0 do
flunk("#{name} hook exited with code #{exitcode}")
end
end
end
end