Class: Danger::CommitMessages

Inherits:
Plugin
  • Object
show all
Defined in:
lib/danger/plugins/commit_messages.rb

Overview

Common helper functions for our danger scripts. See Danger::Helper for more details

Constant Summary collapse

COMMIT_MSG_DOC =
"https://docs.gitlab.com/ee/development/contributing/merge_request_workflow.html#commit-messages-guidelines"
MORE_INFO =
"For more information, take a look at our [Commit message guidelines](#{COMMIT_MSG_DOC}).".freeze
THE_DANGER_JOB_TEXT =
"the `%<job_name>s` job"
MAX_COMMITS_COUNT_EXCEEDED_MESSAGE =
<<~MSG
  This merge request includes more than %<max_commits_count>d commits. Each commit should meet the following criteria:

  1. Have a well-written commit message.
  1. Has all tests passing when used on its own (e.g. when using git checkout SHA).
  1. Can be reverted on its own without also requiring the revert of commit that came before it.
  1. Is small enough that it can be reviewed in isolation in under 30 minutes or so.

  If this merge request contains commits that do not meet this criteria and/or contains intermediate work, please rebase these commits into a smaller number of commits or split this merge request into multiple smaller merge requests.
MSG

Instance Method Summary collapse

Instance Method Details

#build_message(commit, message, more_info: true) ⇒ Object



24
25
26
27
28
29
# File 'lib/danger/plugins/commit_messages.rb', line 24

def build_message(commit, message, more_info: true)
  [message].tap do |full_message|
    full_message << ". #{MORE_INFO}" if more_info
    full_message.unshift("#{commit.sha}: ") if commit.sha
  end.join
end

#count_non_fixup_commits(commit_linters) ⇒ Object



97
98
99
# File 'lib/danger/plugins/commit_messages.rb', line 97

def count_non_fixup_commits(commit_linters)
  commit_linters.count { |commit_linter| !commit_linter.fixup? }
end


39
40
41
42
43
44
45
# File 'lib/danger/plugins/commit_messages.rb', line 39

def danger_job_link
  if helper.ci?
    "[#{format(THE_DANGER_JOB_TEXT, job_name: ENV.fetch('CI_JOB_NAME', nil))}](#{ENV.fetch('CI_JOB_URL', nil)})"
  else
    THE_DANGER_JOB_TEXT
  end
end

#fail_commit(commit, message, more_info: true) ⇒ Object



31
32
33
# File 'lib/danger/plugins/commit_messages.rb', line 31

def fail_commit(commit, message, more_info: true)
  self.fail(build_message(commit, message, more_info: more_info))
end

#lint_commit(commit) ⇒ Object

Perform various checks against commits. We’re not using github.com/jonallured/danger-commit_lint because its output is not very helpful, and it doesn’t offer the means of ignoring merge commits.



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
# File 'lib/danger/plugins/commit_messages.rb', line 50

def lint_commit(commit)
  linter = Gitlab::Dangerfiles::CommitLinter.new(commit)

  # For now we'll ignore merge commits, as getting rid of those is a problem
  # separate from enforcing good commit messages.
  return linter if linter.merge?

  # We ignore revert commits as they are well structured by Git already
  return linter if linter.revert?

  # If MR is set to squash, we ignore fixup commits
  return linter if linter.fixup? && helper.squash_mr?

  if linter.fixup?
    msg = "Squash or fixup commits must be squashed before merge, or **edit** the merge request, " \
          "enable **Squash commits when merge request is accepted** and re-run #{danger_job_link}."

    if helper.draft_mr? || helper.squash_mr?
      warn_commit(commit, msg, more_info: false)
    else
      fail_commit(commit, msg, more_info: false)
    end

    # Makes no sense to process other rules for fixup commits, they trigger just more noise
    return linter
  end

  # Fail if a suggestion commit is used and squash is not enabled
  if linter.suggestion?
    unless helper.squash_mr?
      msg = "If you are applying suggestions, **edit** the merge request, enable **Squash commits when " \
            "merge request is accepted** and re-run #{danger_job_link}."
      fail_commit(commit, msg, more_info: false)
    end

    return linter
  end

  linter.lint
end

#lint_commits(commits) ⇒ Object



101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
# File 'lib/danger/plugins/commit_messages.rb', line 101

def lint_commits(commits)
  commit_linters = commits.map { |commit| lint_commit(commit) }

  if helper.squash_mr?
    multi_line_commit_linter = commit_linters.detect do |commit_linter|
      !commit_linter.merge? && commit_linter.multi_line?
    end

    if multi_line_commit_linter&.failed?
      warn_or_fail_commits(multi_line_commit_linter)
      commit_linters.delete(multi_line_commit_linter) # Don't show an error (here) and a warning (below)
    end
  elsif count_non_fixup_commits(commit_linters) > max_commits_count
    self.warn(format(MAX_COMMITS_COUNT_EXCEEDED_MESSAGE, max_commits_count: max_commits_count))
  end

  failed_commit_linters = commit_linters.select(&:failed?)
  warn_or_fail_commits(failed_commit_linters, default_to_fail: !helper.squash_mr?)
end

#lint_mr_title(mr_title) ⇒ Object



91
92
93
94
95
# File 'lib/danger/plugins/commit_messages.rb', line 91

def lint_mr_title(mr_title)
  commit = Struct.new(:message, :sha).new(mr_title)

  Gitlab::Dangerfiles::MergeRequestLinter.new(commit).lint
end

#max_commits_countObject



121
122
123
# File 'lib/danger/plugins/commit_messages.rb', line 121

def max_commits_count
  helper.config.max_commits_count
end

#warn_commit(commit, message, more_info: true) ⇒ Object



35
36
37
# File 'lib/danger/plugins/commit_messages.rb', line 35

def warn_commit(commit, message, more_info: true)
  self.warn(build_message(commit, message, more_info: more_info))
end

#warn_or_fail_commits(failed_linters, default_to_fail: true) ⇒ Object



125
126
127
128
129
130
131
132
133
134
135
136
137
138
# File 'lib/danger/plugins/commit_messages.rb', line 125

def warn_or_fail_commits(failed_linters, default_to_fail: true)
  level = default_to_fail ? :fail : :warn

  Array(failed_linters).each do |linter|
    linter.problems.each do |problem_key, problem_desc|
      case problem_key
      when :subject_too_short, :details_too_many_changes, :details_line_too_long
        warn_commit(linter.commit, problem_desc)
      else
        __send__(:"#{level}_commit", linter.commit, problem_desc)
      end
    end
  end
end