git-lint 1.0.0

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