Packages
elvis_core
4.1.0
5.0.4
5.0.3
5.0.2
5.0.1
5.0.0
retired
4.2.3
4.2.2
4.2.1
4.2.0
4.1.1
4.1.0
4.0.0
3.2.5
3.2.4
3.2.3
3.2.2
3.2.1
3.2.0
3.1.0
3.0.1
3.0.0
2.0.1
2.0.0
1.4.0
1.3.2
1.3.1
1.3.0
1.2.0
1.1.2
1.1.1
1.1.0
1.0.0
0.7.0
0.6.1
0.6.0
0.5.0
0.4.3
0.4.2
0.4.1
0.4.0
0.3.9
0.3.8
0.3.7
0.3.6
0.3.5
0.3.4
0.3.3
0.3.2
0.3.1
0.3.0
Core library for the Erlang style reviewer
Current section
Files
Jump to
Current section
Files
src/elvis_project.erl
-module(elvis_project).
-behaviour(elvis_ruleset).
-export([default/1, no_branch_deps/3, protocol_for_deps/3, old_configuration_format/3]).
-export_type([protocol_for_deps_config/0]).
-define(DEP_BRANCH,
"Dependency '~s' uses a branch. "
"Please change this to a tag or specific commit."
).
-define(DEP_NO_GIT,
"Dependency '~s' is not using appropriate protocol, "
"please change this to something like '~s'"
).
-define(OLD_CONFIG_FORMAT,
"The current Elvis configuration file has an outdated format. "
"Please check Elvis's GitHub repository to find out what the "
"new format is."
).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Default values
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-spec default(RuleName :: atom()) -> DefaultRuleConfig :: #{atom() := term()}.
default(no_branch_deps) ->
#{ignore => []};
default(protocol_for_deps) ->
#{ignore => [], regex => "^(https://|git://|\\d+(\\.\\d+)*)"};
default(old_configuration_format) ->
#{}.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Rules
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-type protocol_for_deps_config() :: #{ignore => [module()], regex => string()}.
-spec protocol_for_deps(
elvis_config:config(),
elvis_file:file(),
protocol_for_deps_config()
) ->
[elvis_result:item()].
protocol_for_deps(_Config, Target, RuleConfig) ->
IgnoreDeps = option(ignore, RuleConfig, protocol_for_deps),
Regex = option(regex, RuleConfig, protocol_for_deps),
Deps = get_deps(Target),
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:flatmap(
fun(Line) -> dep_to_result(Line, ?DEP_NO_GIT, {IgnoreDeps, Regex}) end,
BadDeps
).
-spec no_branch_deps(
elvis_config:config(),
elvis_file:file(),
elvis_style:empty_rule_config()
) ->
[elvis_result:item()].
no_branch_deps(_Config, Target, RuleConfig) ->
IgnoreDeps = option(ignore, RuleConfig, no_branch_deps),
Deps = get_deps(Target),
BadDeps = lists:filter(fun is_branch_dep/1, Deps),
lists:flatmap(fun(Line) -> dep_to_result(Line, ?DEP_BRANCH, IgnoreDeps) end, BadDeps).
-spec old_configuration_format(
elvis_config:config(),
elvis_file:file(),
elvis_style:empty_rule_config()
) ->
[elvis_result:item()].
old_configuration_format(_Config, Target, _RuleConfig) ->
{Content, _} = elvis_file:src(Target),
[AllConfig] = ktn_code:consult(Content),
case proplists:get_value(elvis, AllConfig) of
undefined ->
[];
ElvisConfig ->
case is_old_config(ElvisConfig) of
false ->
[];
true ->
[elvis_result:new(item, ?OLD_CONFIG_FORMAT, [])]
end
end.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 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, []).
dep_to_result({AppName, _}, Message, {IgnoreDeps, Regex}) ->
case lists:member(AppName, IgnoreDeps) of
true ->
[];
false ->
[elvis_result:new(item, Message, [AppName, Regex])]
end;
dep_to_result({AppName, _}, Message, IgnoreDeps) ->
case lists:member(AppName, IgnoreDeps) of
true ->
[];
false ->
[elvis_result:new(item, Message, [AppName])]
end;
dep_to_result({AppName, _, GitInfo}, Message, {IgnoreDeps, Regex}) ->
dep_to_result({AppName, GitInfo}, Message, {IgnoreDeps, Regex});
dep_to_result({AppName, _, GitInfo}, Message, IgnoreDeps) ->
dep_to_result({AppName, GitInfo}, Message, IgnoreDeps);
dep_to_result({AppName, _Vsn, GitInfo, _Opts}, Message, IgnoreDeps) ->
dep_to_result({AppName, GitInfo}, Message, IgnoreDeps).
%% Old config
is_old_config(ElvisConfig) ->
case proplists:get_value(config, ElvisConfig) of
undefined ->
false;
Config when is_map(Config) ->
true;
Config when is_list(Config) ->
SrcDirsIsKey =
fun(RuleGroup) ->
maps:is_key(src_dirs, RuleGroup) orelse exists_old_rule(RuleGroup)
end,
lists:filter(SrcDirsIsKey, Config) /= []
end.
exists_old_rule(#{rules := Rules}) ->
Filter =
fun
({_, _, Args}) when is_list(Args) ->
true;
(_) ->
false
end,
lists:filter(Filter, Rules) /= [];
exists_old_rule(_) ->
false.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Internal Function Definitions
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-spec option(OptionName, RuleConfig, Rule) -> OptionValue when
OptionName :: atom(),
RuleConfig :: elvis_config:config(),
Rule :: atom(),
OptionValue :: term().
option(OptionName, RuleConfig, Rule) ->
maybe_default_option(maps:get(OptionName, RuleConfig, undefined), OptionName, Rule).
-spec maybe_default_option(UserDefinedOptionValue, OptionName, Rule) -> OptionValue when
UserDefinedOptionValue :: undefined | term(),
OptionName :: atom(),
Rule :: atom(),
OptionValue :: term().
maybe_default_option(undefined = _UserDefinedOptionValue, OptionName, Rule) ->
maps:get(OptionName, default(Rule));
maybe_default_option(UserDefinedOptionValue, _OptionName, _Rule) ->
UserDefinedOptionValue.