summaryrefslogtreecommitdiff
path: root/src/rebar_git_resource.erl
diff options
context:
space:
mode:
Diffstat (limited to 'src/rebar_git_resource.erl')
-rw-r--r--src/rebar_git_resource.erl146
1 files changed, 146 insertions, 0 deletions
diff --git a/src/rebar_git_resource.erl b/src/rebar_git_resource.erl
new file mode 100644
index 0000000..a6b60d5
--- /dev/null
+++ b/src/rebar_git_resource.erl
@@ -0,0 +1,146 @@
+%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*-
+%% ex: ts=4 sw=4 et
+-module(rebar_git_resource).
+
+-behaviour(rebar_resource).
+
+-export([lock/2
+ ,download/2
+ ,needs_update/2
+ ,make_vsn/1]).
+
+-include("rebar.hrl").
+
+lock(AppDir, {git, Url, _}) ->
+ Ref = string:strip(
+ os:cmd("git --git-dir='" ++ AppDir ++ "/.git' rev-parse --verify HEAD")
+ ,both, $\n),
+ {git, Url, {ref, Ref}}.
+
+%% Return true if either the git url or tag/branch/ref is not the same as the currently
+%% checked out git repo for the dep
+needs_update(Dir, {git, Url, {tag, Tag}}) ->
+ {ok, CurrentUrl} = rebar_utils:sh(?FMT("git config --get remote.origin.url", []),
+ [{cd, Dir}]),
+ {ok, Current} = rebar_utils:sh(?FMT("git describe --tags --exact-match", []),
+ [{cd, Dir}]),
+ Current1 = string:strip(string:strip(Current, both, $\n), both, $\r),
+ CurrentUrl1 = string:strip(string:strip(CurrentUrl, both, $\n), both, $\r),
+ ?DEBUG("Comparing git tag ~s with ~s and url ~s with ~s~n", [Tag, Current1, Url, CurrentUrl1]),
+ not ((Current1 =:= Tag) andalso (CurrentUrl1 =:= Url));
+needs_update(Dir, {git, Url, {branch, Branch}}) ->
+ {ok, CurrentUrl} = rebar_utils:sh(?FMT("git config --get remote.origin.url", []),
+ [{cd, Dir}]),
+ {ok, Current} = rebar_utils:sh(?FMT("git symbolic-ref -q --short HEAD", []),
+ [{cd, Dir}]),
+ Current1 = string:strip(string:strip(Current, both, $\n), both, $\r),
+ CurrentUrl1 = string:strip(string:strip(CurrentUrl, both, $\n), both, $\r),
+ ?DEBUG("Comparing git branch ~s with ~s and url ~s with ~s~n", [Branch, Current1, Url, CurrentUrl1]),
+ not ((Current1 =:= Branch) andalso (CurrentUrl1 =:= Url));
+needs_update(Dir, {git, Url, Ref}) ->
+ case Ref of
+ {ref, Ref1} ->
+ Ref1;
+ Ref1 ->
+ Ref1
+ end,
+ {ok, CurrentUrl} = rebar_utils:sh(?FMT("git config --get remote.origin.url", []),
+ [{cd, Dir}]),
+ {ok, Current} = rebar_utils:sh(?FMT("git rev-parse -q HEAD", []),
+ [{cd, Dir}]),
+ Current1 = string:strip(string:strip(Current, both, $\n), both, $\r),
+ CurrentUrl1 = string:strip(string:strip(CurrentUrl, both, $\n), both, $\r),
+ ?DEBUG("Comparing git ref ~s with ~s and url ~s with ~s~n", [Ref1, Current1, Url, CurrentUrl1]),
+ not ((Current1 =:= Ref1) andalso (CurrentUrl1 =:= Url)).
+
+download(Dir, {git, Url}) ->
+ ?WARN("WARNING: It is recommended to use {branch, Name}, {tag, Tag} or {ref, Ref}, otherwise updating the dep may not work as expected.~n", []),
+ download(Dir, {git, Url, {branch, "HEAD"}});
+download(Dir, {git, Url, ""}) ->
+ ?WARN("WARNING: It is recommended to use {branch, Name}, {tag, Tag} or {ref, Ref}, otherwise updating the dep may not work as expected.~n", []),
+ download(Dir, {git, Url, {branch, "HEAD"}});
+download(Dir, {git, Url, {branch, Branch}}) ->
+ ok = filelib:ensure_dir(Dir),
+ rebar_utils:sh(?FMT("git clone ~s ~s -b ~s --single-branch",
+ [Url, filename:basename(Dir), Branch]),
+ [{cd, filename:dirname(Dir)}]);
+download(Dir, {git, Url, {tag, Tag}}) ->
+ ok = filelib:ensure_dir(Dir),
+ rebar_utils:sh(?FMT("git clone ~s ~s -b ~s --single-branch",
+ [Url, filename:basename(Dir), Tag]),
+ [{cd, filename:dirname(Dir)}]);
+download(Dir, {git, Url, {ref, Ref}}) ->
+ ok = filelib:ensure_dir(Dir),
+ rebar_utils:sh(?FMT("git clone -n ~s ~s", [Url, filename:basename(Dir)]),
+ [{cd, filename:dirname(Dir)}]),
+ rebar_utils:sh(?FMT("git checkout -q ~s", [Ref]), [{cd, Dir}]);
+download(Dir, {git, Url, Rev}) ->
+ ?WARN("WARNING: It is recommended to use {branch, Name}, {tag, Tag} or {ref, Ref}, otherwise updating the dep may not work as expected.~n", []),
+ ok = filelib:ensure_dir(Dir),
+ rebar_utils:sh(?FMT("git clone -n ~s ~s", [Url, filename:basename(Dir)]),
+ [{cd, filename:dirname(Dir)}]),
+ rebar_utils:sh(?FMT("git checkout -q ~s", [Rev]), [{cd, Dir}]).
+
+make_vsn(Dir) ->
+ Cwd = file:get_cwd(),
+ try
+ ok = file:set_cwd(Dir),
+ {Vsn, RawRef, RawCount} = collect_default_refcount(),
+ {plain, build_vsn_string(Vsn, RawRef, RawCount)}
+ after
+ file:set_cwd(Cwd)
+ end.
+
+%% Internal functions
+
+collect_default_refcount() ->
+ %% Get the tag timestamp and minimal ref from the system. The
+ %% timestamp is really important from an ordering perspective.
+ RawRef = os:cmd("git log -n 1 --pretty=format:'%h\n' "),
+
+ {Tag, TagVsn} = parse_tags(),
+ RawCount =
+ case Tag of
+ undefined ->
+ os:cmd("git rev-list HEAD | wc -l");
+ _ ->
+ get_patch_count(Tag)
+ end,
+ {TagVsn, RawRef, RawCount}.
+
+build_vsn_string(Vsn, RawRef, RawCount) ->
+ %% Cleanup the tag and the Ref information. Basically leading 'v's and
+ %% whitespace needs to go away.
+ RefTag = case RawRef of
+ undefined ->
+ "";
+ RawRef ->
+ [".ref", re:replace(RawRef, "\\s", "", [global])]
+ end,
+ Count = erlang:iolist_to_binary(re:replace(RawCount, "\\s", "", [global])),
+
+ %% Create the valid [semver](http://semver.org) version from the tag
+ case Count of
+ <<"0">> ->
+ erlang:binary_to_list(erlang:iolist_to_binary(Vsn));
+ _ ->
+ erlang:binary_to_list(erlang:iolist_to_binary([Vsn, "+build.",
+ Count, RefTag]))
+ end.
+
+get_patch_count(RawRef) ->
+ Ref = re:replace(RawRef, "\\s", "", [global]),
+ Cmd = io_lib:format("git rev-list ~s..HEAD | wc -l",
+ [Ref]),
+ os:cmd(Cmd).
+
+parse_tags() ->
+ first_valid_tag(os:cmd("git log --oneline --decorate | fgrep \"tag: \" -1000")).
+
+first_valid_tag(Line) ->
+ case re:run(Line, "(\\(|\\s)tag:\\s(v([^,\\)]+))", [{capture, [2, 3], list}]) of
+ {match,[Tag, Vsn]} ->
+ {Tag, Vsn};
+ nomatch ->
+ {undefined, "0.0.0"}
+ end.