gitlab-styles 4.2.0 → 5.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/.editorconfig +18 -0
  3. data/.gitlab-ci.yml +5 -16
  4. data/.gitlab/merge_request_templates/New Static Analysis Check.md +21 -0
  5. data/.gitlab/merge_request_templates/Release.md +4 -4
  6. data/.rubocop.yml +3 -1
  7. data/Gemfile +2 -2
  8. data/gitlab-styles.gemspec +6 -5
  9. data/lib/gitlab/styles/rubocop.rb +3 -14
  10. data/lib/gitlab/styles/rubocop/cop/active_record_dependent.rb +1 -1
  11. data/lib/gitlab/styles/rubocop/cop/code_reuse/active_record.rb +131 -0
  12. data/lib/gitlab/styles/rubocop/cop/custom_error_class.rb +6 -3
  13. data/lib/gitlab/styles/rubocop/cop/gem_fetcher.rb +1 -1
  14. data/lib/gitlab/styles/rubocop/cop/line_break_after_guard_clauses.rb +1 -1
  15. data/lib/gitlab/styles/rubocop/cop/line_break_around_conditional_block.rb +132 -0
  16. data/lib/gitlab/styles/rubocop/cop/migration/update_large_table.rb +1 -1
  17. data/lib/gitlab/styles/rubocop/cop/polymorphic_associations.rb +1 -1
  18. data/lib/gitlab/styles/rubocop/cop/rspec/base.rb +18 -0
  19. data/lib/gitlab/styles/rubocop/cop/rspec/empty_line_after_let_block.rb +65 -0
  20. data/lib/gitlab/styles/rubocop/cop/rspec/empty_line_after_shared_example.rb +6 -6
  21. data/lib/gitlab/styles/rubocop/cop/rspec/example_starting_character.rb +124 -0
  22. data/lib/gitlab/styles/rubocop/cop/rspec/have_link_parameters.rb +10 -5
  23. data/lib/gitlab/styles/rubocop/cop/rspec/single_line_hook.rb +3 -2
  24. data/lib/gitlab/styles/rubocop/cop/rspec/verbose_include_metadata.rb +10 -13
  25. data/lib/gitlab/styles/rubocop/model_helpers.rb +1 -1
  26. data/lib/gitlab/styles/rubocop/rspec/helpers.rb +17 -0
  27. data/lib/gitlab/styles/version.rb +1 -1
  28. data/rubocop-all.yml +1 -0
  29. data/rubocop-bundler.yml +1 -0
  30. data/rubocop-code_reuse.yml +24 -0
  31. data/rubocop-default.yml +1 -0
  32. data/rubocop-gemspec.yml +1 -0
  33. data/rubocop-layout.yml +6 -0
  34. data/rubocop-lint.yml +63 -5
  35. data/rubocop-metrics.yml +1 -0
  36. data/rubocop-migrations.yml +1 -0
  37. data/rubocop-naming.yml +1 -0
  38. data/rubocop-performance.yml +48 -0
  39. data/rubocop-rails.yml +79 -0
  40. data/rubocop-rspec.yml +10 -0
  41. data/rubocop-security.yml +1 -0
  42. data/rubocop-style.yml +91 -1
  43. metadata +23 -15
  44. data/.rubocop_todo.yml +0 -7
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 86448d470052f87f4e8cac92516a549a08101b13ce226f9f409bbab6577eae8d
4
- data.tar.gz: 4b4e3ad68914fcdd80e5a9d208076b697237bab2d2f469b417f985eb358f380a
3
+ metadata.gz: 53b2719f9b6600bdfd8b362ad363f86f35dbae708f8ebd41f1ef2e2429d4c4f0
4
+ data.tar.gz: f480238835c2218118ea34ac3ebf2869e2386e500304530fd08a112701175845
5
5
  SHA512:
