git-lint 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|