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.
Files changed (60) hide show
  1. checksums.yaml +7 -0
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/LICENSE.adoc +162 -0
  5. data/README.adoc +1068 -0
  6. data/bin/git-lint +9 -0
  7. data/lib/git/kit/repo.rb +30 -0
  8. data/lib/git/lint.rb +51 -0
  9. data/lib/git/lint/analyzers/abstract.rb +108 -0
  10. data/lib/git/lint/analyzers/commit_author_capitalization.rb +35 -0
  11. data/lib/git/lint/analyzers/commit_author_email.rb +35 -0
  12. data/lib/git/lint/analyzers/commit_author_name.rb +40 -0
  13. data/lib/git/lint/analyzers/commit_body_bullet.rb +43 -0
  14. data/lib/git/lint/analyzers/commit_body_bullet_capitalization.rb +46 -0
  15. data/lib/git/lint/analyzers/commit_body_bullet_delimiter.rb +40 -0
  16. data/lib/git/lint/analyzers/commit_body_issue_tracker_link.rb +45 -0
  17. data/lib/git/lint/analyzers/commit_body_leading_line.rb +30 -0
  18. data/lib/git/lint/analyzers/commit_body_line_length.rb +42 -0
  19. data/lib/git/lint/analyzers/commit_body_paragraph_capitalization.rb +47 -0
  20. data/lib/git/lint/analyzers/commit_body_phrase.rb +72 -0
  21. data/lib/git/lint/analyzers/commit_body_presence.rb +36 -0
  22. data/lib/git/lint/analyzers/commit_body_single_bullet.rb +40 -0
  23. data/lib/git/lint/analyzers/commit_subject_length.rb +33 -0
  24. data/lib/git/lint/analyzers/commit_subject_prefix.rb +42 -0
  25. data/lib/git/lint/analyzers/commit_subject_suffix.rb +39 -0
  26. data/lib/git/lint/analyzers/commit_trailer_collaborator_capitalization.rb +51 -0
  27. data/lib/git/lint/analyzers/commit_trailer_collaborator_duplication.rb +56 -0
  28. data/lib/git/lint/analyzers/commit_trailer_collaborator_email.rb +52 -0
  29. data/lib/git/lint/analyzers/commit_trailer_collaborator_key.rb +56 -0
  30. data/lib/git/lint/analyzers/commit_trailer_collaborator_name.rb +57 -0
  31. data/lib/git/lint/branches/environments/circle_ci.rb +28 -0
  32. data/lib/git/lint/branches/environments/local.rb +28 -0
  33. data/lib/git/lint/branches/environments/netlify_ci.rb +34 -0
  34. data/lib/git/lint/branches/environments/travis_ci.rb +57 -0
  35. data/lib/git/lint/branches/feature.rb +44 -0
  36. data/lib/git/lint/cli.rb +122 -0
  37. data/lib/git/lint/collector.rb +64 -0
  38. data/lib/git/lint/commits/saved.rb +104 -0
  39. data/lib/git/lint/commits/unsaved.rb +120 -0
  40. data/lib/git/lint/errors/base.rb +14 -0
  41. data/lib/git/lint/errors/severity.rb +13 -0
  42. data/lib/git/lint/errors/sha.rb +13 -0
  43. data/lib/git/lint/identity.rb +13 -0
  44. data/lib/git/lint/kit/filter_list.rb +30 -0
  45. data/lib/git/lint/parsers/trailers/collaborator.rb +57 -0
  46. data/lib/git/lint/rake/setup.rb +4 -0
  47. data/lib/git/lint/rake/tasks.rb +33 -0
  48. data/lib/git/lint/refinements/strings.rb +25 -0
  49. data/lib/git/lint/reporters/branch.rb +67 -0
  50. data/lib/git/lint/reporters/commit.rb +30 -0
  51. data/lib/git/lint/reporters/line.rb +32 -0
  52. data/lib/git/lint/reporters/lines/paragraph.rb +49 -0
  53. data/lib/git/lint/reporters/lines/sentence.rb +31 -0
  54. data/lib/git/lint/reporters/style.rb +52 -0
  55. data/lib/git/lint/runner.rb +34 -0
  56. data/lib/git/lint/validators/capitalization.rb +29 -0
  57. data/lib/git/lint/validators/email.rb +24 -0
  58. data/lib/git/lint/validators/name.rb +30 -0
  59. metadata +363 -0
  60. metadata.gz.sig +3 -0
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Git
4
+ module Lint
5
+ module Analyzers
6
+ class CommitBodyIssueTrackerLink < Abstract
7
+ def self.defaults
8
+ {
9
+ enabled: true,
10
+ severity: :error,
11
+ excludes: [
12
+ "(f|F)ix(es|ed)?\\s\\#\\d+",
13
+ "(c|C)lose(s|d)?\\s\\#\\d+",
14
+ "(r|R)esolve(s|d)?\\s\\#\\d+",
15
+ "github\\.com\\/.+\\/issues\\/\\d+"
16
+ ]
17
+ }
18
+ end
19
+
20
+ def valid?
21
+ commit.body_lines.none? { |line| invalid_line? line }
22
+ end
23
+
24
+ def issue
25
+ return {} if valid?
26
+
27
+ {
28
+ hint: "Explain issue instead of using link. Avoid: #{filter_list.to_hint}.",
29
+ lines: affected_commit_body_lines
30
+ }
31
+ end
32
+
33
+ protected
34
+
35
+ def load_filter_list
36
+ Kit::FilterList.new settings.fetch :excludes
37
+ end
38
+
39
+ def invalid_line? line
40
+ line.match?(/.*#{Regexp.union filter_list.to_regexp}.*/)
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Git
4
+ module Lint
5
+ module Analyzers
6
+ class CommitBodyLeadingLine < Abstract
7
+ def self.defaults
8
+ {
9
+ enabled: true,
10
+ severity: :error
11
+ }
12
+ end
13
+
14
+ def valid?
15
+ raw_body = commit.raw_body
16
+ subject, body = raw_body.split "\n", 2
17
+ return true if !String(subject).empty? && String(body).strip.empty?
18
+
19
+ raw_body.match?(/\A.+\n\n.+/)
20
+ end
21
+
22
+ def issue
23
+ return {} if valid?
24
+
25
+ {hint: "Use blank line between subject and body."}
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Git
4
+ module Lint
5
+ module Analyzers
6
+ class CommitBodyLineLength < Abstract
7
+ def self.defaults
8
+ {
9
+ enabled: true,
10
+ severity: :error,
11
+ length: 72
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: "Use #{length} characters or less per line.",
24
+ lines: affected_commit_body_lines
25
+ }
26
+ end
27
+
28
+ protected
29
+
30
+ def invalid_line? line
31
+ line.length > length
32
+ end
33
+
34
+ private
35
+
36
+ def length
37
+ settings.fetch :length
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Git
4
+ module Lint
5
+ module Analyzers
6
+ class CommitBodyParagraphCapitalization < Abstract
7
+ def self.defaults
8
+ {
9
+ enabled: true,
10
+ severity: :error
11
+ }
12
+ end
13
+
14
+ def self.invalid? line
15
+ line.match?(/\A[[:lower:]].+\Z/m)
16
+ end
17
+
18
+ def valid?
19
+ lowercased_lines.empty?
20
+ end
21
+
22
+ def issue
23
+ return {} if valid?
24
+
25
+ {
26
+ hint: "Capitalize first word.",
27
+ lines: affected_lines
28
+ }
29
+ end
30
+
31
+ private
32
+
33
+ def lowercased_lines
34
+ commit.body_paragraphs.select { |line| self.class.invalid? line }
35
+ end
36
+
37
+ def affected_lines
38
+ klass = self.class
39
+
40
+ commit.body_paragraphs.each.with_object([]).with_index do |(line, lines), index|
41
+ lines << klass.build_issue_line(index, line) if klass.invalid? line
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Git
4
+ module Lint
5
+ module Analyzers
6
+ class CommitBodyPhrase < Abstract
7
+ # rubocop:disable Metrics/MethodLength
8
+ def self.defaults
9
+ {
10
+ enabled: true,
11
+ severity: :error,
12
+ excludes: [
13
+ "absolutely",
14
+ "actually",
15
+ "all intents and purposes",
16
+ "along the lines",
17
+ "at this moment in time",
18
+ "basically",
19
+ "each and every one",
20
+ "everyone knows",
21
+ "fact of the matter",
22
+ "furthermore",
23
+ "however",
24
+ "in due course",
25
+ "in the end",
26
+ "last but not least",
27
+ "matter of fact",
28
+ "obviously",
29
+ "of course",
30
+ "really",
31
+ "simply",
32
+ "things being equal",
33
+ "would like to",
34
+ /\beasy\b/,
35
+ /\bjust\b/,
36
+ /\bquite\b/,
37
+ /as\sfar\sas\s.+\sconcerned/,
38
+ /of\sthe\s(fact|opinion)\sthat/
39
+ ]
40
+ }
41
+ end
42
+ # rubocop:enable Metrics/MethodLength
43
+
44
+ def valid?
45
+ commit.body_lines.all? { |line| !invalid_line? line }
46
+ end
47
+
48
+ def issue
49
+ return {} if valid?
50
+
51
+ {
52
+ hint: %(Avoid: #{filter_list.to_hint}.),
53
+ lines: affected_commit_body_lines
54
+ }
55
+ end
56
+
57
+ protected
58
+
59
+ def load_filter_list
60
+ Kit::FilterList.new settings.fetch(:excludes)
61
+ end
62
+
63
+ def invalid_line? line
64
+ line.downcase.match? Regexp.new(
65
+ Regexp.union(filter_list.to_regexp).source,
66
+ Regexp::IGNORECASE
67
+ )
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Git
4
+ module Lint
5
+ module Analyzers
6
+ class CommitBodyPresence < Abstract
7
+ using Refinements::Strings
8
+
9
+ def self.defaults
10
+ {
11
+ enabled: true,
12
+ severity: :warn,
13
+ minimum: 1
14
+ }
15
+ end
16
+
17
+ def valid?
18
+ return true if commit.fixup?
19
+
20
+ valid_lines = commit.body_lines.reject { |line| line.match?(/^\s*$/) }
21
+ valid_lines.size >= minimum
22
+ end
23
+
24
+ def minimum
25
+ settings.fetch :minimum
26
+ end
27
+
28
+ def issue
29
+ return {} if valid?
30
+
31
+ {hint: %(Use minimum of #{"line".pluralize count: minimum} (non-empty).)}
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Git
4
+ module Lint
5
+ module Analyzers
6
+ class CommitBodySingleBullet < Abstract
7
+ def self.defaults
8
+ {
9
+ enabled: true,
10
+ severity: :error,
11
+ includes: %w[\\-]
12
+ }
13
+ end
14
+
15
+ def valid?
16
+ affected_commit_body_lines.size != 1
17
+ end
18
+
19
+ def issue
20
+ return {} if valid?
21
+
22
+ {
23
+ hint: "Use paragraph instead of single 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#{Regexp.union filter_list.to_regexp}\s+/)
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Git
4
+ module Lint
5
+ module Analyzers
6
+ class CommitSubjectLength < Abstract
7
+ def self.defaults
8
+ {
9
+ enabled: true,
10
+ severity: :error,
11
+ length: 72
12
+ }
13
+ end
14
+
15
+ def valid?
16
+ commit.subject.sub(/(fixup!|squash!)\s{1}/, "").size <= length
17
+ end
18
+
19
+ def issue
20
+ return {} if valid?
21
+
22
+ {hint: "Use #{length} characters or less."}
23
+ end
24
+
25
+ private
26
+
27
+ def length
28
+ settings.fetch :length
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Git
4
+ module Lint
5
+ module Analyzers
6
+ class CommitSubjectPrefix < Abstract
7
+ def self.defaults
8
+ {
9
+ enabled: true,
10
+ severity: :error,
11
+ includes: %w[Fixed Added Updated Removed Refactored]
12
+ }
13
+ end
14
+
15
+ def valid?
16
+ return true if fixup_or_squash?
17
+ return true if filter_list.empty?
18
+
19
+ commit.subject.match?(/\A#{Regexp.union filter_list.to_regexp}/)
20
+ end
21
+
22
+ def issue
23
+ return {} if valid?
24
+
25
+ {hint: %(Use: #{filter_list.to_hint}.)}
26
+ end
27
+
28
+ protected
29
+
30
+ def load_filter_list
31
+ Kit::FilterList.new settings.fetch(:includes)
32
+ end
33
+
34
+ private
35
+
36
+ def fixup_or_squash?
37
+ commit.is_a?(Git::Lint::Commits::Unsaved) && (commit.fixup? || commit.squash?)
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Git
4
+ module Lint
5
+ module Analyzers
6
+ class CommitSubjectSuffix < Abstract
7
+ def self.defaults
8
+ {
9
+ enabled: true,
10
+ severity: :error,
11
+ excludes: [
12
+ "\\.",
13
+ "\\?",
14
+ "\\!"
15
+ ]
16
+ }
17
+ end
18
+
19
+ def valid?
20
+ return true if filter_list.empty?
21
+
22
+ !commit.subject.match?(/#{Regexp.union filter_list.to_regexp}\Z/)
23
+ end
24
+
25
+ def issue
26
+ return {} if valid?
27
+
28
+ {hint: %(Avoid: #{filter_list.to_hint}.)}
29
+ end
30
+
31
+ protected
32
+
33
+ def load_filter_list
34
+ Kit::FilterList.new settings.fetch(:excludes)
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Git
4
+ module Lint
5
+ module Analyzers
6
+ class CommitTrailerCollaboratorCapitalization < Abstract
7
+ def self.defaults
8
+ {
9
+ enabled: true,
10
+ severity: :error
11
+ }
12
+ end
13
+
14
+ # rubocop:disable Metrics/ParameterLists
15
+ def initialize commit:,
16
+ settings: self.class.defaults,
17
+ parser: Parsers::Trailers::Collaborator,
18
+ validator: Validators::Capitalization
19
+ super commit: commit, settings: settings
20
+ @parser = parser
21
+ @validator = validator
22
+ end
23
+ # rubocop:enable Metrics/ParameterLists
24
+
25
+ def valid?
26
+ affected_commit_trailer_lines.empty?
27
+ end
28
+
29
+ def issue
30
+ return {} if valid?
31
+
32
+ {
33
+ hint: "Name must be capitalized.",
34
+ lines: affected_commit_trailer_lines
35
+ }
36
+ end
37
+
38
+ protected
39
+
40
+ def invalid_line? line
41
+ collaborator = parser.new line
42
+ collaborator.match? && !validator.new(collaborator.name.strip).valid?
43
+ end
44
+
45
+ private
46
+
47
+ attr_reader :parser, :validator
48
+ end
49
+ end
50
+ end
51
+ end