Current section

Files

Jump to
elvis_core src elvis_project.erl
Raw

src/elvis_project.erl

-module(elvis_project).
-behaviour(elvis_rule).
-export([default/1]).
-export([no_branch_deps/2, protocol_for_deps/2]).
% The whole file is considered to have either callback functions or rules.
-ignore_xref(elvis_project).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Default values
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-spec default(RuleName :: atom()) -> elvis_rule:def().
default(no_branch_deps) ->
elvis_rule:defmap(#{
ignore => []
});
default(protocol_for_deps) ->
elvis_rule:defmap(#{
ignore => [],
regex => "^(https://|git://|\\d+(\\.\\d+)*)"
});
default(_RuleName) ->
elvis_rule:defmap(#{}).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Rules
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-spec protocol_for_deps(elvis_rule:t(), elvis_config:t()) -> [elvis_result:item()].
protocol_for_deps(Rule, _ElvisConfig) ->
IgnoreDeps = elvis_rule:option(ignore, Rule),
Regex = elvis_rule:option(regex, Rule),
Deps = get_deps(elvis_rule:file(Rule)),
NoHexDeps = lists:filter(fun(Dep) -> not is_hex_dep(Dep) end, Deps),
BadDeps = lists:filter(fun(Dep) -> is_not_git_dep(Dep, Regex) end, NoHexDeps),
lists:filtermap(
fun(Line) ->
AppName = appname_from_line(Line),
case lists:member(AppName, IgnoreDeps) of
true ->
false;
false ->
{true,
elvis_result:new_item(
"Dependency '~s' is not using appropriate protocol; prefer "
"respecting regular expression '~s'",
[AppName, Regex]
)}
end
end,
BadDeps
).
appname_from_line({AppName, _}) ->
AppName;
appname_from_line({AppName, _, _GitInfo}) ->
AppName;
appname_from_line({AppName, _Vsn, _GitInfo, _Opts}) ->
AppName.
-spec no_branch_deps(elvis_rule:t(), elvis_config:t()) -> [elvis_result:item()].
no_branch_deps(Rule, _ElvisConfig) ->
IgnoreDeps = elvis_rule:option(ignore, Rule),
Deps = get_deps(elvis_rule:file(Rule)),
BadDeps = lists:filter(fun is_branch_dep/1, Deps),
lists:filtermap(
fun(Line) ->
AppName = appname_from_line(Line),
case lists:member(AppName, IgnoreDeps) of
true ->
false;
false ->
{true,
elvis_result:new_item(
"Dependency '~s' uses a branch; prefer a tag or a specific commit",
[AppName]
)}
end
end,
BadDeps
).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Private
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% Rebar
get_deps(File) ->
{Src, _} = elvis_file:src(File),
Terms = ktn_code:consult(Src),
IsDepsTerm =
fun
({deps, _}) ->
true;
(_) ->
false
end,
case lists:filter(IsDepsTerm, Terms) of
[] ->
[];
[{deps, Deps}] ->
Deps
end.
%% Rebar3
is_branch_dep({_AppName, {_SCM, _Location, {branch, _}}}) ->
true;
is_branch_dep({_AppName, {git_subdir, _Url, {branch, _}, _SubDir}}) ->
true;
%% Specific to plugin rebar_raw_resource
is_branch_dep({AppName, {raw, DepResourceSpecification}}) ->
is_branch_dep({AppName, DepResourceSpecification});
%% Rebar2
is_branch_dep({_AppName, _Vsn, {_SCM, _Location, {branch, _}}}) ->
true;
is_branch_dep(_) ->
false.
is_hex_dep(_AppName) when is_atom(_AppName) ->
true;
is_hex_dep({_AppName, _Vsn, {pkg, _PackageName}}) when
is_atom(_AppName), is_list(_Vsn), is_atom(_PackageName)
->
true;
is_hex_dep({_AppName, {pkg, _OtherName}}) when is_atom(_AppName), is_atom(_OtherName) ->
true;
is_hex_dep({_AppName, _Vsn}) when is_atom(_AppName), is_list(_Vsn) ->
true;
is_hex_dep(_) ->
false.
is_not_git_dep({_AppName, {_SCM, Url, _Branch}}, Regex) ->
nomatch =:= re:run(Url, Regex, []);
is_not_git_dep(
{_AppName, {git_subdir, Url, {BranchTagOrRefType, _BranchTagOrRef}, _SubDir}},
Regex
) when
BranchTagOrRefType =:= branch;
BranchTagOrRefType =:= tag;
BranchTagOrRefType =:= ref
->
nomatch =:= re:run(Url, Regex, []);
%% Specific to plugin rebar_raw_resource
is_not_git_dep({AppName, {raw, DepResourceSpecification}}, Regex) ->
is_not_git_dep({AppName, DepResourceSpecification}, Regex);
%% Alternative formats, backwards compatible declarations
is_not_git_dep({_AppName, {_SCM, Url}}, Regex) ->
nomatch =:= re:run(Url, Regex, []);
is_not_git_dep({_AppName, _Vsn, {_SCM, Url}}, Regex) ->
nomatch =:= re:run(Url, Regex, []);
is_not_git_dep({_AppName, _Vsn, {_SCM, Url, _Branch}}, Regex) ->
nomatch =:= re:run(Url, Regex, []);
is_not_git_dep({_AppName, _Vsn, {_SCM, Url, {BranchTagOrRefType, _Branch}}, _Opts}, Regex) when
BranchTagOrRefType =:= branch;
BranchTagOrRefType =:= tag;
BranchTagOrRefType =:= ref
->
nomatch =:= re:run(Url, Regex, []).