Packages
rebar3_hex
7.0.2
7.1.0
7.0.11
7.0.10
7.0.9
7.0.8
7.0.7
7.0.6
7.0.5
7.0.4
7.0.3
7.0.2
7.0.1
7.0.0
6.11.9
6.11.8
6.11.7
6.11.6
6.11.5
6.11.4
6.11.3
6.11.2
6.11.1
6.11.0
6.10.3
6.10.2
6.10.1
6.10.0
6.9.6
6.9.5
6.9.4
6.9.3
6.9.2
6.9.1
6.9.0
6.8.0
6.7.0
6.6.0
6.5.0
6.4.0
6.3.0
6.2.0
6.1.0
6.0.0
4.1.0
4.0.0
3.1.0
3.0.0
2.5.1
2.5.0
2.4.0
2.3.0
2.2.0
2.1.0
2.0.0
1.20.0
1.19.0
1.18.0
1.17.0
1.16.0
1.15.0
1.14.0
1.13.0
1.12.0
1.11.0
1.10.0
1.9.1
1.9.0
1.8.1
1.8.0
1.7.2
1.7.1
1.7.0
1.6.3
1.6.1
1.6.0
1.5.0
1.4.0
1.3.0
1.2.0
1.1.0
0.9.0
0.8.0
0.7.0
0.6.0
0.5.1
0.5.0
0.4.0
0.3.0
0.2.0
0.1.0
Hex.pm plugin for rebar3
Current section
Files
Jump to
Current section
Files
src/rebar3_hex_cut.erl
%% @doc `rebar3 hex cut' - Publish with git tag and version bump features
%%
%% Increments tags and publishes.
%%
%% Incrementing and tagging are both optional features of this provider.
%%
%% By default you'll be prompted what type of increment to make to the version or it can be supplied as an argument to
%% the command `-i', `--increment' switch.
%%
%% `cut' will also optionally create version bump commit, create a tag with a name corresponding to the new version, and
%% prompt you if you'd like to push the tag up to your git repository.
%%
%%
%% Below is a full example of `cut' in action :
%%
%% ```
%% rebar3 hex cut
%% ===> Analyzing applications...
%% ===> Verifying dependencies...
%% Select semver increment or other (Current 0.1.3):
%% 1) patch
%% 2) minor
%% 3) major
%% 4) other
%% [1-4] > 1
%% Create 'v0.1.4' commit? ("Y")>
%% Push master to origin master? ("N")>
%% Local Password:
%% Publishing myapp 0.1.4 to hexpm:myrepo
%% Description: My Application
%% Dependencies:
%% relx ~>4.5.0
%% Included files:
%% LICENSE
%% README.md
%% rebar.config
%% src/myapp.app.src
%% src/myapp.erl
%% Licenses: Apache-2.0
%% Links:
%% github: https://github.com/myname/myapp
%% Build tools: rebar3
%% Before publishing, please read Hex CoC: https://hex.pm/policies/codeofconduct
%% Proceed (with warnings)? ("Y")>
%% ===> Published myapp 0.1.4
%% ===> Analyzing applications...
%% ===> Compiling myapp
%% ===> Running edoc for myapp
%% ===> Running ex_doc for myapp
%% ===> Published docs for myapp 0.1.4
%% Create new git tag v0.1.4? ("Y")>
%% ===> Creating new tag v0.1.4...
%% Push new tag to origin? ("Y")>
%% ===> Pushing new tag v0.1.4...
%% '''
%%
%% <ul>
%% <li>`--repo' - Specify the repository to work with. This option is required when
%% you have multiple repositories configured, including organizations. The argument must
%% be a fully qualified repository name (e.g, `hexpm', `hexpm:my_org', `my_own_hexpm').
%% Defaults to `hexpm'.
%% </li>
%% <li>`-i', `--increment' - Specify the type of version increment to perform without being prompted at runtime.
%% Supported types are :
%% <ul>
%% <li><b>major</b></li>
%% <li><b>minor</b></li>
%% <li><b>patch</b></li>
%% </ul>
%% </li>
%% </ul>
-module(rebar3_hex_cut).
-export([init/1,
do/1,
format_error/1]).
-include("rebar3_hex.hrl").
-define(PROVIDER, cut).
-define(DEPS, [{default, lock}]).
%% ===================================================================
%% Public API
%% ===================================================================
%% @private
-spec init(rebar_state:t()) -> {ok, rebar_state:t()}.
init(State) ->
Provider = providers:create([
{name, ?PROVIDER},
{module, ?MODULE},
{namespace, hex},
{bare, true},
{deps, ?DEPS},
{example, "rebar3 hex cut"},
{short_desc, "Increment version number and publish package"},
{desc, ""},
{opts, [{increment, $i, "increment", {string, undefined},
"Type of semver increment: major, minor or patch"},
rebar3_hex:repo_opt()]}
]),
State1 = rebar_state:add_provider(State, Provider),
{ok, State1}.
%% @private
-spec do(rebar_state:t()) -> {ok, rebar_state:t()}.
do(State) ->
case rebar3_hex:task_state(State) of
{ok, Task} ->
handle_task(Task);
{error, Reason} ->
?RAISE(Reason)
end.
%% default path - maybe tag, maybe commit, maybe push commit, publish, maybe push tag
handle_task(#{args := Args} = Task) ->
#{repo := Repo, state := State, apps := Apps} = Task,
Selected = rebar3_hex_io:select_apps(Apps),
lists:foreach(fun(App) -> cut(State, Repo, App, Args) end, Selected),
{ok, State}.
%% @private
-spec format_error(any()) -> iolist().
format_error({no_write_key, RepoName}) ->
io_lib:format("No api key with permissions to write to the repository ~ts was found.", [RepoName]);
format_error({bad_increment, Type}) ->
io_lib:format("Increment must be major, minor or patch. ~s is not valid.", [Type]);
format_error({invalid_semver, Version}) ->
Err = "non-semantic version number \"~ts\" found",
io_lib:format(Err, [Version]);
format_error(Reason) ->
rebar3_hex_error:format_error(Reason).
%% ===================================================================
%% Public API
%% ===================================================================
cut(State, Repo, App, #{increment := MaybeType} = Args) ->
{Version, ResolvedVersion} = version_info(State, App),
Type = get_increment(MaybeType, ResolvedVersion),
NewVersion = increment_version(Type, ResolvedVersion),
AppSrcFile = rebar_app_info:app_file_src(App),
case Version of
_Git when Version =:= git orelse Version =:= "git" ->
create_tag(NewVersion),
case try_publish(State, Repo, App, Args) of
{ok, _State} ->
case rebar3_hex_io:ask("Push new tag to origin?", boolean, "Y") of
true ->
maybe_push_tag(Version),
{ok, State};
false ->
{ok, State}
end;
_ ->
delete_tag(NewVersion),
{ok, State}
end;
_ ->
Spec = rebar3_hex_file:update_app_src(App, NewVersion),
NewAppSrcFile = io_lib:format("~tp.\n", [Spec]),
ok = rebar_file_utils:write_file_if_contents_differ(AppSrcFile, NewAppSrcFile),
ask_commit_and_push(NewVersion),
case try_publish(State, Repo, rebar_app_info:original_vsn(App, NewVersion), Args) of
{ok, _} ->
maybe_create_and_push_tag(NewVersion),
{ok, State};
{error, publish_failed} ->
delete_tag(NewVersion),
{ok, State}
end
end.
maybe_create_and_push_tag(Version) ->
PromptStr = io_lib:format("Create new git tag v~s?", [Version]),
case rebar3_hex_io:ask(PromptStr, boolean, "Y") of
true ->
create_tag(Version),
maybe_push_tag(Version);
false ->
ok
end.
create_tag(Version) ->
rebar_api:info("Creating new tag v~s...", [Version]),
rebar_utils:sh(io_lib:format("git tag v~s", [Version]), []).
maybe_push_tag(Version) ->
case rebar3_hex_io:ask("Push new tag to origin?", boolean, "Y") of
true ->
push_tag(Version);
false ->
ok
end.
push_tag(Version) ->
rebar_api:info("Pushing new tag v~s...", [Version]),
rebar_utils:sh(io_lib:format("git push origin v~s", [Version]), []).
delete_tag(Version) ->
rebar_api:info("Deleting new tag v~s...", [Version]),
rebar_utils:sh(io_lib:format("git tag -d v~s", [Version]), []).
try_publish(State, Repo, App, Args) ->
try rebar3_hex_publish:publish(State, Repo, App, Args) of
{ok, _} ->
{ok, State}
catch
error:{error, {rebar3_hex_publish, Err}} ->
ErrStr = rebar3_hex_publish:format_error(Err),
rebar_api:error(ErrStr, []),
{error, publish_failed}
end.
increment_version(Type, VersionStr) when is_list(VersionStr) orelse is_binary(VersionStr) ->
Parsed = parse_version(VersionStr),
increment_version(Type, Parsed);
increment_version(Type, Version) ->
rebar3_hex_version:format(rebar3_hex_version:increment(Type, Version)).
get_increment(undefined, Version) ->
rebar3_hex_io:say("Select semver increment or other (Current ~s):", [Version]),
rebar3_hex_io:say("1) patch", []),
rebar3_hex_io:say("2) minor", []),
rebar3_hex_io:say("3) major", []),
rebar3_hex_io:say("4) other", []),
case rebar3_hex_io:ask("[1-4] ", number) of
4 ->
rebar3_hex_io:ask("New Version ", string);
TypeInt ->
case int_to_increment(TypeInt) of
error ->
rebar_api:error("Invalid number given, try again~n", []),
get_increment(undefined, Version);
Type ->
Type
end
end;
get_increment(Type, _) -> normalize_increment(Type).
parse_version(Version) ->
case rebar3_hex_version:parse(Version) of
{ok, Parsed} ->
Parsed;
_Err ->
?RAISE({invalid_semver, Version})
end.
int_to_increment(1) -> patch;
int_to_increment(2) -> minor;
int_to_increment(3) -> major;
int_to_increment(_) -> error.
version_info(State, App) ->
Version = rebar_app_info:original_vsn(App),
Resolved = rebar3_hex_app:vcs_vsn(State, App),
{Version, Resolved}.
normalize_increment("patch") -> patch;
normalize_increment("minor") -> minor;
normalize_increment("major") -> major;
normalize_increment(_) -> error.
ask_commit_and_push(NewVersion) ->
case rebar3_hex_io:ask(io_lib:format("Create 'v~s' commit?", [NewVersion]), boolean, "Y") of
true ->
rebar_utils:sh(io_lib:format("git commit -a -m 'v~s'", [NewVersion]), []),
case rebar3_hex_io:ask("Push master to origin master?", boolean, "N") of
true ->
rebar_utils:sh("git push origin master:master", []);
false ->
ok
end;
false ->
ok
end.