gitlab-styles 3.4.0 → 5.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b5f56cd42de5afd61307ea92fd0114cb64e79b575697800b948e6d3e42e32d7c
4
- data.tar.gz: 25c343aae93b4bb8d03d57b92152c5c51433cacec86c86b6449ba8a6af3cc6e2
3
+ metadata.gz: f34644596ecbfd782a8a8d5781251743cbe2f0ef079467416c4e15c784769693
4
+ data.tar.gz: 531fb802e1a50564fbbfad34e96f66b8a2227e6c182999750a39456f2ae17c98
5
5
  SHA512:
6
- metadata.gz: a7e7dbc2a80150595404fb1639266f462687668411b4410378a1021d9d07915fec14d6b34265d2a6672d65e5005cb4e2d7a1e99acff2ddbe0bc827280dc1c011
7
- data.tar.gz: c0b9073e17bf0c11f283ef485612746c3baa1a6de27392029ca930413f939cc192d797e0a3a3016da2db0bdc219444c93129199fbba9ab81e533f86182c65d10
6
+ metadata.gz: 9a4036572db91754b18045e0e9ec40d9747a633773c49fb3edcdc53d0ac8b6ae2743f94ffe5fc2ce6cc8a60a51216eb7b85c89dfb039f747370cca4831c9cc41
7
+ data.tar.gz: 78eb2aaec9c221dad33fb99c719c3ad838a39f4978381b2f99ef011f87c8c8ee67a24d9b6e26af2eea4d1b3a03e03e80aab5c2718afaa998764a3cf68d8153e4
@@ -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
data/.gitignore CHANGED
@@ -9,3 +9,6 @@
9
9
 
10
10
  # rspec failure tracking
11
11
  .rspec_status
12
+
13
+ # Ignore IDE specific files
14
+ .idea/
@@ -32,4 +32,4 @@ with the latest commit from https://gitlab.com/gitlab-org/gitlab-styles/commits/
32
32
  - Checklist after merging:
