git-lint 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/LICENSE.adoc +162 -0
- data/README.adoc +1068 -0
- data/bin/git-lint +9 -0
- data/lib/git/kit/repo.rb +30 -0
- data/lib/git/lint.rb +51 -0
- data/lib/git/lint/analyzers/abstract.rb +108 -0
- data/lib/git/lint/analyzers/commit_author_capitalization.rb +35 -0
- data/lib/git/lint/analyzers/commit_author_email.rb +35 -0
- data/lib/git/lint/analyzers/commit_author_name.rb +40 -0
- data/lib/git/lint/analyzers/commit_body_bullet.rb +43 -0
- data/lib/git/lint/analyzers/commit_body_bullet_capitalization.rb +46 -0
- data/lib/git/lint/analyzers/commit_body_bullet_delimiter.rb +40 -0
- data/lib/git/lint/analyzers/commit_body_issue_tracker_link.rb +45 -0
- data/lib/git/lint/analyzers/commit_body_leading_line.rb +30 -0
- data/lib/git/lint/analyzers/commit_body_line_length.rb +42 -0
- data/lib/git/lint/analyzers/commit_body_paragraph_capitalization.rb +47 -0
- data/lib/git/lint/analyzers/commit_body_phrase.rb +72 -0
- data/lib/git/lint/analyzers/commit_body_presence.rb +36 -0
- data/lib/git/lint/analyzers/commit_body_single_bullet.rb +40 -0
- data/lib/git/lint/analyzers/commit_subject_length.rb +33 -0
- data/lib/git/lint/analyzers/commit_subject_prefix.rb +42 -0
- data/lib/git/lint/analyzers/commit_subject_suffix.rb +39 -0
- data/lib/git/lint/analyzers/commit_trailer_collaborator_capitalization.rb +51 -0
- data/lib/git/lint/analyzers/commit_trailer_collaborator_duplication.rb +56 -0
- data/lib/git/lint/analyzers/commit_trailer_collaborator_email.rb +52 -0
- data/lib/git/lint/analyzers/commit_trailer_collaborator_key.rb +56 -0
- data/lib/git/lint/analyzers/commit_trailer_collaborator_name.rb +57 -0
- data/lib/git/lint/branches/environments/circle_ci.rb +28 -0
- data/lib/git/lint/branches/environments/local.rb +28 -0
- data/lib/git/lint/branches/environments/netlify_ci.rb +34 -0
- data/lib/git/lint/branches/environments/travis_ci.rb +57 -0
- data/lib/git/lint/branches/feature.rb +44 -0
- data/lib/git/lint/cli.rb +122 -0
- data/lib/git/lint/collector.rb +64 -0
- data/lib/git/lint/commits/saved.rb +104 -0
- data/lib/git/lint/commits/unsaved.rb +120 -0
- data/lib/git/lint/errors/base.rb +14 -0
- data/lib/git/lint/errors/severity.rb +13 -0
- data/lib/git/lint/errors/sha.rb +13 -0
- data/lib/git/lint/identity.rb +13 -0
- data/lib/git/lint/kit/filter_list.rb +30 -0
- data/lib/git/lint/parsers/trailers/collaborator.rb +57 -0
- data/lib/git/lint/rake/setup.rb +4 -0
- data/lib/git/lint/rake/tasks.rb +33 -0
- data/lib/git/lint/refinements/strings.rb +25 -0
- data/lib/git/lint/reporters/branch.rb +67 -0
- data/lib/git/lint/reporters/commit.rb +30 -0
- data/lib/git/lint/reporters/line.rb +32 -0
- data/lib/git/lint/reporters/lines/paragraph.rb +49 -0
- data/lib/git/lint/reporters/lines/sentence.rb +31 -0
- data/lib/git/lint/reporters/style.rb +52 -0
- data/lib/git/lint/runner.rb +34 -0
- data/lib/git/lint/validators/capitalization.rb +29 -0
- data/lib/git/lint/validators/email.rb +24 -0
- data/lib/git/lint/validators/name.rb +30 -0
- metadata +363 -0
- metadata.gz.sig +3 -0
data/bin/git-lint
ADDED
data/lib/git/kit/repo.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Git
|
4
|
+
module Kit
|
5
|
+
class Repo
|
6
|
+
def initialize shell: Open3
|
7
|
+
@shell = shell
|
8
|
+
end
|
9
|
+
|
10
|
+
def exist?
|
11
|
+
shell.capture2e("git rev-parse --git-dir > /dev/null 2>&1")
|
12
|
+
.then { |result, status| result && status.success? }
|
13
|
+
end
|
14
|
+
|
15
|
+
def branch_name
|
16
|
+
shell.capture2e("git rev-parse --abbrev-ref HEAD | tr -d '\n'")
|
17
|
+
.then { |result, _status| result }
|
18
|
+
end
|
19
|
+
|
20
|
+
def shas start: "master", finish: branch_name
|
21
|
+
shell.capture2e(%(git log --pretty=format:"%H" #{start}..#{finish}))
|
22
|
+
.then { |result, _status| result.split "\n" }
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
attr_reader :shell
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
data/lib/git/lint.rb
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "git/lint/identity"
|
4
|
+
require "git/kit/repo"
|
5
|
+
require "git/lint/refinements/strings"
|
6
|
+
require "git/lint/errors/base"
|
7
|
+
require "git/lint/errors/severity"
|
8
|
+
require "git/lint/errors/sha"
|
9
|
+
require "git/lint/kit/filter_list"
|
10
|
+
require "git/lint/validators/email"
|
11
|
+
require "git/lint/validators/name"
|
12
|
+
require "git/lint/validators/capitalization"
|
13
|
+
require "git/lint/parsers/trailers/collaborator"
|
14
|
+
require "git/lint/commits/saved"
|
15
|
+
require "git/lint/commits/unsaved"
|
16
|
+
require "git/lint/branches/environments/local"
|
17
|
+
require "git/lint/branches/environments/circle_ci"
|
18
|
+
require "git/lint/branches/environments/netlify_ci"
|
19
|
+
require "git/lint/branches/environments/travis_ci"
|
20
|
+
require "git/lint/branches/feature"
|
21
|
+
require "git/lint/analyzers/abstract"
|
22
|
+
require "git/lint/analyzers/commit_author_capitalization"
|
23
|
+
require "git/lint/analyzers/commit_author_email"
|
24
|
+
require "git/lint/analyzers/commit_author_name"
|
25
|
+
require "git/lint/analyzers/commit_body_bullet"
|
26
|
+
require "git/lint/analyzers/commit_body_bullet_capitalization"
|
27
|
+
require "git/lint/analyzers/commit_body_bullet_delimiter"
|
28
|
+
require "git/lint/analyzers/commit_body_issue_tracker_link"
|
29
|
+
require "git/lint/analyzers/commit_body_leading_line"
|
30
|
+
require "git/lint/analyzers/commit_body_line_length"
|
31
|
+
require "git/lint/analyzers/commit_body_paragraph_capitalization"
|
32
|
+
require "git/lint/analyzers/commit_body_phrase"
|
33
|
+
require "git/lint/analyzers/commit_body_presence"
|
34
|
+
require "git/lint/analyzers/commit_body_single_bullet"
|
35
|
+
require "git/lint/analyzers/commit_subject_length"
|
36
|
+
require "git/lint/analyzers/commit_subject_prefix"
|
37
|
+
require "git/lint/analyzers/commit_subject_suffix"
|
38
|
+
require "git/lint/analyzers/commit_trailer_collaborator_capitalization"
|
39
|
+
require "git/lint/analyzers/commit_trailer_collaborator_duplication"
|
40
|
+
require "git/lint/analyzers/commit_trailer_collaborator_email"
|
41
|
+
require "git/lint/analyzers/commit_trailer_collaborator_key"
|
42
|
+
require "git/lint/analyzers/commit_trailer_collaborator_name"
|
43
|
+
require "git/lint/collector"
|
44
|
+
require "git/lint/reporters/lines/sentence"
|
45
|
+
require "git/lint/reporters/lines/paragraph"
|
46
|
+
require "git/lint/reporters/line"
|
47
|
+
require "git/lint/reporters/style"
|
48
|
+
require "git/lint/reporters/commit"
|
49
|
+
require "git/lint/reporters/branch"
|
50
|
+
require "git/lint/runner"
|
51
|
+
require "git/lint/cli"
|
@@ -0,0 +1,108 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "refinements/strings"
|
4
|
+
|
5
|
+
module Git
|
6
|
+
module Lint
|
7
|
+
module Analyzers
|
8
|
+
# An abstract class which provides basic functionality from which all analyzers inherit from.
|
9
|
+
class Abstract
|
10
|
+
using ::Refinements::Strings
|
11
|
+
|
12
|
+
LEVELS = %i[warn error].freeze
|
13
|
+
ISSUE_LINE_OFFSET = 2
|
14
|
+
|
15
|
+
def self.inherited klass
|
16
|
+
@descendants ||= []
|
17
|
+
@descendants << klass unless klass.to_s.start_with? "#<Class" # Ignore anonymous classes.
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.id
|
21
|
+
to_s.delete_prefix("Git::Lint::Analyzers").snakecase.to_sym
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.label
|
25
|
+
to_s.delete_prefix("Git::Lint::Analyzers").titleize
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.defaults
|
29
|
+
fail NotImplementedError, "The `.#{__method__}` method must be implemented."
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.descendants
|
33
|
+
@descendants || []
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.build_issue_line index, line
|
37
|
+
{number: index + ISSUE_LINE_OFFSET, content: line}
|
38
|
+
end
|
39
|
+
|
40
|
+
attr_reader :commit
|
41
|
+
|
42
|
+
def initialize commit:, settings: self.class.defaults
|
43
|
+
@commit = commit
|
44
|
+
@settings = settings
|
45
|
+
@filter_list = load_filter_list
|
46
|
+
end
|
47
|
+
|
48
|
+
def enabled?
|
49
|
+
settings.fetch :enabled
|
50
|
+
end
|
51
|
+
|
52
|
+
def severity
|
53
|
+
settings.fetch(:severity).tap do |level|
|
54
|
+
fail Errors::Severity, level unless LEVELS.include? level
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def valid?
|
59
|
+
fail NotImplementedError, "The `##{__method__}` method must be implemented."
|
60
|
+
end
|
61
|
+
|
62
|
+
def invalid?
|
63
|
+
!valid?
|
64
|
+
end
|
65
|
+
|
66
|
+
def warning?
|
67
|
+
invalid? && severity == :warn
|
68
|
+
end
|
69
|
+
|
70
|
+
def error?
|
71
|
+
invalid? && severity == :error
|
72
|
+
end
|
73
|
+
|
74
|
+
def issue
|
75
|
+
fail NotImplementedError, "The `##{__method__}` method must be implemented."
|
76
|
+
end
|
77
|
+
|
78
|
+
protected
|
79
|
+
|
80
|
+
attr_reader :settings, :filter_list
|
81
|
+
|
82
|
+
def load_filter_list
|
83
|
+
Kit::FilterList.new settings[:list]
|
84
|
+
end
|
85
|
+
|
86
|
+
def affected_commit_body_lines
|
87
|
+
commit.body_lines.each.with_object([]).with_index do |(line, lines), index|
|
88
|
+
yield if block_given?
|
89
|
+
lines << self.class.build_issue_line(index, line) if invalid_line? line
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def affected_commit_trailer_lines
|
94
|
+
commit.trailer_lines
|
95
|
+
.each.with_object([])
|
96
|
+
.with_index(commit.trailer_index) do |(line, lines), index|
|
97
|
+
yield if block_given?
|
98
|
+
lines << self.class.build_issue_line(index, line) if invalid_line? line
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def invalid_line? _line
|
103
|
+
fail NotImplementedError, "The `.#{__method__}` method must be implemented."
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Git
|
4
|
+
module Lint
|
5
|
+
module Analyzers
|
6
|
+
class CommitAuthorCapitalization < Abstract
|
7
|
+
def self.defaults
|
8
|
+
{
|
9
|
+
enabled: true,
|
10
|
+
severity: :error
|
11
|
+
}
|
12
|
+
end
|
13
|
+
|
14
|
+
def initialize commit:, settings: self.class.defaults, validator: Validators::Capitalization
|
15
|
+
super commit: commit, settings: settings
|
16
|
+
@validator = validator
|
17
|
+
end
|
18
|
+
|
19
|
+
def valid?
|
20
|
+
validator.new(commit.author_name).valid?
|
21
|
+
end
|
22
|
+
|
23
|
+
def issue
|
24
|
+
return {} if valid?
|
25
|
+
|
26
|
+
{hint: %(Capitalize each part of name: "#{commit.author_name}".)}
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
attr_reader :validator
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Git
|
4
|
+
module Lint
|
5
|
+
module Analyzers
|
6
|
+
class CommitAuthorEmail < Abstract
|
7
|
+
def self.defaults
|
8
|
+
{
|
9
|
+
enabled: true,
|
10
|
+
severity: :error
|
11
|
+
}
|
12
|
+
end
|
13
|
+
|
14
|
+
def initialize commit:, settings: self.class.defaults, validator: Validators::Email
|
15
|
+
super commit: commit, settings: settings
|
16
|
+
@validator = validator
|
17
|
+
end
|
18
|
+
|
19
|
+
def valid?
|
20
|
+
validator.new(commit.author_email).valid?
|
21
|
+
end
|
22
|
+
|
23
|
+
def issue
|
24
|
+
return {} if valid?
|
25
|
+
|
26
|
+
{hint: %(Use "<name>@<server>.<domain>" instead of "#{commit.author_email}".)}
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
attr_reader :validator
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Git
|
4
|
+
module Lint
|
5
|
+
module Analyzers
|
6
|
+
class CommitAuthorName < Abstract
|
7
|
+
def self.defaults
|
8
|
+
{
|
9
|
+
enabled: true,
|
10
|
+
severity: :error,
|
11
|
+
minimum: 2
|
12
|
+
}
|
13
|
+
end
|
14
|
+
|
15
|
+
def initialize commit:, settings: self.class.defaults, validator: Validators::Name
|
16
|
+
super commit: commit, settings: settings
|
17
|
+
@validator = validator
|
18
|
+
end
|
19
|
+
|
20
|
+
def valid?
|
21
|
+
validator.new(commit.author_name, minimum: minimum).valid?
|
22
|
+
end
|
23
|
+
|
24
|
+
def issue
|
25
|
+
return {} if valid?
|
26
|
+
|
27
|
+
{hint: "Author name must consist of #{minimum} parts (minimum)."}
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
attr_reader :validator
|
33
|
+
|
34
|
+
def minimum
|
35
|
+
settings.fetch :minimum
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Git
|
4
|
+
module Lint
|
5
|
+
module Analyzers
|
6
|
+
class CommitBodyBullet < Abstract
|
7
|
+
def self.defaults
|
8
|
+
{
|
9
|
+
enabled: true,
|
10
|
+
severity: :error,
|
11
|
+
excludes: %w[\\* •]
|
12
|
+
}
|
13
|
+
end
|
14
|
+
|
15
|
+
def valid?
|
16
|
+
commit.body_lines.all? { |line| !invalid_line? line }
|
17
|
+
end
|
18
|
+
|
19
|
+
def issue
|
20
|
+
return {} if valid?
|
21
|
+
|
22
|
+
{
|
23
|
+
hint: %(Avoid: #{filter_list.to_hint}.),
|
24
|
+
lines: affected_commit_body_lines
|
25
|
+
}
|
26
|
+
end
|
27
|
+
|
28
|
+
protected
|
29
|
+
|
30
|
+
def load_filter_list
|
31
|
+
Kit::FilterList.new settings.fetch :excludes
|
32
|
+
end
|
33
|
+
|
34
|
+
# :reek:FeatureEnvy
|
35
|
+
def invalid_line? line
|
36
|
+
return false if line.strip.empty?
|
37
|
+
|
38
|
+
!line.match?(/\A(?!\s*#{Regexp.union filter_list.to_regexp}\s+).+\Z/)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Git
|
4
|
+
module Lint
|
5
|
+
module Analyzers
|
6
|
+
class CommitBodyBulletCapitalization < Abstract
|
7
|
+
def self.defaults
|
8
|
+
{
|
9
|
+
enabled: true,
|
10
|
+
severity: :error,
|
11
|
+
includes: %w[\\-]
|
12
|
+
}
|
13
|
+
end
|
14
|
+
|
15
|
+
def valid?
|
16
|
+
lowercased_bullets.size.zero?
|
17
|
+
end
|
18
|
+
|
19
|
+
def issue
|
20
|
+
return {} if valid?
|
21
|
+
|
22
|
+
{
|
23
|
+
hint: "Capitalize first word.",
|
24
|
+
lines: affected_commit_body_lines
|
25
|
+
}
|
26
|
+
end
|
27
|
+
|
28
|
+
protected
|
29
|
+
|
30
|
+
def load_filter_list
|
31
|
+
Kit::FilterList.new settings.fetch :includes
|
32
|
+
end
|
33
|
+
|
34
|
+
def invalid_line? line
|
35
|
+
line.match?(/\A\s*#{Regexp.union filter_list.to_regexp}\s[[:lower:]]+/)
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def lowercased_bullets
|
41
|
+
commit.body_lines.select { |line| invalid_line? line }
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Git
|
4
|
+
module Lint
|
5
|
+
module Analyzers
|
6
|
+
class CommitBodyBulletDelimiter < Abstract
|
7
|
+
def self.defaults
|
8
|
+
{
|
9
|
+
enabled: true,
|
10
|
+
severity: :error,
|
11
|
+
includes: %w[\\-]
|
12
|
+
}
|
13
|
+
end
|
14
|
+
|
15
|
+
def valid?
|
16
|
+
commit.body_lines.none? { |line| invalid_line? line }
|
17
|
+
end
|
18
|
+
|
19
|
+
def issue
|
20
|
+
return {} if valid?
|
21
|
+
|
22
|
+
{
|
23
|
+
hint: "Use space after bullet.",
|
24
|
+
lines: affected_commit_body_lines
|
25
|
+
}
|
26
|
+
end
|
27
|
+
|
28
|
+
protected
|
29
|
+
|
30
|
+
def load_filter_list
|
31
|
+
Kit::FilterList.new settings.fetch :includes
|
32
|
+
end
|
33
|
+
|
34
|
+
def invalid_line? line
|
35
|
+
line.match?(/\A\s*#{Regexp.union filter_list.to_regexp}(?!\s).+\Z/)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|