6
- metadata.gz: 5be83ec65d0f6f4f019a2357d730d1c28071c40e20f230e28fcb4caa044c16c18cc0a43f6b1dd2d9bbd6e7abfe70f42dd7b5a2b25230c1ae3bfadb932169050d
7
- data.tar.gz: a7b2d85615687f1e8bcf85b6a89da0c1e1f93e27ea4c9bca938dbc51cb37d4287f4b0a52792232650050db25a7d7dff7e11f6fa8a7f5ad0c763f82800aab660f
6
+ metadata.gz: f5d87f035504e911546c7aaaaad23bac44035f65848d0a3c74cf284bdddd4e8f460c6da7e4766ee9465f75051d38d5e1d3c4b674bc910ce320204e356a9f3622
7
+ data.tar.gz: b97be714bb886604639de5d788284b0c8af1b081ce279c8a5fdf6dafff9b2aeee83060576f0ce0322bb49168ccdc58a4abafd0de13442425d705523d9d6a6a5d
@@ -0,0 +1,18 @@
1
+ # top-most EditorConfig file
2
+ root = true
3
+
4
+ # Unix-style newlines with a newline ending every file
5
+ [*]
6
+ end_of_line = lf
7
+ trim_trailing_whitespace = true
8
+ insert_final_newline = true
9
+
10
+ [*.{rb,yml}]
11
+ indent_size = 2
12
+
13
+ [*.{rb,yml,md}]
14
+ indent_style = space
15
+ charset = utf-8
16
+
17
+ [*.{md,markdown}]
18
+ trim_trailing_whitespace = false
@@ -1,9 +1,9 @@
1
1
  stages:
2
- - release
3
2
  - test
3
+ - deploy
4
4
 
5
5
  default:
6
- image: ruby:2.6
6
+ image: ruby:2.7
7
7
  tags:
8
8
  - gitlab-org
9
9
  before_script:
@@ -29,17 +29,6 @@ specs:
29
29
  script:
30
30
  - bundle exec rspec
31
31
 
