git-lint 2.4.0 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (75) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/LICENSE.adoc +207 -155
  4. data/README.adoc +201 -203
  5. data/{bin → exe}/git-lint +1 -3
  6. data/lib/git/lint/analyzer.rb +76 -0
  7. data/lib/git/lint/analyzers/abstract.rb +8 -18
  8. data/lib/git/lint/analyzers/commit_author_capitalization.rb +3 -9
  9. data/lib/git/lint/analyzers/commit_author_email.rb +3 -9
  10. data/lib/git/lint/analyzers/commit_author_name.rb +5 -14
  11. data/lib/git/lint/analyzers/commit_body_bullet.rb +2 -9
  12. data/lib/git/lint/analyzers/commit_body_bullet_capitalization.rb +2 -9
  13. data/lib/git/lint/analyzers/commit_body_bullet_delimiter.rb +2 -9
  14. data/lib/git/lint/analyzers/commit_body_leading_line.rb +1 -7
  15. data/lib/git/lint/analyzers/commit_body_line_length.rb +4 -11
  16. data/lib/git/lint/analyzers/commit_body_paragraph_capitalization.rb +1 -7
  17. data/lib/git/lint/analyzers/commit_body_phrase.rb +2 -38
  18. data/lib/git/lint/analyzers/commit_body_presence.rb +4 -11
  19. data/lib/git/lint/analyzers/commit_body_single_bullet.rb +2 -9
  20. data/lib/git/lint/analyzers/commit_body_tracker_shorthand.rb +27 -0
  21. data/lib/git/lint/analyzers/commit_subject_length.rb +4 -11
  22. data/lib/git/lint/analyzers/commit_subject_prefix.rb +2 -9
  23. data/lib/git/lint/analyzers/commit_subject_suffix.rb +2 -13
  24. data/lib/git/lint/analyzers/commit_trailer_collaborator_capitalization.rb +3 -12
  25. data/lib/git/lint/analyzers/commit_trailer_collaborator_duplication.rb +3 -11
  26. data/lib/git/lint/analyzers/commit_trailer_collaborator_email.rb +3 -12
  27. data/lib/git/lint/analyzers/commit_trailer_collaborator_key.rb +4 -13
  28. data/lib/git/lint/analyzers/commit_trailer_collaborator_name.rb +5 -15
  29. data/lib/git/lint/cli/actions/analyze/branch.rb +43 -0
  30. data/lib/git/lint/cli/actions/analyze/commit.rb +44 -0
  31. data/lib/git/lint/cli/actions/config.rb +37 -0
  32. data/lib/git/lint/cli/actions/hook.rb +34 -0
  33. data/lib/git/lint/cli/parser.rb +33 -0
  34. data/lib/git/lint/cli/parsers/analyze.rb +40 -0
  35. data/lib/git/lint/cli/parsers/core.rb +72 -0
  36. data/lib/git/lint/cli/shell.rb +56 -0
  37. data/lib/git/lint/collector.rb +3 -4
  38. data/lib/git/lint/commits/container.rb +19 -0
  39. data/lib/git/lint/commits/loader.rb +51 -0
  40. data/lib/git/lint/commits/systems/circle_ci.rb +26 -0
  41. data/lib/git/lint/{branches/environments → commits/systems}/git_hub_action.rb +10 -8
  42. data/lib/git/lint/commits/systems/local.rb +26 -0
  43. data/lib/git/lint/{branches/environments → commits/systems}/netlify_ci.rb +14 -10
  44. data/lib/git/lint/configuration/content.rb +26 -0
  45. data/lib/git/lint/configuration/defaults.yml +118 -0
  46. data/lib/git/lint/configuration/loader.rb +50 -0
  47. data/lib/git/lint/configuration/setting.rb +24 -0
  48. data/lib/git/lint/container.rb +41 -0
  49. data/lib/git/lint/errors/severity.rb +1 -0
  50. data/lib/git/lint/errors/sha.rb +1 -0
  51. data/lib/git/lint/identity.rb +2 -1
  52. data/lib/git/lint/kit/filter_list.rb +1 -1
  53. data/lib/git/lint/parsers/trailers/collaborator.rb +1 -0
  54. data/lib/git/lint/rake/tasks.rb +5 -4
  55. data/lib/git/lint/reporters/branch.rb +12 -6
  56. data/lib/git/lint/reporters/commit.rb +3 -1
  57. data/lib/git/lint/reporters/line.rb +3 -5
  58. data/lib/git/lint/reporters/lines/paragraph.rb +3 -0
  59. data/lib/git/lint/reporters/lines/sentence.rb +3 -0
  60. data/lib/git/lint/reporters/style.rb +3 -1
  61. data/lib/git/lint/validators/capitalization.rb +1 -0
  62. data/lib/git/lint/validators/email.rb +1 -0
  63. data/lib/git/lint/validators/name.rb +1 -0
  64. data/lib/git/lint.rb +18 -47
  65. data.tar.gz.sig +0 -0
  66. metadata +54 -28
  67. metadata.gz.sig +0 -0
  68. data/lib/git/lint/analyzers/commit_body_issue_tracker_link.rb +0 -39
  69. data/lib/git/lint/branches/environments/circle_ci.rb +0 -24
  70. data/lib/git/lint/branches/environments/local.rb +0 -24
  71. data/lib/git/lint/branches/environments/travis_ci.rb +0 -49
  72. data/lib/git/lint/branches/feature.rb +0 -42
  73. data/lib/git/lint/cli.rb +0 -117
  74. data/lib/git/lint/refinements/strings.rb +0 -21
  75. data/lib/git/lint/runner.rb +0 -35
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Git
4
+ module Lint
5
+ module CLI
6
+ module Actions
7
+ module Analyze
8
+ # Handles analyze action for commit(s) by SHA.
9
+ class Commit
10
+ def initialize analyzer: Analyzer.new,
11
+ parser: GitPlus::Parsers::Commits::Saved::History.with_show,
12
+ container: Container
13
+ @analyzer = analyzer
14
+ @parser = parser
15
+ @container = container
16
+ end
17
+
18
+ def call sha = nil
19
+ process sha
20
+ rescue Errors::Base => error
21
+ logger.error { error.message }
22
+ kernel.abort
23
+ end
24
+
25
+ private
26
+
27
+ attr_reader :analyzer, :parser, :container
28
+
29
+ def process sha
30
+ analyzer.call commits: parser.call(*sha) do |collector, reporter|
31
+ kernel.puts reporter
32
+ kernel.abort if collector.errors?
33
+ end
34
+ end
35
+
36
+ def kernel = container[__method__]
37
+
38
+ def logger = container[__method__]
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Git
4
+ module Lint
5
+ module CLI
6
+ module Actions
7
+ # Handles gem configuration action.
8
+ class Config
9
+ def initialize configuration: Configuration::Loader::CLIENT, container: Container
10
+ @configuration = configuration
11
+ @container = container
12
+ end
13
+
14
+ def call action
15
+ case action
16
+ when :edit then edit
17
+ when :view then view
18
+ else logger.error { "Invalid configuration action: #{action}." }
19
+ end
20
+ end
21
+
22
+ private
23
+
24
+ attr_reader :configuration, :container
25
+
26
+ def edit = kernel.system("$EDITOR #{configuration.current}")
27
+
28
+ def view = kernel.system("cat #{configuration.current}")
29
+
30
+ def kernel = container[__method__]
31
+
32
+ def logger = container[__method__]
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Git
4
+ module Lint
5
+ module CLI
6
+ module Actions
7
+ # Handles unsaved Git commit action.
8
+ class Hook
9
+ def initialize analyzer: Analyzer.new, container: Container
10
+ @analyzer = analyzer
11
+ @container = container
12
+ end
13
+
14
+ def call path
15
+ analyzer.call commits: [repository.unsaved(path)] do |collector, reporter|
16
+ kernel.puts reporter
17
+ kernel.abort if collector.errors?
18
+ end
19
+ end
20
+
21
+ private
22
+
23
+ attr_reader :analyzer, :container
24
+
25
+ def repository = container[__method__]
26
+
27
+ def kernel = container[__method__]
28
+
29
+ def logger = container[__method__]
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "optparse"
4
+
5
+ module Git
6
+ module Lint
7
+ module CLI
8
+ # Assembles and parses all Command Line Interface (CLI) options.
9
+ class Parser
10
+ CLIENT = OptionParser.new nil, 40, " "
11
+ SECTIONS = [Parsers::Core, Parsers::Analyze].freeze # Order matters.
12
+
13
+ def initialize sections: SECTIONS, client: CLIENT, container: Container
14
+ @sections = sections
15
+ @client = client
16
+ @configuration = container[:configuration].dup
17
+ end
18
+
19
+ def call arguments = []
20
+ sections.each { |section| section.call configuration, client: }
21
+ client.parse arguments
22
+ configuration.freeze
23
+ end
24
+
25
+ def to_s = client.to_s
26
+
27
+ private
28
+
29
+ attr_reader :sections, :client, :configuration
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "refinements/structs"
4
+
5
+ module Git
6
+ module Lint
7
+ module CLI
8
+ module Parsers
9
+ # Handles parsing of Command Line Interface (CLI) core options.
10
+ class Analyze
11
+ using ::Refinements::Structs
12
+
13
+ def self.call(...) = new(...).call
14
+
15
+ def initialize configuration = Container[:configuration], client: Parser::CLIENT
16
+ @configuration = configuration
17
+ @client = client
18
+ end
19
+
20
+ def call arguments = []
21
+ client.separator "\nANALYZE OPTIONS:\n"
22
+ add_sha
23
+ client.parse arguments
24
+ configuration
25
+ end
26
+
27
+ private
28
+
29
+ attr_reader :configuration, :client
30
+
31
+ def add_sha
32
+ client.on "--sha HASH", "Analyze specific commit SHA." do |sha|
33
+ configuration.merge! analyze_sha: sha
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "refinements/structs"
4
+
5
+ module Git
6
+ module Lint
7
+ module CLI
8
+ module Parsers
9
+ # Handles parsing of Command Line Interface (CLI) core options.
10
+ class Core
11
+ using ::Refinements::Structs
12
+
13
+ def self.call(...) = new(...).call
14
+
15
+ def initialize configuration = Container[:configuration], client: Parser::CLIENT
16
+ @configuration = configuration
17
+ @client = client
18
+ end
19
+
20
+ def call arguments = []
21
+ client.banner = "#{Identity::LABEL} - #{Identity::SUMMARY}"
22
+ client.separator "\nUSAGE:\n"
23
+ collate
24
+ client.parse arguments
25
+ configuration
26
+ end
27
+
28
+ private
29
+
30
+ attr_reader :configuration, :client
31
+
32
+ def collate = private_methods.sort.grep(/add_/).each { |method| __send__ method }
33
+
34
+ def add_analyze
35
+ client.on "-a", "--analyze [options]", "Analyze current branch commits." do
36
+ configuration.merge! action_analyze: true
37
+ end
38
+ end
39
+
40
+ def add_config
41
+ client.on(
42
+ "-c",
43
+ "--config ACTION",
44
+ %i[edit view],
45
+ "Manage gem configuration. Actions: edit or view."
46
+ ) do |action|
47
+ configuration.merge! action_config: action
48
+ end
49
+ end
50
+
51
+ def add_hook
52
+ client.on "--hook PATH", "Hook for analyzing unsaved commits." do |path|
53
+ configuration.merge! action_hook: Pathname(path)
54
+ end
55
+ end
56
+
57
+ def add_version
58
+ client.on "-v", "--version", "Show gem version." do
59
+ configuration.merge! action_version: Identity::VERSION_LABEL
60
+ end
61
+ end
62
+
63
+ def add_help
64
+ client.on "-h", "--help", "Show this message." do
65
+ configuration.merge! action_help: true
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Git
4
+ module Lint
5
+ module CLI
6
+ # The main Command Line Interface (CLI) object.
7
+ class Shell
8
+ ACTIONS = {
9
+ analyze_branch: Actions::Analyze::Branch.new,
10
+ analyze_commit: Actions::Analyze::Commit.new,
11
+ config: Actions::Config.new,
12
+ hook: Actions::Hook.new
13
+ }.freeze
14
+
15
+ def initialize parser: Parser.new, actions: ACTIONS, container: Container
16
+ @parser = parser
17
+ @actions = actions
18
+ @container = container
19
+ end
20
+
21
+ def call arguments = []
22
+ perform parser.call(arguments)
23
+ rescue OptionParser::ParseError, Errors::Base => error
24
+ logger.error { error.message }
25
+ end
26
+
27
+ private
28
+
29
+ attr_reader :parser, :actions, :container
30
+
31
+ def perform configuration
32
+ case configuration
33
+ in action_analyze: true, analyze_sha: nil then analyze_branch
34
+ in action_analyze: true, analyze_sha: String => sha then analyze_commit sha
35
+ in action_config: Symbol => action then config action
36
+ in action_hook: Pathname => path then hook path
37
+ in action_version: String => version then logger.info version
38
+ else usage
39
+ end
40
+ end
41
+
42
+ def analyze_branch = actions.fetch(__method__).call
43
+
44
+ def analyze_commit(sha) = actions.fetch(__method__).call(sha)
45
+
46
+ def config(action) = actions.fetch(__method__).call(action)
47
+
48
+ def hook(path) = actions.fetch(__method__).call(path)
49
+
50
+ def usage = logger.unknown { parser.to_s }
51
+
52
+ def logger = container[__method__]
53
+ end
54
+ end
55
+ end
56
+ end
@@ -1,12 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "refinements/hashes"
4
-
5
3
  module Git
