Skip to content

Commit

Permalink
Add new ruleset, gitignore, with rule gitignore_patterns
Browse files Browse the repository at this point in the history
It'll use files identified by `dirs`+`filter`
(which should be `.gitignore`) and apply
(at the moment, the only existing) rule
`gitignore_patterns` with the default value found in
the documentation
  • Loading branch information
paulo-ferraz-oliveira committed Sep 15, 2024
1 parent 7d7ac44 commit 1efede8
Show file tree
Hide file tree
Showing 8 changed files with 117 additions and 5 deletions.
5 changes: 5 additions & 0 deletions RULES.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ identified with `(since ...)` for convenience purposes.

## Project rules

- [`.gitignore` patterns](doc_rules/elvis_project/gitignore_patterns.md)
- [No deps master erlang.mk - *deprecated*](doc_rules/elvis_project/no_deps_master_erlang_mk.md)
- [No deps master rebar - *deprecated*](doc_rules/elvis_project/no_deps_master_rebar.md)
- [No deps with branches](doc_rules/elvis_project/no_branch_deps.md)
Expand All @@ -83,6 +84,7 @@ The six pre-defined rulesets are:
- `elvis_config`, for elvis configuration files.
- `erl_files`, for Erlang source files (pre-defined rule set).
- `erl_files_strict`, for Erlang source files (all available rules).
- `gitignore`, for `.gitignore` files.
- `hrl_files`, for Erlang header files.
- `makefiles`, for Makefiles.
- `rebar_config`, for rebar configuration files.
Expand Down Expand Up @@ -200,6 +202,9 @@ line numbers will most surely not correspond with those in the source file.
, filter => "elvis.config"
, ruleset => elvis_config
, rules => [] }
, #{ dirs => ["."]
, filter => ".gitignore"
, ruleset => gitignore }
]}
, {verbose, true}
]}].
Expand Down
7 changes: 6 additions & 1 deletion config/test.config
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,10 @@
ruleset => rebar_config},
#{dirs => ["../../_build/test/lib/elvis_core/test/examples"],
filter => "elvis.config",
ruleset => elvis_config}]},
ruleset => elvis_config},
#{dirs =>
["../../_build/test/lib/elvis_core/test/dirs/apps/app1",
"../../_build/test/lib/elvis_core/test/dirs/apps/app2"],
filter => ".gitignore",
ruleset => gitignore}]},
{output_format, plain}]}].
22 changes: 22 additions & 0 deletions doc_rules/elvis_project/gitignore_patterns.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# `.gitignore` patterns

(since [3.3.0](https://github.com/inaka/elvis_core/releases/tag/3.3.0))

Include, in the project's `.gitignore` file, the patterns identified by the rule.

## Options

- `all_of :: [string()]`.
- default: `["^.rebar3/$",
"^_build/$",
"^_checkouts/$",
"^doc/$",
"^/erl_crash.dump$",
"^/rebar3.crashdump$",
"^test/logs/$"]`.

## Example

```erlang
{elvis_project, gitignore_patterns, #{}}
```
51 changes: 48 additions & 3 deletions src/elvis_project.erl
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
-module(elvis_project).

-export([default/1, no_branch_deps/3, protocol_for_deps/3, old_configuration_format/3]).
-export([default/1, no_branch_deps/3, protocol_for_deps/3, old_configuration_format/3,
gitignore_patterns/3]).

-export_type([protocol_for_deps_config/0]).

Expand All @@ -14,13 +15,17 @@
"The current Elvis configuration file has an outdated format. "
"Please check Elvis's GitHub repository to find out what the "
"new format is.").
-define(MISSING_GITIGNORE_PATTERN, "Your .gitignore file should contain pattern '~s'.").

% These are part of a non-declared "behaviour"
% The reason why we don't try to handle them with different arity is
% that arguments are ignored in different positions (1 and 3) so that'd
% probably be messier than to ignore the warning
-hank([{unnecessary_function_arguments,
[{old_configuration_format, 3}, {no_branch_deps, 3}, {protocol_for_deps, 3}]}]).
[{old_configuration_format, 3},
{no_branch_deps, 3},
{protocol_for_deps, 3},
{gitignore_patterns, 3}]}]).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Default values
Expand All @@ -32,7 +37,16 @@ default(no_branch_deps) ->
default(protocol_for_deps) ->
#{ignore => [], regex => "(https://.*|[0-9]+([.][0-9]+)*)"};
default(old_configuration_format) ->
#{}.
#{};
default(gitignore_patterns) ->
#{all_of =>
["^.rebar3/$",
"^_build/$",
"^_checkouts/$",
"^doc/$",
"^/erl_crash.dump$",
"^/rebar3.crashdump$",
"^test/logs/$"]}.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Rules
Expand Down Expand Up @@ -82,6 +96,22 @@ old_configuration_format(_Config, Target, _RuleConfig) ->
end
end.