32
- release:
33
- stage: release
34
- rules:
35
- - if: '$CI_COMMIT_TAG'
36
- script:
37
- - gem update --system
38
- - ruby --version
39
- - gem env version
40
- - gem build gitlab-styles.gemspec
41
- - gem push gitlab-styles*.gem
42
- artifacts:
43
- paths:
44
- - gitlab-styles*.gem
45
- expire_in: 30 days
32
+ include:
33
+ - project: 'gitlab-org/quality/pipeline-common'
34
+ file: '/ci/gem-release.yml'
@@ -0,0 +1,21 @@
1
+ ## Description of the proposal
2
+
3
+ <!--
4
+ Please describe the proposal and add a link to the source (for example, http://www.betterspecs.org/).
5
+ -->
6
+
7
+ ### Check-list
8
+
9
+ - [ ] Mention this proposal in the relevant Slack channels (e.g. `#development`, `#backend`, `#frontend`)
10
+ - [ ] If there is a choice to make between two potential styles, set up an emoji vote in the MR:
11
+ - CHOICE_A: :a:
12
+ - CHOICE_B: :b:
13
+ - Vote yourself for both choices so that people know these are the choices
14
+ - [ ] The MR doesn't have significant objections, and is getting a majority of :+1: vs :-1: (remember that [we don't need to reach a consensus](https://about.gitlab.com/handbook/values/#collaboration-is-not-consensus))
15
+ - [ ] (If applicable) One style is getting a majority of vote (compared to the other choice)
16
+ - [ ] (If applicable) Update the MR with the chosen style
17
+ - [ ] Follow the [review process](https://docs.gitlab.com/ee/development/code_review.html) as usual
18
+
19
+ /label ~"Engineering Productivity" ~"development guidelines" ~"static code analysis"
20
+
21
+ /cc @gitlab-org/maintainers/rails-backend
@@ -7,7 +7,7 @@ with the latest commit from https://gitlab.com/gitlab-org/gitlab-styles/commits/
7
7
  <!-- Keep the sections order but remove the empty sections -->
8
8
 
9
9
  ```markdown
10
- ### New scenarios and scenario updates
10
+ ### New features and features updates
11
11
 
12
12
  - !aaa <Title of the aaa MR>.
13
13
 
@@ -19,7 +19,7 @@ with the latest commit from https://gitlab.com/gitlab-org/gitlab-styles/commits/
19
19
 
20
20
  - !ccc <Title of the ccc MR>.
21
21
 
22
- ### Other changes (CI, backstage)
22
+ ### Other changes (tooling, technical debt)
23
23
 
24
24
  - !ddd <Title of the ddd MR>.
25
25
  ```
@@ -30,6 +30,6 @@ with the latest commit from https://gitlab.com/gitlab-org/gitlab-styles/commits/
30
30
  - [ ] Release notes are accurate.
31
31
 
32
32
  - Checklist after merging:
33
- - [ ] [Create a tag for the new release version](docs/release_process.md#how-to).
33
+ - [ ] [Update the release notes for the newly created tag](docs/release_process.md#how-to).
34
34
 
35
- /label ~"Engineering Productivity" ~backstage
35
+ /label ~"Engineering Productivity" ~"feature" ~"feature::maintenance" ~"static code analysis"
@@ -1,3 +1,5 @@
1
1
  inherit_from:
2
2
  - rubocop-default.yml
3
- - .rubocop_todo.yml
3
+
4
+ require:
5
+ - rubocop/cop/internal_affairs
data/Gemfile CHANGED
@@ -7,7 +7,7 @@ gemspec
7
7
 
8
8
  group :test do
9
9
  # Pin these dependencies, otherwise a new rule could break the CI pipelines
10
- gem 'rubocop', '0.82.0'
11
- gem 'rubocop-rspec', '1.36.0'
10
+ gem 'rubocop', '0.89.1'
11
+ gem 'rubocop-rspec', '1.44.1'
12
12
  gem 'rspec-parameterized', '0.4.2', require: false
13
13
  end
@@ -5,6 +5,7 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
5
  require 'gitlab/styles/version'
6
6
 
7
7
  Gem::Specification.new do |spec|
8
+ spec.required_ruby_version = '>= 2.6'
8
9
  spec.name = 'gitlab-styles'
9
10
  spec.version = Gitlab::Styles::VERSION
10
11
  spec.authors = ['GitLab']
@@ -21,13 +22,13 @@ Gem::Specification.new do |spec|
21
22
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
22
23
  spec.require_paths = ['lib']
23
24
 
24
- spec.add_dependency 'rubocop', '~> 0.82.0'
25
+ spec.add_dependency 'rubocop', '~> 0.89.1'
25
26
  spec.add_dependency 'rubocop-gitlab-security', '~> 0.1.0'
26
- spec.add_dependency 'rubocop-performance', '~> 1.5.2'
27
- spec.add_dependency 'rubocop-rails', '~> 2.5'
28
- spec.add_dependency 'rubocop-rspec', '~> 1.36'
27
+ spec.add_dependency 'rubocop-performance', '~> 1.8.1'
28
+ spec.add_dependency 'rubocop-rails', '~> 2.8'
29
+ spec.add_dependency 'rubocop-rspec', '~> 1.44'
29
30
 
30
- spec.add_development_dependency 'bundler', '~> 1.16'
31
+ spec.add_development_dependency 'bundler', '~> 2.1'
31
32
  spec.add_development_dependency 'rake', '~> 10.0'
32
33
  spec.add_development_dependency 'rspec', '~> 3.0'
33
34
  end
@@ -1,19 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'gitlab/styles/rubocop/cop/custom_error_class'
4
- require 'gitlab/styles/rubocop/cop/gem_fetcher'
5
- require 'gitlab/styles/rubocop/cop/active_record_serialize'
6
- require 'gitlab/styles/rubocop/cop/redirect_with_status'
7
- require 'gitlab/styles/rubocop/cop/polymorphic_associations'
8
- require 'gitlab/styles/rubocop/cop/active_record_dependent'
9
- require 'gitlab/styles/rubocop/cop/in_batches'
10
- require 'gitlab/styles/rubocop/cop/line_break_after_guard_clauses'
11
- require 'gitlab/styles/rubocop/cop/migration/update_large_table'
12
- require 'gitlab/styles/rubocop/cop/without_reactive_cache'
13
- require 'gitlab/styles/rubocop/cop/rspec/single_line_hook'
14
- require 'gitlab/styles/rubocop/cop/rspec/have_link_parameters'
15
- require 'gitlab/styles/rubocop/cop/rspec/verbose_include_metadata'
16
- require 'gitlab/styles/rubocop/cop/rspec/empty_line_after_shared_example'
3
+ # Auto-require all cops under `gitlab/styles/rubocop/cop/**/*.rb`
4
+ cops_glob = File.join(__dir__, 'rubocop', 'cop', '**', '*.rb')
5
+ Dir[cops_glob].sort.each(&method(:require))
17
6
 
18
7
  module Gitlab
19
8
  module Styles
@@ -22,7 +22,7 @@ module Gitlab
22
22
  node.children.last.each_node(:pair) do |pair|
23
23
  key_name = pair.children[0].children[0]
24
24
 
25
- add_offense(pair, location: :expression) if key_name == :dependent
25
+ add_offense(pair) if key_name == :dependent
26
26
  end
27
27
  end
28
28
  end
@@ -0,0 +1,131 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Gitlab
4
+ module Styles
5
+ module Rubocop
6
+ module Cop
7
+ module CodeReuse
8
+ # Cop that denies the use of ActiveRecord methods outside of models.
9
+ class ActiveRecord < RuboCop::Cop::Cop
10
+ MSG = 'This method can only be used inside an ActiveRecord model: ' \
11
+ 'https://gitlab.com/gitlab-org/gitlab-foss/issues/49653'
12
+
13
+ # Various methods from ActiveRecord::Querying that are denied. We
14
+ # exclude some generic ones such as `any?` and `first`, as these may
15
+ # lead to too many false positives, since `Array` also supports these
16
+ # methods.
17
+ #
18
+ # The keys of this Hash are the denied method names. The values are
19
+ # booleans that indicate if the method should only be denied if any
20
+ # arguments are provided.
21
+ NOT_ALLOWED = {
22
+ average: true,
23
+ calculate: true,
24
+ count_by_sql: true,
25
+ create_with: true,
26
+ distinct: false,
27
+ eager_load: true,
28
+ exists?: true,
29
+ find_by: true,
30
+ find_by!: true,
31
+ find_by_sql: true,
32
+ find_each: true,
33
+ find_in_batches: true,
34
+ find_or_create_by: true,
35
+ find_or_create_by!: true,
36
+ find_or_initialize_by: true,
37
+ first!: false,
38
+ first_or_create: true,
39
+ first_or_create!: true,
40
+ first_or_initialize: true,
41
+ from: true,
42
+ group: true,
43
+ having: true,
44
+ ids: false,
45
+ includes: true,
46
+ joins: true,
47
+ limit: true,
48
+ lock: false,
49
+ many?: false,
50
+ offset: true,
51
+ order: true,
52
+ pluck: true,
53
+ preload: true,
54
+ readonly: false,
55
+ references: true,
56
+ reorder: true,
57
+ rewhere: true,
58
+ take: false,
59
+ take!: false,
60
+ unscope: false,
61
+ where: false,
62
+ with: true
63
+ }.freeze
64
+
65
+ def on_send(node)
66
+ receiver = node.children[0]
67
+ send_name = node.children[1]
68
+ first_arg = node.children[2]
69
+
70
+ return unless receiver && NOT_ALLOWED.key?(send_name)
71
+
72
+ # If the rule requires an argument to be given, but none are
73
+ # provided, we won't register an offense. This prevents us from
74
+ # adding offenses for `project.group`, while still covering
75
+ # `Project.group(:name)`.
76
+ return if NOT_ALLOWED[send_name] && !first_arg
77
+
78
+ add_offense(node, location: :selector)
79
+ end
80
+
81
+ # We can not auto correct code like this, as it requires manual
82
+ # refactoring. Instead, we'll just allow the surrounding scope.
83
+ #
84
+ # Despite this method's presence, you should not use it. This method
85
+ # exists to make it possible to allow large chunks of offenses we
86
+ # can't fix in the short term. If you are writing new code, follow the
87
+ # code reuse guidelines, instead of allowing any new offenses.
88
+ def autocorrect(node)
89
+ scope = surrounding_scope_of(node)
90
+ indent = indentation_of(scope)
91
+
92
+ lambda do |corrector|
93
+ # This prevents us from inserting the same enable/disable comment
94
+ # for a method or block that has multiple offenses.
95
+ next if allowed_scopes.include?(scope)
96
+
97
+ corrector.insert_before(
98
+ scope.source_range,
99
+ "# rubocop: disable #{cop_name}\n#{indent}"
100
+ )
101
+
102
+ corrector.insert_after(
103
+ scope.source_range,
104
+ "\n#{indent}# rubocop: enable #{cop_name}"
105
+ )
106
+
107
+ allowed_scopes << scope
108
+ end
109
+ end
110
+
111
+ def indentation_of(node)
112
+ ' ' * node.loc.expression.source_line[/\A */].length
113
+ end
114
+
115
+ def surrounding_scope_of(node)
116
+ %i[def defs block begin].each do |type|
117
+ if (found = node.each_ancestor(type).first)
118
+ return found
119
+ end
120
+ end
121
+ end
122
+
123
+ def allowed_scopes
124
+ @allowed_scopes ||= Set.new
125
+ end
126
+ end
127
+ end
128
+ end
129
+ end
130
+ end
131
+ end
@@ -21,7 +21,8 @@ module Gitlab
21
21
  MSG = 'Use `Class.new(SuperClass)` to define an empty custom error class.'
22
22
 
23
23
  def on_class(node)
24
- _klass, parent, body = node.children
24
+ parent = node.parent_class
25
+ body = node.body
25
26
 
26
27
  return if body
27
28
 
@@ -29,11 +30,13 @@ module Gitlab
29
30
 
30
31
  return unless parent_klass&.to_s&.end_with?('Error')
31
32
 
32
- add_offense(node, location: :expression)
33
+ add_offense(node)
33
34
  end
34
35
 
35
36
  def autocorrect(node)
36
- klass, parent, _body = node.children
37
+ klass = node.identifier
38
+ parent = node.parent_class
39
+
37
40
  replacement = "#{class_name_from_node(klass)} = Class.new(#{class_name_from_node(parent)})"
38
41
 
39
42
  lambda do |corrector|
@@ -20,7 +20,7 @@ module Gitlab
20
20
 
21
21
  node.children.last.each_node(:pair) do |pair|
22
22
  key_name = pair.children[0].children[0].to_sym
23
- add_offense(node, location: pair.source_range, message: MSG) if GIT_KEYS.include?(key_name)
23
+ add_offense(node, location: pair.source_range) if GIT_KEYS.include?(key_name)
24
24
  end
25
25
  end
26
26
 
@@ -69,7 +69,7 @@ module Gitlab
69
69
  return unless guard_clause?(node)
70
70
  return if next_line(node).blank? || clause_last_line?(next_line(node)) || guard_clause?(next_sibling(node))
71
71
 
72
- add_offense(node, location: :expression, message: MSG)
72
+ add_offense(node)
73
73
  end
74
74
 
75
75
  def autocorrect(node)
@@ -0,0 +1,132 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Gitlab
4
+ module Styles
5
+ module Rubocop
6
+ module Cop
7
+ # Ensures a line break around conditional blocks.
8
+ #
9
+ # @example
10
+ # # bad
11
+ # do_something
12
+ # if condition
13
+ # do_extra_stuff
14
+ # end
15
+ # do_something_more
16
+ #
17
+ # # good
18
+ # do_something
19
+ #
20
+ # if condition
21
+ # do_extra_stuff
22
+ # end
23
+ #
24
+ # do_something_more
25
+ #
26
+ # # bad
27
+ # do_something
28
+ # unless condition
29
+ # do_extra_stuff
30
+ # end
31
+ #
32
+ # do_something_more
33
+ #
34
+ # # good
35
+ # def a_method
36
+ # if condition
37
+ # do_something
38
+ # end
39
+ # end
40
+ #
41
+ # # good
42
+ # on_block do
43
+ # if condition
44
+ # do_something
45
+ # end
46
+ # end
47
+ class LineBreakAroundConditionalBlock < RuboCop::Cop::Cop
48
+ include RuboCop::Cop::RangeHelp
49
+
50
+ MSG = 'Add a line break around conditional blocks'
51
+
52
+ def on_if(node)
53
+ # This cop causes errors in haml files, so let's skip those
54
+ return if in_haml?(node)
55
+ return if node.single_line?
56
+ return unless node.if? || node.unless?
57
+
58
+ add_offense(node) unless previous_line_valid?(node)
59
+ add_offense(node) unless last_line_valid?(node)
60
+ end
61
+
62
+ def autocorrect(node)
63
+ lambda do |corrector|
64
+ line = range_by_whole_lines(node.source_range)
65
+
66
+ corrector.insert_before(line, "\n") unless previous_line_valid?(node)
67
+ corrector.insert_after(line, "\n") unless last_line_valid?(node)
68
+ end
69
+ end
70
+
71
+ private
72
+
73
+ def previous_line_valid?(node)
74
+ previous_line(node).empty? ||
75
+ start_clause_line?(previous_line(node)) ||
76
+ block_start?(previous_line(node)) ||
77
+ begin_line?(previous_line(node)) ||
78
+ assignment_line?(previous_line(node)) ||
79
+ rescue_line?(previous_line(node))
80
+ end
81
+
82
+ def last_line_valid?(node)
83
+ last_line(node).empty? ||
84
+ end_line?(last_line(node)) ||
85
+ end_clause_line?(last_line(node))
86
+ end
87
+
88
+ def previous_line(node)
89
+ processed_source[node.loc.line - 2]
90
+ end
91
+
92
+ def last_line(node)
93
+ processed_source[node.loc.last_line]
94
+ end
95
+
96
+ def start_clause_line?(line)
97
+ line =~ /^\s*(def|=end|#|module|class|if|unless|else|elsif|ensure|when)/
98
+ end
99
+
100
+ def end_clause_line?(line)
101
+ line =~ /^\s*(#|rescue|else|elsif|when)/
102
+ end
103
+
104
+ def begin_line?(line)
105
+ # an assignment followed by a begin or ust a begin
106
+ line =~ /^\s*(@?(\w|\|+|=|\[|\]|\s)+begin|begin)/
107
+ end
108
+
109
+ def assignment_line?(line)
110
+ line =~ /^\s*.*=/
111
+ end
112
+
113
+ def rescue_line?(line)
114
+ line =~ /^\s*rescue/
115
+ end
116
+
117
+ def block_start?(line)
118
+ line.match(/ (do|{)( \|.*?\|)?\s?(#.+)?\z/)
119
+ end
120
+
121
+ def end_line?(line)
122
+ line =~ /^\s*(end|})/
123
+ end
124
+
125
+ def in_haml?(node)
126
+ node.location.expression.source_buffer.name.end_with?('.haml.rb')
127
+ end
128
+ end
129
+ end
130
+ end
131
+ end
132
+ end