6
4
  module Lint
5
+ # Collects and categorizes, by severity, all issues (if any).
7
6
  class Collector
8
- using ::Refinements::Hashes
9
-
10
7
  def initialize
11
8
  @collection = Hash.new { |default, missing_id| default[missing_id] = [] }
12
9
  end
@@ -18,6 +15,8 @@ module Git
18
15
 
19
16
  def retrieve(id) = collection[id]
20
17
 
18
+ def clear = collection.clear && self
19
+
21
20
  def empty? = collection.empty?
22
21
 
23
22
  def warnings? = collection.values.flatten.any?(&:warning?)
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "dry-container"
4
+ require "git_plus"
5
+
6
+ module Git
7
+ module Lint
8
+ module Commits
9
+ # Provides container specific to this namespace for all systems.
10
+ module Container
11
+ extend Dry::Container::Mixin
12
+
13
+ register(:repository) { GitPlus::Repository.new }
14
+ register(:shell) { Open3 }
15
+ register(:environment) { ENV }
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "refinements/strings"
4
+
5
+ module Git
6
+ module Lint
7
+ module Commits
8
+ # Automatically detects and loads system.
9
+ class Loader
10
+ using ::Refinements::Strings
11
+
12
+ SYSTEMS = {
13
+ circle_ci: Systems::CircleCI.new,
14
+ git_hub_action: Systems::GitHubAction.new,
15
+ netlify_ci: Systems::NetlifyCI.new,
16
+ local: Systems::Local.new
17
+ }.freeze
18
+
19
+ def initialize systems: SYSTEMS, container: Container
20
+ @systems = systems
21
+ @container = container
22
+ end
23
+
24
+ def call
25
+ message = "Invalid repository. Are you within a Git repository?"
26
+ fail Errors::Base, message unless repository.exist?
27
+
28
+ load_system.call
29
+ end
30
+
31
+ private
32
+
33
+ attr_reader :systems, :container
34
+
35
+ def load_system
36
+ if key? "CIRCLECI" then systems.fetch :circle_ci
37
+ elsif key? "GITHUB_ACTIONS" then systems.fetch :git_hub_action
38
+ elsif key? "NETLIFY" then systems.fetch :netlify_ci
39
+ else systems.fetch :local
40
+ end
41
+ end
42
+
43
+ def key?(key) = environment.fetch(key, "false").to_bool
44
+
45
+ def repository = container[__method__]
46
+
47
+ def environment = container[__method__]
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Git
4
+ module Lint
5
+ module Commits
6
+ module Systems
7
+ # Provides Circle CI build environment feature branch information.
8
+ class CircleCI
9
+ def initialize container: Container
10
+ @container = container
11
+ end
12
+
13
+ def call = repository.commits("origin/#{repository.branch_default}..#{name}")
14
+
15
+ private
16
+
17
+ attr_reader :container
18
+
19
+ def name = "origin/#{repository.branch_name}"
20
+
21
+ def repository = container[__method__]
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -2,21 +2,23 @@
2
2
 