-spec gitignore_patterns(elvis_config:config(),
elvis_file:file(),
elvis_style:empty_rule_config()) ->
[elvis_result:item()].
gitignore_patterns(_Config, #{path := Path}, RuleConfig) ->
AllOf = option(all_of, RuleConfig, gitignore_patterns),
case file:read_file(Path) of
{ok, PatternsBin} ->
PatternsStr = binary_to_list(PatternsBin),
PatternsNoR = string:replace(PatternsStr, "\r", ""),
Patterns = string:split(PatternsNoR, "\n", all),
check_patterns_in_lines(Patterns, AllOf, []);
{error, _} ->
[]
end.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Private
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Expand Down Expand Up @@ -193,6 +223,21 @@ exists_old_rule(#{rules := Rules}) ->
exists_old_rule(_) ->
false.

%% .gitignore
%% @private
check_patterns_in_lines(_Lines, [], Results) ->
{ok, Results};
check_patterns_in_lines(Lines, [Pattern | Rest], Results0) ->
PatternFound = lists:any(fun(Line) -> re:run(Line, Pattern) =/= nomatch end, Lines),
Results =
case PatternFound of
true ->
Results0;
false ->
[elvis_result:new(item, ?MISSING_GITIGNORE_PATTERN, [Pattern]) | Results0]
end,
check_patterns_in_lines(Lines, Rest, Results).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Internal Function Definitions
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Expand Down
3 changes: 3 additions & 0 deletions src/elvis_rulesets.erl
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ set_rulesets(RuleSets) ->
maps:to_list(RuleSets)).

-spec rules(Group :: atom()) -> [elvis_core:rule()].
rules(gitignore) ->
lists:map(fun({Mod, Rule}) -> {Mod, Rule, apply(Mod, default, [Rule])} end,
[{elvis_project, Rule} || Rule <- [gitignore_patterns]]);
rules(hrl_files) ->
lists:map(fun({Mod, Rule}) -> {Mod, Rule, apply(Mod, default, [Rule])} end,
[{elvis_text_style, Rule} || Rule <- [line_length, no_tabs, no_trailing_whitespace]]
Expand Down
9 changes: 9 additions & 0 deletions test/dirs/apps/app1/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
.rebar3/
_build/

_checkouts/
extra
doc/
/erl_crash.dump
/rebar3.crashdump
test/logs/
8 changes: 8 additions & 0 deletions test/dirs/apps/app2/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
.rebar3/
_build/
_checkouts/
#doc/

/erl_crash.dump
/rebar3.crashdump
test/logs/
17 changes: 16 additions & 1 deletion test/project_SUITE.erl
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

-export([all/0, init_per_suite/1, end_per_suite/1]).
-export([verify_no_branch_deps/1, verify_hex_dep/1, verify_protocol_for_deps/1,
verify_old_config_format/1]).
verify_old_config_format/1, verify_gitignore_patterns/1]).

-define(EXCLUDED_FUNS, [module_info, all, test, init_per_suite, end_per_suite]).

Expand Down Expand Up @@ -110,3 +110,18 @@ verify_old_config_format(_Config) ->
PathPass = "pass.elvis.config",
{ok, FilePass} = elvis_test_utils:find_file(SrcDirs, PathPass),
[] = elvis_project:old_configuration_format(ElvisConfig, FilePass, #{}).

-spec verify_gitignore_patterns(config()) -> any().
verify_gitignore_patterns(_Config) ->
GitIgnoreConfig = elvis_test_utils:config(gitignore),
[SrcDirPass, SrcDirFail] = elvis_config:dirs(GitIgnoreConfig),
NoRuleConfig = #{},

PathPass = ".gitignore",
{ok, FilePass} = elvis_test_utils:find_file([SrcDirPass], PathPass),
{ok, []} = elvis_project:gitignore_patterns(GitIgnoreConfig, FilePass, NoRuleConfig),

PathFail = ".gitignore",
{ok, FileFail} = elvis_test_utils:find_file([SrcDirFail], PathFail),
{ok, [Res]} = elvis_project:gitignore_patterns(GitIgnoreConfig, FileFail, NoRuleConfig),
#{info := ["^doc/$"]} = Res.

0 comments on commit 1efede8

Please sign in to comment.