Skip to content

Commit

Permalink
improvement: handle incrementing rc versions more gracefully
Browse files Browse the repository at this point in the history
  • Loading branch information
zachdaniel committed Oct 4, 2022
1 parent fe2b685 commit 22b2139
Show file tree
Hide file tree
Showing 4 changed files with 62 additions and 38 deletions.
5 changes: 0 additions & 5 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,2 @@
{
"workbench.colorCustomizations": {
"activityBar.background": "#5C043C",
"titleBar.activeBackground": "#810654",
"titleBar.activeForeground": "#FFFBFD"
}
}
32 changes: 27 additions & 5 deletions lib/git_ops/version.ex
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,28 @@ defmodule GitOps.Version do
end)
end

def determine_new_version(current_version, prefix, commits, opts) do
def determine_new_version(current_version, prefix, commits, last_valid_non_rc_version, opts) do
parsed = parse!(prefix, prefix <> current_version)

rc? = opts[:rc]

build = opts[:build]

new_version = new_version(commits, parsed, rc?, opts)
last_valid_non_rc_version =
if last_valid_non_rc_version && prefix && prefix != "" do
String.trim_leading(last_valid_non_rc_version, prefix)
else
last_valid_non_rc_version
end

new_version =
new_version(
commits,
parsed,
rc?,
last_valid_non_rc_version,
opts
)