3
3
  module Git
4
4
  module Lint
5
- module Branches
6
- module Environments
5
+ module Commits
6
+ module Systems
7
7
  # Provides GitHub Action build environment feature branch information.
8
8
  class GitHubAction
9
- def initialize repository: GitPlus::Repository.new
10
- @repository = repository
9
+ def initialize container: Container
10
+ @container = container
11
11
  end
12
12
 
13
- def name = "origin/#{repository.branch_name}"
14
-
15
- def commits = repository.commits("origin/#{repository.branch_default}..#{name}")
13
+ def call = repository.commits("origin/#{repository.branch_default}..#{name}")
16
14
 
17
15
  private
18
16
 
19
- attr_reader :repository
17
+ attr_reader :container
18
+
19
+ def name = "origin/#{repository.branch_name}"
20
+
21
+ def repository = container[__method__]
20
22
  end
21
23
  end
22
24
  end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Git
4
+ module Lint
5
+ module Commits
6
+ module Systems
7
+ # Provides local build environment feature branch information.
8
+ class Local
9
+ def initialize container: Container
10
+ @container = container
11
+ end
12
+
13
+ def call = repository.commits("#{repository.branch_default}..#{name}")
14
+
15
+ private
16
+
17
+ attr_reader :container
18
+
19
+ def name = repository.branch_name
20
+
21
+ def repository = container[__method__]
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -4,19 +4,15 @@ require "open3"
4
4
 
