diff options
Diffstat (limited to 'src/rebar_git_resource.erl')
-rw-r--r-- | src/rebar_git_resource.erl | 146 |
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. |