if versions_equal?(new_version, parsed) && build == parsed.build do
raise """
Expand Down Expand Up @@ -69,18 +83,26 @@ defmodule GitOps.Version do
end)
end

defp new_version(commits, parsed, rc?, opts) do
defp new_version(commits, parsed, rc?, last_valid_non_rc_version, opts) do
pre = default_pre_release(rc?, opts[:pre_release])

last_valid_non_rc_version =
last_valid_non_rc_version && Version.parse!(last_valid_non_rc_version)

cond do
Enum.any?(commits, &Commit.breaking?/1) ->
Enum.any?(commits, &Commit.breaking?/1) &&
!(rc? && last_valid_non_rc_version &&
last_valid_non_rc_version.major != parsed.major) ->
if opts[:no_major] do
%{parsed | minor: parsed.minor + 1, patch: 0, pre: pre}
else
%{parsed | major: parsed.major + 1, minor: 0, patch: 0, pre: pre}
end

Enum.any?(commits, &Commit.feature?/1) ->
Enum.any?(commits, &Commit.feature?/1) &&
!(rc? && last_valid_non_rc_version &&
(last_valid_non_rc_version.major != parsed.major ||
last_valid_non_rc_version.minor != parsed.minor)) ->
%{parsed | minor: parsed.minor + 1, patch: 0, pre: pre}

Enum.any?(commits, &Commit.fix?/1) || opts[:force_patch] ->
Expand Down
1 change: 1 addition & 0 deletions lib/mix/tasks/git_ops.release.ex
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ defmodule Mix.Tasks.GitOps.Release do
current_version,
prefix,
commits_for_version,
GitOps.Version.last_valid_non_rc_version(tags, prefix),
opts
)
end
Expand Down
62 changes: 34 additions & 28 deletions test/version_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,16 @@ defmodule GitOps.Test.VersionTest do

alias GitOps.Version

defp new_version(current_version, commits, opts \\ []) do
defp new_version(current_version, commits, last_valid_non_rc_version \\ nil, opts \\ []) do
{prefix, opts} = Keyword.pop(opts, :prefix)

Version.determine_new_version(current_version, prefix || "", commits, opts)
Version.determine_new_version(
current_version,
prefix || "",
commits,
last_valid_non_rc_version,
opts
)
end

defp minor do
Expand Down Expand Up @@ -55,7 +61,7 @@ defmodule GitOps.Test.VersionTest do
end

test "build metadata can be set with the build option" do
assert new_version("0.1.1", [break()], build: "150") == "1.0.0+150"
assert new_version("0.1.1", [break()], nil, build: "150") == "1.0.0+150"
end

test "attempting to release when no commits would yield a new version number is an error" do
Expand All @@ -65,112 +71,112 @@ defmodule GitOps.Test.VersionTest do
end

test "if changing the build metadata, a non-version change is not an error" do
new_version("0.1.1+10", [chore()], build: "11")
new_version("0.1.1+10", [chore()], nil, build: "11")
end

test "if changing the pre_release, a non-version change is not an error" do
new_version("0.1.1+10", [chore()], pre_release: "alpha")
new_version("0.1.1+10", [chore()], nil, pre_release: "alpha")
end

test "if the force_patch option is present, no error is raised and the version is patched regardless" do
assert new_version("0.1.1", [chore()], force_patch: true) == "0.1.2"
assert new_version("0.1.1", [chore()], nil, force_patch: true) == "0.1.2"
end

test "the force_patch option is present with a minor update results in minor bump" do
assert new_version("0.1.1", [minor()], force_patch: true) == "0.2.0"
assert new_version("0.1.1", [minor()], nil, force_patch: true) == "0.2.0"
end

test "the force_patch option is present on an rc with a minor update results in minor bump" do
assert new_version("0.1.1-rc1", [minor()], force_patch: true) == "0.2.0"
assert new_version("0.1.1-rc1", [minor()], nil, force_patch: true) == "0.2.0"
end

test "the force_patch and rc options are present on an rc with a patch results in an rc bump" do
assert new_version("0.1.1-rc1", [patch()], rc: true, force_patch: true) == "0.1.1-rc2"
assert new_version("0.1.1-rc1", [patch()], nil, rc: true, force_patch: true) == "0.1.1-rc2"
end

test "the force_patch option is present on an rc with a patch results in an patch bump" do
assert new_version("0.1.1-rc1", [patch()], force_patch: true) == "0.1.2"
assert new_version("0.1.1-rc1", [patch()], nil, force_patch: true) == "0.1.2"
end

test "if the no_major option is present, a major change only updates the patch" do
assert new_version("0.1.1", [break()], no_major: true) == "0.2.0"
assert new_version("0.1.1", [break()], nil, no_major: true) == "0.2.0"
end

test "if a pre_release is specified, you get the next version tagged with that pre-release with a minor change" do
assert new_version("0.1.1", [minor()], pre_release: "alpha") == "0.2.0-alpha"
assert new_version("0.1.1", [minor()], nil, pre_release: "alpha") == "0.2.0-alpha"
end

test "if a pre_release is specified, you get the next version tagged with that pre-release with a major change" do
assert new_version("0.1.1", [break()], pre_release: "alpha") == "1.0.0-alpha"
assert new_version("0.1.1", [break()], nil, pre_release: "alpha") == "1.0.0-alpha"
end

test "if a pre_release is performed after a pre_release, and the version would not change then it is unchanged" do
assert new_version("0.1.2-alpha", [patch()], pre_release: "beta") == "0.1.2-beta"
assert new_version("0.1.2-alpha", [patch()], nil, pre_release: "beta") == "0.1.2-beta"
end

test "if a pre_release is performed after a pre_release, and the version would change, then it is changed" do
assert new_version("0.1.2-alpha", [minor()], pre_release: "beta") == "0.2.0-beta"
assert new_version("0.1.2-alpha", [minor()], nil, pre_release: "beta") == "0.2.0-beta"
end

test "if a pre_release is performed after using rc, and the version would change, then it is changed with pre-release" do
assert new_version("0.1.2-rc0", [patch()], pre_release: "alpha") == "0.1.3-alpha"
assert new_version("0.1.2-rc0", [patch()], nil, pre_release: "alpha") == "0.1.3-alpha"
end

test "a release candidate starts at 0 if requested on patch" do
assert new_version("0.1.0", [patch()], rc: true) == "0.1.1-rc.0"
assert new_version("0.1.0", [patch()], nil, rc: true) == "0.1.1-rc.0"
end

test "a release candidate starts at 0 if requested on minor" do
assert new_version("0.1.0", [minor()], rc: true) == "0.2.0-rc.0"
assert new_version("0.1.0", [minor()], nil, rc: true) == "0.2.0-rc.0"
end

test "a release candidate starts at 0 if requested on break" do
assert new_version("0.1.0", [break()], rc: true) == "1.0.0-rc.0"
assert new_version("0.1.0", [break()], nil, rc: true) == "1.0.0-rc.0"
end

test "an old style release candidate increments by one on patch" do
assert new_version("0.1.1-rc0", [patch()], rc: true) == "0.1.1-rc1"
assert new_version("0.1.1-rc0", [patch()], nil, rc: true) == "0.1.1-rc1"
end

test "an old style release candidate increments by one on patch and retains the lack of a dot" do
assert new_version("0.1.1-rc1", [patch()], rc: true) == "0.1.1-rc2"
assert new_version("0.1.1-rc1", [patch()], nil, rc: true) == "0.1.1-rc2"
end

test "a release candidate increments by one on patch" do
assert new_version("0.1.1-rc.0", [patch()], rc: true) == "0.1.1-rc.1"
assert new_version("0.1.1-rc.0", [patch()], nil, rc: true) == "0.1.1-rc.1"
end

test "a release candidate resets on minor" do
assert new_version("0.1.1-rc.0", [minor()], rc: true) == "0.2.0-rc.0"
assert new_version("0.1.1-rc.0", [minor()], nil, rc: true) == "0.2.0-rc.0"
end

test "a release candidate resets on major" do
assert new_version("0.1.1-rc.0", [break()], rc: true) == "1.0.0-rc.0"
assert new_version("0.1.1-rc.0", [break()], nil, rc: true) == "1.0.0-rc.0"
end

test "a release candidate raises correctly when it would not change" do
assert_raise RuntimeError, ~r/No changes should result in a new release version./, fn ->
new_version("0.1.1-rc0", [chore()], rc: true)
new_version("0.1.1-rc0", [chore()], nil, rc: true)
end
end

test "invalid rc number version raises" do
assert_raise RuntimeError, ~r/Found an rc version that could not be parsed/, fn ->
new_version("0.1.1-rca", [patch()], rc: true)
new_version("0.1.1-rca", [patch()], nil, rc: true)
end
end

test "invalid rc label version raises" do
assert_raise RuntimeError, ~r/Found an rc version that could not be parsed/, fn ->
new_version("0.1.1-releasecandidate", [patch()], rc: true)
new_version("0.1.1-releasecandidate", [patch()], nil, rc: true)
end
end

test "no prefix on version raises" do
assert_raise ArgumentError,
~r/Expected: .+ to be parseable as a version, but it was not/,
fn ->
new_version("v0.1.1", [patch()], rc: true, prefix: "vv")
new_version("v0.1.1", [patch()], nil, rc: true, prefix: "vv")
end
end

Expand Down

0 comments on commit 22b2139

Please sign in to comment.