5
5
  module Git
6
6
  module Lint
7
- module Branches
8
- module Environments
7
+ module Commits
8
+ module Systems
9
9
  # Provides Netlify CI build environment feature branch information.
10
10
  class NetlifyCI
11
- def initialize repository: GitPlus::Repository.new, shell: Open3, environment: ENV
12
- @repository = repository
13
- @shell = shell
14
- @environment = environment
11
+ def initialize container: Container
12
+ @container = container
15
13
  end
16
14
 
17
- def name = environment["HEAD"]
18
-
19
- def commits
15
+ def call
20
16
  shell.capture3 "git remote add -f origin #{environment["REPOSITORY_URL"]}"
21
17
  shell.capture3 "git fetch origin #{name}:#{name}"
22
18
  repository.commits "origin/#{repository.branch_default}..origin/#{name}"
@@ -24,7 +20,15 @@ module Git
24
20
 
25
21
  private
26
22
 
27
- attr_reader :repository, :shell, :environment
23
+ attr_reader :container
24
+
25
+ def name = environment["HEAD"]
26
+
27
+ def repository = container[__method__]
28
+
29
+ def shell = container[__method__]
30
+
31
+ def environment = container[__method__]
28
32
  end
29
33
  end
30
34
  end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Git
4
+ module Lint
5
+ module Configuration
6
+ # Defines configuration content as the primary source of truth for use throughout the gem.
7
+ Content = Struct.new(
8
+ :action_analyze,
9
+ :action_config,
10
+ :action_help,
11
+ :action_hook,
12
+ :action_version,
13
+ :analyze_sha,
14
+ :analyzers,
15
+ keyword_init: true
16
+ ) do
17
+ def initialize *arguments
18
+ super
19
+ freeze
20
+ end
21
+
22
+ def find_setting(id) = analyzers.find { |setting| setting.id == id }
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,118 @@
1
+ :analyzers:
2
+ :commit_author_capitalization:
3
+ :enabled: true
4
+ :severity: :error
5
+ :commit_author_email:
6
+ :enabled: true
7
+ :severity: :error
8
+ :commit_author_name:
9
+ :enabled: true
10
+ :severity: :error
11
+ :minimum: 2
12
+ :commit_body_bullet:
13
+ :enabled: true
14
+ :severity: :error
15
+ :excludes:
16
+ - "\\*"
17
+ - "•"
18
+ :commit_body_bullet_capitalization:
19
+ :enabled: true
20
+ :severity: :error
21
+ :includes: "\\-"
22
+ :commit_body_bullet_delimiter:
23
+ :enabled: true
24
+ :severity: :error
25
+ :includes: "\\-"
26
+ :commit_body_tracker_shorthand:
27
+ :enabled: true
28
+ :severity: :error
29
+ :excludes:
30
+ - "(f|F)ix(es|ed)?\\s\\#\\d+"
31
+ - "(c|C)lose(s|d)?\\s\\#\\d+"
32
+ - "(r|R)esolve(s|d)?\\s\\#\\d+"
33
+ :commit_body_leading_line:
34
+ :enabled: false
35
+ :severity: :warn
36
+ :commit_body_line_length:
37
+ :enabled: true
38
+ :severity: :error
39
+ :maximum: 72
40
+ :commit_body_paragraph_capitalization:
41
+ :enabled: true
42
+ :severity: :error
43
+ :commit_body_phrase:
44
+ :enabled: true
45
+ :severity: :error
46
+ :excludes:
47
+ - "absolutely"
48
+ - "actually"
49
+ - "all intents and purposes"
50
+ - "along the lines"
51
+ - "at this moment in time"
52
+ - "basically"
53
+ - "each and every one"
54
+ - "everyone knows"
55
+ - "fact of the matter"
56
+ - "furthermore"
57
+ - "however"
58
+ - "in due course"
59
+ - "in the end"
60
+ - "last but not least"
61
+ - "matter of fact"
62
+ - "obviously"
63
+ - "of course"
64
+ - "really"
65
+ - "simply"
66
+ - "things being equal"
67
+ - "would like to"
68
+ - "\\beasy\\b"
69
+ - "\\bjust\\b"
70
+ - "\\bquite\\b"
71
+ - "as\\sfar\\sas\\s.+\\sconcerned"
72
+ - "of\\sthe\\s(fact|opinion)\\sthat"
73
+ :commit_body_presence:
74
+ :enabled: false
75
+ :severity: :warn
76
+ :minimum: 1
77
+ :commit_body_single_bullet:
78
+ :enabled: true
79
+ :severity: :error
80
+ :includes: "\\-"
81
+ :commit_subject_length:
82
+ :enabled: true
83
+ :severity: :error
84
+ :maximum: 72
85
+ :commit_subject_prefix:
86
+ :enabled: true
87
+ :severity: :error
88
+ :includes:
89
+ - Fixed
90
+ - Added
91
+ - Updated
92
+ - Removed
93
+ - Refactored
94
+ :commit_subject_suffix:
95
+ :enabled: true
96
+ :severity: :error
97
+ :excludes:
98
+ - "\\."
99
+ - "\\?"
100
+ - "\\!"
101
+ :commit_trailer_collaborator_capitalization:
102
+ :enabled: true
103
+ :severity: :error
104
+ :commit_trailer_collaborator_duplication:
105
+ :enabled: true
106
+ :severity: :error
107
+ :commit_trailer_collaborator_email:
108
+ :enabled: true
109
+ :severity: :error
110
+ :commit_trailer_collaborator_key:
111
+ :enabled: true
112
+ :severity: :error
113
+ :includes:
114
+ - "Co-Authored-By"
115
+ :commit_trailer_collaborator_name:
116
+ :enabled: true
117
+ :severity: :error
118
+ :minimum: 2