summaryrefslogtreecommitdiff
path: root/tooling/danger/base_linter.rb
blob: c58f2d84dc80102d2648fbc71d711f63a8a6ea1b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
# frozen_string_literal: true

require_relative 'title_linting'

module Tooling
  module Danger
    class BaseLinter
      MIN_SUBJECT_WORDS_COUNT = 3
      MAX_LINE_LENGTH = 72

      attr_reader :commit, :problems

      def self.problems_mapping
        {
          subject_too_short: "The %s must contain at least #{MIN_SUBJECT_WORDS_COUNT} words",
          subject_too_long: "The %s may not be longer than #{MAX_LINE_LENGTH} characters",
          subject_starts_with_lowercase: "The %s must start with a capital letter",
          subject_ends_with_a_period: "The %s must not end with a period"
        }
      end

      def self.subject_description
        'commit subject'
      end

      def initialize(commit)
        @commit = commit
        @problems = {}
      end

      def failed?
        problems.any?
      end

      def add_problem(problem_key, *args)
        @problems[problem_key] = sprintf(self.class.problems_mapping[problem_key], *args)
      end

      def lint_subject
        if subject_too_short?
          add_problem(:subject_too_short, self.class.subject_description)
        end

        if subject_too_long?
          add_problem(:subject_too_long, self.class.subject_description)
        end

        if subject_starts_with_lowercase?
          add_problem(:subject_starts_with_lowercase, self.class.subject_description)
        end

        if subject_ends_with_a_period?
          add_problem(:subject_ends_with_a_period, self.class.subject_description)
        end

        self
      end

      private

      def subject
        TitleLinting.remove_draft_flag(message_parts[0])
      end

      def subject_too_short?
        subject.split(' ').length < MIN_SUBJECT_WORDS_COUNT
      end

      def subject_too_long?
        line_too_long?(subject)
      end

      def line_too_long?(line)
        line.length > MAX_LINE_LENGTH
      end

      def subject_starts_with_lowercase?
        return false if ('A'..'Z').cover?(subject[0])

        first_char = subject.sub(/\A(\[.+\]|\w+:)\s/, '')[0]
        first_char_downcased = first_char.downcase
        return true unless ('a'..'z').cover?(first_char_downcased)

        first_char.downcase == first_char
      end

      def subject_ends_with_a_period?
        subject.end_with?('.')
      end

      def message_parts
        @message_parts ||= commit.message.split("\n", 3)
      end
    end
  end
end