33
33
  - [ ] [Create a tag for the new release version](docs/release_process.md#how-to).
34
34
 
35
- /label ~"Engineering Productivity" ~backstage
35
+ /label ~"Engineering Productivity" ~"tooling::workflow"
data/Gemfile CHANGED
@@ -7,6 +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.74.0'
11
- gem 'rubocop-rspec', '1.36.0'
10
+ gem 'rubocop', '0.89.1'
11
+ gem 'rubocop-rspec', '1.44.1'
12
+ gem 'rspec-parameterized', '0.4.2', require: false
12
13
  end
data/README.md CHANGED
@@ -42,6 +42,7 @@ rules:
42
42
  - `rubocop-gemspec.yml`
43
43
  - `rubocop-layout.yml`
44
44
  - `rubocop-lint.yml`
45
+ - `rubocop-migrations.yml`
45
46
  - `rubocop-metrics.yml`
46
47
  - `rubocop-naming.yml`
47
48
  - `rubocop-performance.yml`
@@ -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,11 +22,11 @@ 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.74.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.4.1'
27
- spec.add_dependency 'rubocop-rails', '~> 2.0'
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
31
  spec.add_development_dependency 'bundler', '~> 1.16'
31
32
  spec.add_development_dependency 'rake', '~> 10.0'
@@ -8,10 +8,14 @@ require 'gitlab/styles/rubocop/cop/polymorphic_associations'
8
8
  require 'gitlab/styles/rubocop/cop/active_record_dependent'
9
9
  require 'gitlab/styles/rubocop/cop/in_batches'
10
10
  require 'gitlab/styles/rubocop/cop/line_break_after_guard_clauses'
11
+ require 'gitlab/styles/rubocop/cop/code_reuse/active_record'
12
+ require 'gitlab/styles/rubocop/cop/migration/update_large_table'
11
13
  require 'gitlab/styles/rubocop/cop/without_reactive_cache'
12
14
  require 'gitlab/styles/rubocop/cop/rspec/single_line_hook'
13
15
  require 'gitlab/styles/rubocop/cop/rspec/have_link_parameters'
14
16
  require 'gitlab/styles/rubocop/cop/rspec/verbose_include_metadata'
17
+ require 'gitlab/styles/rubocop/cop/rspec/empty_line_after_shared_example'
18
+ require 'gitlab/styles/rubocop/cop/rspec/empty_line_after_let_block'
15
19
 
16
20
  module Gitlab
17
21
  module Styles
@@ -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
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+ require_relative '../../migration_helpers'
3
+
4
+ module Gitlab
5
+ module Styles
6
+ module Rubocop
7
+ module Cop
8
+ module Migration
9
+ # This cop checks for methods that may lead to batch type issues on a table that's been
10
+ # explicitly denied because of its size.
11
+ #
12
+ # Even though though these methods perform functions to avoid
13
+ # downtime, using it with tables with millions of rows still causes a
14
+ # significant delay in the deploy process and is best avoided.
15
+ #
16
+ # See https://gitlab.com/gitlab-com/infrastructure/issues/1602 for more
17
+ # information.
18
+ class UpdateLargeTable < RuboCop::Cop::Cop
19
+ include MigrationHelpers
20
+
21
+ MSG = 'Using `%s` on the `%s` table will take a long time to ' \
22
+ 'complete, and should be avoided unless absolutely ' \
23
+ 'necessary'
24
+
25
+ def_node_matcher :batch_update?, <<~PATTERN
26
+ (send nil? ${#denied_method?}
27
+ (sym $...)
28
+ ...)
29
+ PATTERN
30
+
31
+ def on_send(node)
32
+ return if denied_tables.empty? || denied_methods.empty?
33
+ return unless in_migration?(node)
34
+
35
+ matches = batch_update?(node)
36
+ return unless matches
37
+
38
+ update_method = matches.first
39
+ table = matches.last.to_a.first
40
+
41
+ return unless denied_tables.include?(table)
42
+
43
+ add_offense(node, location: :expression, message: format(MSG, update_method, table))
44
+ end
45
+
46
+ private
47
+
48
+ def denied_tables
49
+ cop_config['DeniedTables'] || []
50
+ end
51
+
52
+ def denied_method?(method_name)
53
+ denied_methods.include?(method_name)
54
+ end
55
+
56
+ def denied_methods
57
+ cop_config['DeniedMethods'] || []
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rubocop-rspec'
4
+ require_relative '../../rspec/helpers'
5
+
6
+ module Gitlab
7
+ module Styles
8
+ module Rubocop
9
+ module Cop
10
+ module RSpec
11
+ class Base < RuboCop::Cop::RSpec::Base
12
+ include Rubocop::Rspec::Helpers
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rubocop-rspec'
4
+ require_relative 'base'
5
+
6
+ module Gitlab
7
+ module Styles
8
+ module Rubocop
9
+ module Cop
10
+ module RSpec
11
+ # Checks if there is an empty line after let blocks.
12
+ #
13
+ # @example
14
+ # # bad
15
+ # RSpec.describe Foo do
16
+ # let(:something) { 'something' }
17
+ # let(:another_thing) do
18
+ # end
19
+ # let(:something_else) do
20
+ # end
21
+ # let(:last_thing) { 'last thing' }
22
+ # end
23
+ #
24
+ # # good
25
+ # RSpec.describe Foo do
26
+ # let(:something) { 'something' }
27
+ # let(:another_thing) do
28
+ # end
29
+ #
30
+ # let(:something_else) do
31
+ # end
32
+ #
33
+ # let(:last_thing) { 'last thing' }
34
+ # end
35
+ #
36
+ # # good - it's ok to have non-separated without do/end blocks
37
+ # RSpec.describe Foo do
38
+ # let(:something) { 'something' }
39
+ # let(:last_thing) { 'last thing' }
40
+ # end
41
+ #
42
+ class EmptyLineAfterLetBlock < Base
43
+ extend RuboCop::Cop::AutoCorrector
44
+ include RuboCop::RSpec::EmptyLineSeparation
45
+
46
+ MSG = 'Add an empty line after `%<let>s` block.'
47
+
48
+ def_node_matcher :lets, LET.block_pattern
49
+
50
+ def on_block(node)
51
+ lets(node) do
52
+ break if last_child?(node)
53
+ next if node.single_line?
54
+
55
+ missing_separating_line_offense(node) do |method|
56
+ format(MSG, let: method)
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rubocop-rspec'
4
+ require_relative 'base'
5
+
6
+ module Gitlab
7
+ module Styles
8
+ module Rubocop
9
+ module Cop
10
+ module RSpec
11
+ # Checks if there is an empty line after shared example blocks.
12
+ #
13
+ # @example
14
+ # # bad
15
+ # RSpec.describe Foo do
16
+ # it_behaves_like 'do this first'
17
+ # it_behaves_like 'does this' do
18
+ # end
19
+ # it_behaves_like 'does that' do
20
+ # end
21
+ # it_behaves_like 'do some more'
22
+ # end
23
+ #
24
+ # # good
25
+ # RSpec.describe Foo do
26
+ # it_behaves_like 'do this first'
27
+ # it_behaves_like 'does this' do
28
+ # end
29
+ #
30
+ # it_behaves_like 'does that' do
31
+ # end
32
+ #
33
+ # it_behaves_like 'do some more'
34
+ # end
35
+ #
36
+ # # fair - it's ok to have non-separated without blocks
37
+ # RSpec.describe Foo do
38
+ # it_behaves_like 'do this first'
39
+ # it_behaves_like 'does this'
40
+ # end
41
+ #
42
+ class EmptyLineAfterSharedExample < Base
43
+ extend RuboCop::Cop::AutoCorrector
44
+ include RuboCop::RSpec::EmptyLineSeparation
45
+
46
+ MSG = 'Add an empty line after `%<example>s` block.'
47
+
48
+ def_node_matcher :shared_examples,
49
+ (SharedGroups::ALL + Includes::ALL).block_pattern
50
+
51
+ def on_block(node)
52
+ shared_examples(node) do
53
+ break if last_child?(node)
54
+
55
+ missing_separating_line_offense(node) do |method|
56
+ format(MSG, example: method)
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end