gitlab-styles 3.4.0 → 5.0.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 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