gitlab-dangerfiles 0.1.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.
- checksums.yaml +7 -0
- data/.gitignore +14 -0
- data/.gitlab-ci.yml +43 -0
- data/.gitlab/merge_request_templates/Release.md +35 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +7 -0
- data/Guardfile +70 -0
- data/LICENSE.txt +21 -0
- data/README.md +43 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/fixtures/emojis/aliases.json +542 -0
- data/fixtures/emojis/digests.json +12553 -0
- data/gitlab-dangerfiles.gemspec +38 -0
- data/lib/danger/changelog.rb +39 -0
- data/lib/danger/helper.rb +260 -0
- data/lib/danger/roulette.rb +135 -0
- data/lib/danger/sidekiq_queues.rb +37 -0
- data/lib/gitlab-dangerfiles.rb +1 -0
- data/lib/gitlab/Dangerfile +1 -0
- data/lib/gitlab/dangerfiles.rb +40 -0
- data/lib/gitlab/dangerfiles/bundle_size/Dangerfile +38 -0
- data/lib/gitlab/dangerfiles/ce_ee_vue_templates/Dangerfile +56 -0
- data/lib/gitlab/dangerfiles/changelog/Dangerfile +90 -0
- data/lib/gitlab/dangerfiles/changes_size/Dangerfile +17 -0
- data/lib/gitlab/dangerfiles/commit_linter.rb +226 -0
- data/lib/gitlab/dangerfiles/commit_messages/Dangerfile +135 -0
- data/lib/gitlab/dangerfiles/database/Dangerfile +67 -0
- data/lib/gitlab/dangerfiles/documentation/Dangerfile +29 -0
- data/lib/gitlab/dangerfiles/duplicate_yarn_dependencies/Dangerfile +29 -0
- data/lib/gitlab/dangerfiles/emoji_checker.rb +45 -0
- data/lib/gitlab/dangerfiles/eslint/Dangerfile +31 -0
- data/lib/gitlab/dangerfiles/frozen_string/Dangerfile +28 -0
- data/lib/gitlab/dangerfiles/karma/Dangerfile +51 -0
- data/lib/gitlab/dangerfiles/metadata/Dangerfile +50 -0
- data/lib/gitlab/dangerfiles/popen.rb +55 -0
- data/lib/gitlab/dangerfiles/prettier/Dangerfile +41 -0
- data/lib/gitlab/dangerfiles/roulette/Dangerfile +97 -0
- data/lib/gitlab/dangerfiles/sidekiq_queues/Dangerfile +27 -0
- data/lib/gitlab/dangerfiles/specs/Dangerfile +42 -0
- data/lib/gitlab/dangerfiles/tasks.rb +19 -0
- data/lib/gitlab/dangerfiles/teammate.rb +106 -0
- data/lib/gitlab/dangerfiles/telemetry/Dangerfile +32 -0
- data/lib/gitlab/dangerfiles/utility_css/Dangerfile +51 -0
- data/lib/gitlab/dangerfiles/version.rb +5 -0
- metadata +191 -0
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Danger
|
4
|
+
# Common helper functions for our danger scripts. See Danger::Helper
|
5
|
+
# for more details
|
6
|
+
class SidekiqQueues < Danger::Plugin
|
7
|
+
def changed_queue_files
|
8
|
+
@changed_queue_files ||= git.modified_files.grep(%r{\A(ee/)?app/workers/all_queues\.yml})
|
9
|
+
end
|
10
|
+
|
11
|
+
def added_queue_names
|
12
|
+
@added_queue_names ||= new_queues.keys - old_queues.keys
|
13
|
+
end
|
14
|
+
|
15
|
+
def changed_queue_names
|
16
|
+
@changed_queue_names ||=
|
17
|
+
(new_queues.values_at(*old_queues.keys) - old_queues.values)
|
18
|
+
.compact.map { |queue| queue[:name] }
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def old_queues
|
24
|
+
@old_queues ||= queues_for(gitlab.base_commit)
|
25
|
+
end
|
26
|
+
|
27
|
+
def new_queues
|
28
|
+
@new_queues ||= queues_for(gitlab.head_commit)
|
29
|
+
end
|
30
|
+
|
31
|
+
def queues_for(branch)
|
32
|
+
changed_queue_files
|
33
|
+
.flat_map { |file| YAML.safe_load(`git show #{branch}:#{file}`, permitted_classes: [Symbol]) }
|
34
|
+
.to_h { |queue| [queue[:name], queue] }
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require_relative "gitlab/dangerfiles"
|
@@ -0,0 +1 @@
|
|
1
|
+
danger.import_plugin(File.expand_path("../danger/*.rb", __dir__))
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require "gitlab/dangerfiles/version"
|
2
|
+
|
3
|
+
module Gitlab
|
4
|
+
module Dangerfiles
|
5
|
+
LOCAL_RULES ||= %w[
|
6
|
+
changes_size
|
7
|
+
commit_messages
|
8
|
+
database
|
9
|
+
documentation
|
10
|
+
duplicate_yarn_dependencies
|
11
|
+
eslint
|
12
|
+
frozen_string
|
13
|
+
karma
|
14
|
+
prettier
|
15
|
+
telemetry
|
16
|
+
utility_css
|
17
|
+
].freeze
|
18
|
+
CI_ONLY_RULES ||= %w[
|
19
|
+
ce_ee_vue_templates
|
20
|
+
changelog
|
21
|
+
metadata
|
22
|
+
roulette
|
23
|
+
sidekiq_queues
|
24
|
+
specs
|
25
|
+
].freeze
|
26
|
+
MESSAGE_PREFIX = "==>".freeze
|
27
|
+
|
28
|
+
def self.load_tasks
|
29
|
+
require "gitlab/dangerfiles/tasks"
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.local_warning_message
|
33
|
+
"#{MESSAGE_PREFIX} Only the following Danger rules can be run locally: #{LOCAL_RULES.join(", ")}"
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.success_message
|
37
|
+
"#{MESSAGE_PREFIX} No Danger rule violations!"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
analysis_result = "./bundle-size-review/analysis.json"
|
4
|
+
markdown_result = "./bundle-size-review/comparison.md"
|
5
|
+
|
6
|
+
# Executing the webpack-entry-point-analyser
|
7
|
+
# We would like to do that in the CI file directly,
|
8
|
+
# but unfortunately the head_commit SHA is not available
|
9
|
+
# as a CI variable due to our merge into master simulation
|
10
|
+
analyze_cmd = [
|
11
|
+
"webpack-entry-point-analyser",
|
12
|
+
"--from-file ./webpack-report/stats.json",
|
13
|
+
"--json #{analysis_result}",
|
14
|
+
" --sha #{gitlab&.head_commit}"
|
15
|
+
].join(" ")
|
16
|
+
|
17
|
+
# execute analysis
|
18
|
+
`#{analyze_cmd}`
|
19
|
+
|
20
|
+
# We are executing the comparison by comparing the start_sha
|
21
|
+
# to the current pipeline result. The start_sha is the commit
|
22
|
+
# from master that was merged into for the merged pipeline.
|
23
|
+
comparison_cmd = [
|
24
|
+
"webpack-compare-reports",
|
25
|
+
"--job #{ENV["CI_JOB_ID"]}",
|
26
|
+
"--to-file #{analysis_result}",
|
27
|
+
"--html ./bundle-size-review/comparison.html",
|
28
|
+
"--markdown #{markdown_result}"
|
29
|
+
].join(" ")
|
30
|
+
|
31
|
+
# execute comparison
|
32
|
+
`#{comparison_cmd}`
|
33
|
+
|
34
|
+
comment = `cat #{markdown_result}`
|
35
|
+
|
36
|
+
markdown(<<~MARKDOWN)
|
37
|
+
#{comment}
|
38
|
+
MARKDOWN
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'cgi'
|
3
|
+
|
4
|
+
def get_vue_files_with_ce_and_ee_versions(files)
|
5
|
+
files.select do |file|
|
6
|
+
if file.end_with?('.vue')
|
7
|
+
counterpart_path = if file.start_with?('ee/')
|
8
|
+
file.delete_prefix('ee/')
|
9
|
+
else
|
10
|
+
"ee/#{file}"
|
11
|
+
end
|
12
|
+
|
13
|
+
escaped_path = CGI.escape(counterpart_path)
|
14
|
+
api_endpoint = "https://gitlab.com/api/v4/projects/gitlab-org%2Fgitlab-ee/repository/files/#{escaped_path}?ref=master"
|
15
|
+
response = HTTParty.get(api_endpoint) # rubocop:disable Gitlab/HTTParty
|
16
|
+
response.code != 404
|
17
|
+
else
|
18
|
+
false
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
vue_candidates = get_vue_files_with_ce_and_ee_versions(helper.all_changed_files)
|
24
|
+
|
25
|
+
return if vue_candidates.empty?
|
26
|
+
|
27
|
+
message 'This merge request includes changes to Vue files that have both CE and EE versions.'
|
28
|
+
|
29
|
+
markdown(<<~MARKDOWN)
|
30
|
+
## Vue `<template>` in CE and EE
|
31
|
+
|
32
|
+
Some Vue files in CE have a counterpart in EE.
|
33
|
+
(For example, `path/to/file.vue` and `ee/path/to/file.vue`.)
|
34
|
+
|
35
|
+
When run in the context of CE, the `<template>` of the CE Vue file is used.
|
36
|
+
When run in the context of EE, the `<template>` of the EE Vue file is used.
|
37
|
+
|
38
|
+
It's easy to accidentally make a change to a CE `<template>` that _should_
|
39
|
+
appear in both CE and EE without making the change in both places.
|
40
|
+
When this happens, the change only takes effect in CE.
|
41
|
+
|
42
|
+
The following Vue files were changed as part of this merge request that
|
43
|
+
include both a CE and EE version of the file:
|
44
|
+
|
45
|
+
* #{vue_candidates.map { |path| "`#{path}`" }.join("\n* ")}
|
46
|
+
|
47
|
+
If you made a change to the `<template>` of any of these Vue files that
|
48
|
+
should be visible in both CE and EE, please ensure you have made your
|
49
|
+
change to both versions of the file.
|
50
|
+
|
51
|
+
### A better alternative
|
52
|
+
|
53
|
+
An even _better_ alternative is to refactor this component to only use
|
54
|
+
a single template for both CE and EE. More info on this approach here:
|
55
|
+
https://docs.gitlab.com/ee/development/ee_features.html#template-tag
|
56
|
+
MARKDOWN
|
@@ -0,0 +1,90 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# rubocop:disable Style/SignalException
|
3
|
+
|
4
|
+
require 'yaml'
|
5
|
+
|
6
|
+
SEE_DOC = "See the [changelog documentation](https://docs.gitlab.com/ee/development/changelog.html)."
|
7
|
+
CREATE_CHANGELOG_MESSAGE = <<~MSG
|
8
|
+
If you want to create a changelog entry for GitLab FOSS, run the following:
|
9
|
+
|
10
|
+
```
|
11
|
+
bin/changelog -m %<mr_iid>s "%<mr_title>s"
|
12
|
+
```
|
13
|
+
|
14
|
+
If you want to create a changelog entry for GitLab EE, run the following instead:
|
15
|
+
|
16
|
+
```
|
17
|
+
bin/changelog --ee -m %<mr_iid>s "%<mr_title>s"
|
18
|
+
```
|
19
|
+
|
20
|
+
If this merge request [doesn't need a CHANGELOG entry](https://docs.gitlab.com/ee/development/changelog.html#what-warrants-a-changelog-entry), feel free to ignore this message.
|
21
|
+
MSG
|
22
|
+
|
23
|
+
SUGGEST_MR_COMMENT = <<~SUGGEST_COMMENT
|
24
|
+
```suggestion
|
25
|
+
merge_request: %<mr_iid>s
|
26
|
+
```
|
27
|
+
|
28
|
+
#{SEE_DOC}
|
29
|
+
SUGGEST_COMMENT
|
30
|
+
|
31
|
+
def check_changelog_yaml(path)
|
32
|
+
raw_file = File.read(path)
|
33
|
+
yaml = YAML.safe_load(raw_file)
|
34
|
+
|
35
|
+
fail "`title` should be set, in #{helper.html_link(path)}! #{SEE_DOC}" if yaml["title"].nil?
|
36
|
+
fail "`type` should be set, in #{helper.html_link(path)}! #{SEE_DOC}" if yaml["type"].nil?
|
37
|
+
|
38
|
+
return if helper.security_mr?
|
39
|
+
|
40
|
+
cherry_pick_against_stable_branch = helper.cherry_pick_mr? && helper.stable_branch?
|
41
|
+
|
42
|
+
if yaml["merge_request"].nil?
|
43
|
+
mr_line = raw_file.lines.find_index("merge_request:\n")
|
44
|
+
|
45
|
+
if mr_line
|
46
|
+
markdown(format(SUGGEST_MR_COMMENT, mr_iid: gitlab.mr_json["iid"]), file: path, line: mr_line.succ)
|
47
|
+
else
|
48
|
+
message "Consider setting `merge_request` to #{gitlab.mr_json["iid"]} in #{helper.html_link(path)}. #{SEE_DOC}"
|
49
|
+
end
|
50
|
+
elsif yaml["merge_request"] != gitlab.mr_json["iid"] && !cherry_pick_against_stable_branch
|
51
|
+
fail "Merge request ID was not set to #{gitlab.mr_json["iid"]}! #{SEE_DOC}"
|
52
|
+
end
|
53
|
+
rescue Psych::SyntaxError, Psych::DisallowedClass, Psych::BadAlias
|
54
|
+
# YAML could not be parsed, fail the build.
|
55
|
+
fail "#{helper.html_link(path)} isn't valid YAML! #{SEE_DOC}"
|
56
|
+
rescue StandardError => e
|
57
|
+
warn "There was a problem trying to check the Changelog. Exception: #{e.class.name} - #{e.message}"
|
58
|
+
end
|
59
|
+
|
60
|
+
def check_changelog_path(path)
|
61
|
+
ee_changes = helper.all_ee_changes.dup
|
62
|
+
ee_changes.delete(path)
|
63
|
+
|
64
|
+
if ee_changes.any? && !changelog.ee_changelog?
|
65
|
+
warn "This MR has a Changelog file outside `ee/`, but code changes in `ee/`. Consider moving the Changelog file into `ee/`."
|
66
|
+
end
|
67
|
+
|
68
|
+
if ee_changes.empty? && changelog.ee_changelog?
|
69
|
+
warn "This MR has a Changelog file in `ee/`, but no code changes in `ee/`. Consider moving the Changelog file outside `ee/`."
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def sanitized_mr_title
|
74
|
+
helper.sanitize_mr_title(gitlab.mr_json["title"])
|
75
|
+
end
|
76
|
+
|
77
|
+
if git.modified_files.include?("CHANGELOG.md")
|
78
|
+
fail "**CHANGELOG.md was edited.** Please remove the additions and create a CHANGELOG entry.\n\n" +
|
79
|
+
format(CREATE_CHANGELOG_MESSAGE, mr_iid: gitlab.mr_json["iid"], mr_title: sanitized_mr_title)
|
80
|
+
end
|
81
|
+
|
82
|
+
changelog_found = changelog.found
|
83
|
+
|
84
|
+
if changelog_found
|
85
|
+
check_changelog_yaml(changelog_found)
|
86
|
+
check_changelog_path(changelog_found)
|
87
|
+
elsif changelog.needed?
|
88
|
+
message "**[CHANGELOG missing](https://docs.gitlab.com/ee/development/changelog.html)**:\n\n" +
|
89
|
+
format(CREATE_CHANGELOG_MESSAGE, mr_iid: gitlab.mr_json["iid"], mr_title: sanitized_mr_title)
|
90
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# FIXME: git.info_for_file raises the following error
|
2
|
+
# /usr/local/bundle/gems/git-1.4.0/lib/git/lib.rb:956:in `command': (Danger::DSLError)
|
3
|
+
# [!] Invalid `Dangerfile` file:
|
4
|
+
# [!] Invalid `Dangerfile` file: git '--git-dir=/builds/gitlab-org/gitlab/.git' '--work-tree=/builds/gitlab-org/gitlab' cat-file '-t' '' 2>&1:fatal: Not a valid object name
|
5
|
+
# This seems to be the same as https://github.com/danger/danger/issues/535.
|
6
|
+
|
7
|
+
# locale_files_updated = git.modified_files.select { |path| path.start_with?('locale') }
|
8
|
+
# locale_files_updated.each do |locale_file_updated|
|
9
|
+
# git_stats = git.info_for_file(locale_file_updated)
|
10
|
+
# message "Git stats for #{locale_file_updated}: #{git_stats[:insertions]} insertions, #{git_stats[:deletions]} insertions"
|
11
|
+
# end
|
12
|
+
|
13
|
+
if git.lines_of_code > 2_000
|
14
|
+
warn "This merge request is definitely too big (more than #{git.lines_of_code} lines changed), please split it into multiple merge requests."
|
15
|
+
elsif git.lines_of_code > 500
|
16
|
+
warn "This merge request is quite big (more than #{git.lines_of_code} lines changed), please consider splitting it into multiple merge requests."
|
17
|
+
end
|
@@ -0,0 +1,226 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
emoji_checker_path = File.expand_path("emoji_checker", __dir__)
|
4
|
+
defined?(Rails) ? require_dependency(emoji_checker_path) : require_relative(emoji_checker_path)
|
5
|
+
|
6
|
+
module Gitlab
|
7
|
+
module Dangerfiles
|
8
|
+
class CommitLinter
|
9
|
+
MIN_SUBJECT_WORDS_COUNT = 3
|
10
|
+
MAX_LINE_LENGTH = 72
|
11
|
+
MAX_CHANGED_FILES_IN_COMMIT = 3
|
12
|
+
MAX_CHANGED_LINES_IN_COMMIT = 30
|
13
|
+
SHORT_REFERENCE_REGEX = %r{([\w\-\/]+)?(#|!|&|%)\d+\b}.freeze
|
14
|
+
DEFAULT_SUBJECT_DESCRIPTION = "commit subject"
|
15
|
+
WIP_PREFIX = "WIP: "
|
16
|
+
PROBLEMS = {
|
17
|
+
subject_too_short: "The %s must contain at least #{MIN_SUBJECT_WORDS_COUNT} words",
|
18
|
+
subject_too_long: "The %s may not be longer than #{MAX_LINE_LENGTH} characters",
|
19
|
+
subject_starts_with_lowercase: "The %s must start with a capital letter",
|
20
|
+
subject_ends_with_a_period: "The %s must not end with a period",
|
21
|
+
separator_missing: "The commit subject and body must be separated by a blank line",
|
22
|
+
details_too_many_changes: "Commits that change #{MAX_CHANGED_LINES_IN_COMMIT} or more lines across " \
|
23
|
+
"at least #{MAX_CHANGED_FILES_IN_COMMIT} files must describe these changes in the commit body",
|
24
|
+
details_line_too_long: "The commit body should not contain more than #{MAX_LINE_LENGTH} characters per line",
|
25
|
+
message_contains_text_emoji: "Avoid the use of Markdown Emoji such as `:+1:`. These add limited value " \
|
26
|
+
"to the commit message, and are displayed as plain text outside of GitLab",
|
27
|
+
message_contains_unicode_emoji: "Avoid the use of Unicode Emoji. These add no value to the commit " \
|
28
|
+
"message, and may not be displayed properly everywhere",
|
29
|
+
message_contains_short_reference: "Use full URLs instead of short references (`gitlab-org/gitlab#123` or " \
|
30
|
+
"`!123`), as short references are displayed as plain text outside of GitLab",
|
31
|
+
}.freeze
|
32
|
+
|
33
|
+
attr_reader :commit, :problems
|
34
|
+
|
35
|
+
def initialize(commit)
|
36
|
+
@commit = commit
|
37
|
+
@problems = {}
|
38
|
+
@linted = false
|
39
|
+
end
|
40
|
+
|
41
|
+
def fixup?
|
42
|
+
commit.message.start_with?("fixup!", "squash!")
|
43
|
+
end
|
44
|
+
|
45
|
+
def suggestion?
|
46
|
+
commit.message.start_with?("Apply suggestion to")
|
47
|
+
end
|
48
|
+
|
49
|
+
def merge?
|
50
|
+
commit.message.start_with?("Merge branch")
|
51
|
+
end
|
52
|
+
|
53
|
+
def revert?
|
54
|
+
commit.message.start_with?('Revert "')
|
55
|
+
end
|
56
|
+
|
57
|
+
def multi_line?
|
58
|
+
!details.nil? && !details.empty?
|
59
|
+
end
|
60
|
+
|
61
|
+
def failed?
|
62
|
+
problems.any?
|
63
|
+
end
|
64
|
+
|
65
|
+
def add_problem(problem_key, *args)
|
66
|
+
@problems[problem_key] = sprintf(PROBLEMS[problem_key], *args)
|
67
|
+
end
|
68
|
+
|
69
|
+
def lint(subject_description = "commit subject")
|
70
|
+
return self if @linted
|
71
|
+
|
72
|
+
@linted = true
|
73
|
+
lint_subject(subject_description)
|
74
|
+
lint_separator
|
75
|
+
lint_details
|
76
|
+
lint_message
|
77
|
+
|
78
|
+
self
|
79
|
+
end
|
80
|
+
|
81
|
+
def lint_subject(subject_description)
|
82
|
+
if subject_too_short?
|
83
|
+
add_problem(:subject_too_short, subject_description)
|
84
|
+
end
|
85
|
+
|
86
|
+
if subject_too_long?
|
87
|
+
add_problem(:subject_too_long, subject_description)
|
88
|
+
end
|
89
|
+
|
90
|
+
if subject_starts_with_lowercase?
|
91
|
+
add_problem(:subject_starts_with_lowercase, subject_description)
|
92
|
+
end
|
93
|
+
|
94
|
+
if subject_ends_with_a_period?
|
95
|
+
add_problem(:subject_ends_with_a_period, subject_description)
|
96
|
+
end
|
97
|
+
|
98
|
+
self
|
99
|
+
end
|
100
|
+
|
101
|
+
private
|
102
|
+
|
103
|
+
def lint_separator
|
104
|
+
return self unless separator && !separator.empty?
|
105
|
+
|
106
|
+
add_problem(:separator_missing)
|
107
|
+
|
108
|
+
self
|
109
|
+
end
|
110
|
+
|
111
|
+
def lint_details
|
112
|
+
if !multi_line? && many_changes?
|
113
|
+
add_problem(:details_too_many_changes)
|
114
|
+
end
|
115
|
+
|
116
|
+
details&.each_line do |line|
|
117
|
+
line = line.strip
|
118
|
+
|
119
|
+
next unless line_too_long?(line)
|
120
|
+
|
121
|
+
url_size = line.scan(%r((https?://\S+))).sum { |(url)| url.length } # rubocop:disable CodeReuse/ActiveRecord
|
122
|
+
|
123
|
+
# If the line includes a URL, we'll allow it to exceed MAX_LINE_LENGTH characters, but
|
124
|
+
# only if the line _without_ the URL does not exceed this limit.
|
125
|
+
next unless line_too_long?(line.length - url_size)
|
126
|
+
|
127
|
+
add_problem(:details_line_too_long)
|
128
|
+
break
|
129
|
+
end
|
130
|
+
|
131
|
+
self
|
132
|
+
end
|
133
|
+
|
134
|
+
def lint_message
|
135
|
+
if message_contains_text_emoji?
|
136
|
+
add_problem(:message_contains_text_emoji)
|
137
|
+
end
|
138
|
+
|
139
|
+
if message_contains_unicode_emoji?
|
140
|
+
add_problem(:message_contains_unicode_emoji)
|
141
|
+
end
|
142
|
+
|
143
|
+
if message_contains_short_reference?
|
144
|
+
add_problem(:message_contains_short_reference)
|
145
|
+
end
|
146
|
+
|
147
|
+
self
|
148
|
+
end
|
149
|
+
|
150
|
+
def files_changed
|
151
|
+
commit.diff_parent.stats[:total][:files]
|
152
|
+
end
|
153
|
+
|
154
|
+
def lines_changed
|
155
|
+
commit.diff_parent.stats[:total][:lines]
|
156
|
+
end
|
157
|
+
|
158
|
+
def many_changes?
|
159
|
+
files_changed > MAX_CHANGED_FILES_IN_COMMIT && lines_changed > MAX_CHANGED_LINES_IN_COMMIT
|
160
|
+
end
|
161
|
+
|
162
|
+
def subject
|
163
|
+
message_parts[0].delete_prefix(WIP_PREFIX)
|
164
|
+
end
|
165
|
+
|
166
|
+
def separator
|
167
|
+
message_parts[1]
|
168
|
+
end
|
169
|
+
|
170
|
+
def details
|
171
|
+
message_parts[2]&.gsub(/^Signed-off-by.*$/, "")
|
172
|
+
end
|
173
|
+
|
174
|
+
def line_too_long?(line)
|
175
|
+
case line
|
176
|
+
when String
|
177
|
+
line.length > MAX_LINE_LENGTH
|
178
|
+
when Integer
|
179
|
+
line > MAX_LINE_LENGTH
|
180
|
+
else
|
181
|
+
raise ArgumentError, "The line argument (#{line}) should be a String or an Integer! #{line.class} given."
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
def subject_too_short?
|
186
|
+
subject.split(" ").length < MIN_SUBJECT_WORDS_COUNT
|
187
|
+
end
|
188
|
+
|
189
|
+
def subject_too_long?
|
190
|
+
line_too_long?(subject)
|
191
|
+
end
|
192
|
+
|
193
|
+
def subject_starts_with_lowercase?
|
194
|
+
first_char = subject.sub(/\A\[.+\]\s/, "")[0]
|
195
|
+
first_char_downcased = first_char.downcase
|
196
|
+
return true unless ("a".."z").cover?(first_char_downcased)
|
197
|
+
|
198
|
+
first_char.downcase == first_char
|
199
|
+
end
|
200
|
+
|
201
|
+
def subject_ends_with_a_period?
|
202
|
+
subject.end_with?(".")
|
203
|
+
end
|
204
|
+
|
205
|
+
def message_contains_text_emoji?
|
206
|
+
emoji_checker.includes_text_emoji?(commit.message)
|
207
|
+
end
|
208
|
+
|
209
|
+
def message_contains_unicode_emoji?
|
210
|
+
emoji_checker.includes_unicode_emoji?(commit.message)
|
211
|
+
end
|
212
|
+
|
213
|
+
def message_contains_short_reference?
|
214
|
+
commit.message.match?(SHORT_REFERENCE_REGEX)
|
215
|
+
end
|
216
|
+
|
217
|
+
def emoji_checker
|
218
|
+
@emoji_checker ||= Gitlab::Dangerfiles::EmojiChecker.new
|
219
|
+
end
|
220
|
+
|
221
|
+
def message_parts
|
222
|
+
@message_parts ||= commit.message.split("\n", 3)
|
223
|
+
end
|
224
|
+
end
|
225
|
+
end
|
226
|
+
end
|