rubocop-minitest 0.15.1 → 0.17.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +9 -9
  3. data/.github/PULL_REQUEST_TEMPLATE.md +3 -4
  4. data/.github/workflows/spell_checking.yml +33 -0
  5. data/.rubocop.yml +2 -3
  6. data/.rubocop_todo.yml +2 -0
  7. data/.yardopts +3 -0
  8. data/CHANGELOG.md +30 -0
  9. data/CONTRIBUTING.md +3 -2
  10. data/Gemfile +4 -1
  11. data/LICENSE.txt +1 -1
  12. data/Rakefile +4 -6
  13. data/codespell.txt +0 -0
  14. data/config/default.yml +13 -2
  15. data/docs/antora.yml +1 -1
  16. data/docs/modules/ROOT/pages/cops_minitest.adoc +99 -4
  17. data/docs/modules/ROOT/pages/installation.adoc +1 -1
  18. data/lib/rubocop/cop/minitest/assert_empty.rb +12 -0
  19. data/lib/rubocop/cop/minitest/assert_empty_literal.rb +1 -1
  20. data/lib/rubocop/cop/minitest/assert_with_expected_argument.rb +3 -2
  21. data/lib/rubocop/cop/minitest/assertion_in_lifecycle_hook.rb +1 -1
  22. data/lib/rubocop/cop/minitest/global_expectations.rb +109 -38
  23. data/lib/rubocop/cop/minitest/multiple_assertions.rb +1 -1
  24. data/lib/rubocop/cop/minitest/no_assertions.rb +1 -1
  25. data/lib/rubocop/cop/minitest/refute_empty.rb +12 -0
  26. data/lib/rubocop/cop/minitest/unreachable_assertion.rb +2 -4
  27. data/lib/rubocop/cop/mixin/minitest_exploration_helpers.rb +9 -21
  28. data/lib/rubocop/minitest/assert_offense.rb +183 -0
  29. data/lib/rubocop/minitest/support.rb +10 -0
  30. data/lib/rubocop/minitest/version.rb +1 -1
  31. data/mkdocs.yml +1 -1
  32. data/relnotes/v0.15.2.md +5 -0
  33. data/relnotes/v0.16.0.md +11 -0
  34. data/relnotes/v0.17.0.md +5 -0
  35. data/relnotes/v0.17.1.md +5 -0
  36. data/rubocop-minitest.gemspec +2 -1
  37. data/tasks/changelog.rake +34 -0
  38. data/tasks/changelog.rb +166 -0
  39. data/tasks/cops_documentation.rake +5 -15
  40. data/tasks/cut_release.rake +19 -4
  41. metadata +16 -4
@@ -6,17 +6,83 @@ module RuboCop
6
6
  # This cop checks for deprecated global expectations
7
7
  # and autocorrects them to use expect format.
8
8
  #
9
- # @example
9
+ # @example EnforcedStyle: _
10
10
  # # bad
11
11
  # musts.must_equal expected_musts
12
12
  # wonts.wont_match expected_wonts
13
13
  # musts.must_raise TypeError
14
14
  #
15
+ # expect(musts).must_equal expected_musts
16
+ # expect(wonts).wont_match expected_wonts
17
+ # expect { musts }.must_raise TypeError
18
+ #
19
+ # value(musts).must_equal expected_musts
20
+ # value(wonts).wont_match expected_wonts
21
+ # value { musts }.must_raise TypeError
22
+ #
15
23
  # # good
16
24
  # _(musts).must_equal expected_musts
17
25
  # _(wonts).wont_match expected_wonts
18
26
  # _ { musts }.must_raise TypeError
27
+ #
28
+ # @example EnforcedStyle: any (default)
29
+ # # bad
30
+ # musts.must_equal expected_musts
31
+ # wonts.wont_match expected_wonts
32
+ # musts.must_raise TypeError
33
+ #
34
+ # # good
35
+ # _(musts).must_equal expected_musts
36
+ # _(wonts).wont_match expected_wonts
37
+ # _ { musts }.must_raise TypeError
38
+ #
39
+ # expect(musts).must_equal expected_musts
40
+ # expect(wonts).wont_match expected_wonts
41
+ # expect { musts }.must_raise TypeError
42
+ #
43
+ # value(musts).must_equal expected_musts
44
+ # value(wonts).wont_match expected_wonts
45
+ # value { musts }.must_raise TypeError
46
+ #
47
+ # @example EnforcedStyle: expect
48
+ # # bad
49
+ # musts.must_equal expected_musts
50
+ # wonts.wont_match expected_wonts
51
+ # musts.must_raise TypeError
52
+ #
53
+ # _(musts).must_equal expected_musts
54
+ # _(wonts).wont_match expected_wonts
55
+ # _ { musts }.must_raise TypeError
56
+ #
57
+ # value(musts).must_equal expected_musts
58
+ # value(wonts).wont_match expected_wonts
59
+ # value { musts }.must_raise TypeError
60
+ #
61
+ # # good
62
+ # expect(musts).must_equal expected_musts
63
+ # expect(wonts).wont_match expected_wonts
64
+ # expect { musts }.must_raise TypeError
65
+ #
66
+ # @example EnforcedStyle: value
67
+ # # bad
68
+ # musts.must_equal expected_musts
69
+ # wonts.wont_match expected_wonts
70
+ # musts.must_raise TypeError
71
+ #
72
+ # _(musts).must_equal expected_musts
73
+ # _(wonts).wont_match expected_wonts
74
+ # _ { musts }.must_raise TypeError
75
+ #
76
+ # expect(musts).must_equal expected_musts
77
+ # expect(wonts).wont_match expected_wonts
78
+ # expect { musts }.must_raise TypeError
79
+ #
80
+ # # good
81
+ # value(musts).must_equal expected_musts
82
+ # value(wonts).wont_match expected_wonts
83
+ # value { musts }.must_raise TypeError
19
84
  class GlobalExpectations < Base
85
+ include ConfigurableEnforcedStyle
20
86
  extend AutoCorrector
21
87
 
22
88
  MSG = 'Use `%<preferred>s` instead.'
@@ -34,58 +100,63 @@ module RuboCop
34
100
 
35
101
  RESTRICT_ON_SEND = VALUE_MATCHERS + BLOCK_MATCHERS
36
102
 
37
- VALUE_MATCHERS_STR = VALUE_MATCHERS.map do |m|
38
- ":#{m}"
39
- end.join(' ').freeze
103
+ # There are aliases for the `_` method - `expect` and `value`
104
+ DSL_METHODS = %i[_ expect value].freeze
40
105
 
41
- BLOCK_MATCHERS_STR = BLOCK_MATCHERS.map do |m|
42
- ":#{m}"
43
- end.join(' ').freeze
106
+ def on_send(node)
107
+ receiver = node.receiver
108
+ return unless receiver
44
109
 
45
- # There are aliases for the `_` method - `expect` and `value`
46
- DSL_METHODS_LIST = %w[_ value expect].map do |n|
47
- ":#{n}"
48
- end.join(' ').freeze
110
+ method = block_receiver?(receiver) || value_receiver?(receiver)
111
+ return if method == preferred_method || (method && style == :any)
112
+
113
+ register_offense(node, method)
114
+ end
49
115
 
50
- def_node_matcher :value_global_expectation?, <<~PATTERN
51
- (send !(send nil? {#{DSL_METHODS_LIST}} _) {#{VALUE_MATCHERS_STR}} ...)
116
+ private
117
+
118
+ def_node_matcher :block_receiver?, <<~PATTERN
119
+ (block (send nil? $#method_allowed?) _ _)
52
120
  PATTERN
53
121
 
54
- def_node_matcher :block_global_expectation?, <<~PATTERN
55
- (send
56
- [
57
- !(send nil? {#{DSL_METHODS_LIST}} _)
58
- !(block (send nil? {#{DSL_METHODS_LIST}}) _ _)
59
- ]
60
- {#{BLOCK_MATCHERS_STR}}
61
- _
62
- )
122
+ def_node_matcher :value_receiver?, <<~PATTERN
123
+ (send nil? $#method_allowed? _)
63
124
  PATTERN
64
125
 
65
- def on_send(node)
66
- return unless value_global_expectation?(node) || block_global_expectation?(node)
126
+ def method_allowed?(method)
127
+ DSL_METHODS.include?(method)
128
+ end
67
129
 
68
- message = format(MSG, preferred: preferred_receiver(node))
130
+ def preferred_method
131
+ style == :any ? :_ : style
132
+ end
69
133
 
70
- add_offense(node.receiver.source_range, message: message) do |corrector|
71
- receiver = node.receiver.source_range
134
+ def preferred_receiver(node)
135
+ receiver = node.receiver
72
136
 
73
- if BLOCK_MATCHERS.include?(node.method_name)
74
- corrector.wrap(receiver, '_ { ', ' }')
75
- else
76
- corrector.wrap(receiver, '_(', ')')
77
- end
137
+ if BLOCK_MATCHERS.include?(node.method_name)
138
+ body = receiver.lambda? ? receiver.body : receiver
139
+ "#{preferred_method} { #{body.source} }"
140
+ else
141
+ "#{preferred_method}(#{receiver.source})"
78
142
  end
79
143
  end
80
144
 
81
- private
145
+ def register_offense(node, method)
146
+ receiver = node.receiver
82
147
 
83
- def preferred_receiver(node)
84
- source = node.receiver.source
85
- if BLOCK_MATCHERS.include?(node.method_name)
86
- "_ { #{source} }"
148
+ if method
149
+ preferred = preferred_method
150
+ replacement = receiver.source.sub(method.to_s, preferred_method.to_s)
87
151
  else
88
- "_(#{source})"
152
+ preferred = preferred_receiver(node)
153
+ replacement = preferred
154
+ end
155
+
156
+ message = format(MSG, preferred: preferred)
157
+
158
+ add_offense(receiver, message: message) do |corrector|
159
+ corrector.replace(receiver, replacement)
89
160
  end
90
161
  end
91
162
  end
@@ -50,7 +50,7 @@ module RuboCop
50
50
  private
51
51
 
52
52
  def assertions_count(node)
53
- base = assertion?(node) ? 1 : 0
53
+ base = assertion_method?(node) ? 1 : 0
54
54
  base + node.each_child_node.sum { |c| assertions_count(c) }
55
55
  end
56
56
 
@@ -39,7 +39,7 @@ module RuboCop
39
39
  private
40
40
 
41
41
  def assertions_count(node)
42
- base = assertion?(node) ? 1 : 0
42
+ base = assertion_method?(node) ? 1 : 0
43
43
  base + node.each_child_node.sum { |c| assertions_count(c) }
44
44
  end
45
45
  end
@@ -19,6 +19,18 @@ module RuboCop
19
19
  extend MinitestCopRule
20
20
 
21
21
  define_rule :refute, target_method: :empty?
22
+
23
+ remove_method :on_send
24
+ def on_send(node)
25
+ return unless node.method?(:refute)
26
+ return unless (arguments = peel_redundant_parentheses_from(node.arguments))
27
+ return unless arguments.first.respond_to?(:method?) && arguments.first.method?(:empty?)
28
+ return unless arguments.first.arguments.empty?
29
+
30
+ add_offense(node, message: offense_message(arguments)) do |corrector|
31
+ autocorrect(corrector, node, arguments)
32
+ end
33
+ end
22
34
  end
23
35
  end
24
36
  end
@@ -30,11 +30,9 @@ module RuboCop
30
30
 
31
31
  last_node = body.begin_type? ? body.children.last : body
32
32
  return unless last_node.send_type?
33
+ return unless assertion_method?(last_node)
33
34
 
34
- method_name = last_node.method_name
35
- return unless assertion_method?(method_name)
36
-
37
- add_offense(last_node, message: format(MSG, assertion_method: method_name))
35
+ add_offense(last_node, message: format(MSG, assertion_method: last_node.method_name))
38
36
  end
39
37
  end
40
38
  end
@@ -10,17 +10,6 @@ module RuboCop
10
10
 
11
11
  ASSERTION_PREFIXES = %w[assert refute].freeze
12
12
 
13
- ASSERTION_METHODS = %i[
14
- assert assert_empty assert_equal assert_in_delta assert_in_epsilon assert_includes assert_instance_of
15
- assert_kind_of assert_match assert_nil assert_operator assert_output assert_path_exists assert_predicate
16
- assert_raises assert_respond_to assert_same assert_send assert_silent assert_throws
17
- refute refute_empty refute_equal refute_in_delta refute_in_epsilon refute_includes refute_instance_of
18
- refute_kind_of refute_match refute_nil refute_operator refute_path_exists refute_predicate
19
- refute_respond_to refute_same
20
- ].freeze
21
-
22
- FLUNK = 'flunk'
23
-
24
13
  LIFECYCLE_HOOK_METHODS = %i[
25
14
  before_setup
26
15
  setup
@@ -84,19 +73,18 @@ module RuboCop
84
73
  method_def.each_child_node(:send)
85
74
  end
86
75
 
87
- send_nodes.select { |send_node| assertion?(send_node) }
76
+ send_nodes.select { |send_node| assertion_method?(send_node) }
88
77
  end
89
78
 
90
- def assertion?(node)
91
- node.send_type? &&
92
- ASSERTION_PREFIXES.any? do |prefix|
93
- method_name = node.method_name.to_s
94
- method_name == FLUNK || method_name.start_with?(prefix)
95
- end
96
- end
79
+ def assertion_method?(node)
80
+ return false unless node.send_type?
81
+
82
+ ASSERTION_PREFIXES.any? do |prefix|
83
+ method_name = node.method_name
97
84
 
98
- def assertion_method?(method_name)
99
- method_name == FLUNK || ASSERTION_METHODS.include?(method_name)
85
+ # TODO: Remove the fllowing `to_s` since Ruby 2.7 that supports `Symbol#start_with?`.
86
+ method_name.to_s.start_with?(prefix) || node.method?(:flunk)
87
+ end
100
88
  end
101
89
 
102
90
  def lifecycle_hook_method?(node)
@@ -0,0 +1,183 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Laziness copied from rubocop source code
4
+ require 'rubocop/rspec/expect_offense'
5
+ require 'rubocop/cop/legacy/corrector'
6
+
7
+ module RuboCop
8
+ module Minitest
9
+ # Mixin for `assert_offense` and `assert_no_offenses`
10
+ #
11
+ # This mixin makes it easier to specify strict offense assertions
12
+ # in a declarative and visual fashion. Just type out the code that
13
+ # should generate a offense, annotate code by writing '^'s
14
+ # underneath each character that should be highlighted, and follow
15
+ # the carets with a string (separated by a space) that is the
16
+ # message of the offense. You can include multiple offenses in
17
+ # one code snippet.
18
+ #
19
+ # @example Usage
20
+ #
21
+ # assert_offense(<<~RUBY)
22
+ # class FooTest < Minitest::Test
23
+ # def test_do_something
24
+ # assert_equal(nil, somestuff)
25
+ # ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Prefer using `assert_nil(somestuff)` over `assert_equal(nil, somestuff)`.
26
+ # end
27
+ # end
28
+ # RUBY
29
+ #
30
+ # Auto-correction can be tested using `assert_correction` after
31
+ # `assert_offense`.
32
+ #
33
+ # @example `assert_offense` and `assert_correction`
34
+ #
35
+ # assert_offense(<<~RUBY)
36
+ # class FooTest < Minitest::Test
37
+ # def test_do_something
38
+ # assert_equal(nil, somestuff)
39
+ # ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Prefer using `assert_nil(somestuff)` over `assert_equal(nil, somestuff)`.
40
+ # end
41
+ # end
42
+ # RUBY
43
+ #
44
+ # assert_correction(<<~RUBY)
45
+ # class FooTest < Minitest::Test
46
+ # def test_do_something
47
+ # assert_nil(somestuff)
48
+ # end
49
+ # end
50
+ # RUBY
51
+ #
52
+ # If you do not want to specify an offense then use the
53
+ # companion method `assert_no_offenses`. This method is a much
54
+ # simpler assertion since it just inspects the source and checks
55
+ # that there were no offenses. The `assert_offense` method has
56
+ # to do more work by parsing out lines that contain carets.
57
+ #
58
+ # If the code produces an offense that could not be auto-corrected, you can
59
+ # use `assert_no_corrections` after `assert_offense`.
60
+ #
61
+ # @example `assert_offense` and `assert_no_corrections`
62
+ #
63
+ # assert_offense(<<~RUBY)
64
+ # class FooTest < Minitest::Test
65
+ # def test_do_something
66
+ # assert_equal(nil, somestuff)
67
+ # ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Prefer using `assert_nil(somestuff)` over `assert_equal(nil, somestuff)`.
68
+ # end
69
+ # end
70
+ # RUBY
71
+ #
72
+ # assert_no_corrections
73
+ module AssertOffense
74
+ private
75
+
76
+ def setup
77
+ cop_name = self.class.to_s.delete_suffix('Test')
78
+
79
+ @cop = RuboCop::Cop::Minitest.const_get(cop_name).new
80
+ end
81
+
82
+ def assert_no_offenses(source, file = nil)
83
+ setup_assertion
84
+
85
+ offenses = inspect_source(source, @cop, file)
86
+
87
+ expected_annotations = RuboCop::RSpec::ExpectOffense::AnnotatedSource.parse(source)
88
+ actual_annotations = expected_annotations.with_offense_annotations(offenses)
89
+
90
+ assert_equal(source, actual_annotations.to_s)
91
+ end
92
+
93
+ def assert_offense(source, file = nil)
94
+ setup_assertion
95
+
96
+ @cop.instance_variable_get(:@options)[:auto_correct] = true
97
+
98
+ expected_annotations = RuboCop::RSpec::ExpectOffense::AnnotatedSource.parse(source)
99
+ if expected_annotations.plain_source == source
100
+ raise 'Use `assert_no_offenses` to assert that no offenses are found'
101
+ end
102
+
103
+ @processed_source = parse_source!(expected_annotations.plain_source, file)
104
+
105
+ offenses = _investigate(@cop, @processed_source)
106
+
107
+ actual_annotations = expected_annotations.with_offense_annotations(offenses)
108
+
109
+ assert_equal(expected_annotations.to_s, actual_annotations.to_s)
110
+ end
111
+
112
+ def _investigate(cop, processed_source)
113
+ team = RuboCop::Cop::Team.new([cop], nil, raise_error: true)
114
+ report = team.investigate(processed_source)
115
+ @last_corrector = report.correctors.first || RuboCop::Cop::Corrector.new(processed_source)
116
+ report.offenses
117
+ end
118
+
119
+ def assert_correction(correction, loop: true)
120
+ raise '`assert_correction` must follow `assert_offense`' unless @processed_source
121
+
122
+ iteration = 0
123
+ new_source = loop do
124
+ iteration += 1
125
+
126
+ corrected_source = @last_corrector.rewrite
127
+
128
+ break corrected_source unless loop
129
+ break corrected_source if @last_corrector.empty? || corrected_source == @processed_source.buffer.source
130
+
131
+ if iteration > RuboCop::Runner::MAX_ITERATIONS
132
+ raise RuboCop::Runner::InfiniteCorrectionLoop.new(@processed_source.path, [])
133
+ end
134
+
135
+ # Prepare for next loop
136
+ @processed_source = parse_source!(corrected_source, @processed_source.path)
137
+
138
+ _investigate(@cop, @processed_source)
139
+ end
140
+
141
+ assert_equal(correction, new_source)
142
+ end
143
+
144
+ def setup_assertion
145
+ RuboCop::Formatter::DisabledConfigFormatter.config_to_allow_offenses = {}
146
+ RuboCop::Formatter::DisabledConfigFormatter.detected_styles = {}
147
+ end
148
+
149
+ def inspect_source(source, cop, file = nil)
150
+ processed_source = parse_source!(source, file)
151
+ raise 'Error parsing example code' unless processed_source.valid_syntax?
152
+
153
+ _investigate(cop, processed_source)
154
+ end
155
+
156
+ def investigate(cop, processed_source)
157
+ needed = Hash.new { |h, k| h[k] = [] }
158
+ Array(cop.class.joining_forces).each { |force| needed[force] << cop }
159
+ forces = needed.map do |force_class, joining_cops|
160
+ force_class.new(joining_cops)
161
+ end
162
+
163
+ commissioner = RuboCop::Cop::Commissioner.new([cop], forces, raise_error: true)
164
+ commissioner.investigate(processed_source)
165
+ commissioner
166
+ end
167
+
168
+ def parse_source!(source, file = nil)
169
+ if file.respond_to?(:write)
170
+ file.write(source)
171
+ file.rewind
172
+ file = file.path
173
+ end
174
+
175
+ RuboCop::ProcessedSource.new(source, ruby_version, file)
176
+ end
177
+
178
+ def ruby_version
179
+ 2.5
180
+ end
181
+ end
182
+ end
183
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Require this file to load code that supports testing using Minitest.
4
+
5
+ require 'rubocop'
6
+ require 'minitest/autorun'
7
+ require 'minitest/pride'
8
+ require_relative 'assert_offense'
9
+
10
+ Minitest::Test.include RuboCop::Minitest::AssertOffense
@@ -4,7 +4,7 @@ module RuboCop
4
4
  module Minitest
5
5
  # This module holds the RuboCop Minitest version information.
6
6
  module Version
7
- STRING = '0.15.1'
7
+ STRING = '0.17.1'
8
8
 
9
9
  def self.document_version
10
10
  STRING.match('\d+\.\d+').to_s
data/mkdocs.yml CHANGED
@@ -1,7 +1,7 @@
1
1
  site_name: "A RuboCop extension focused on enforcing Minitest best practices and coding conventions."
2
2
  repo_url: https://github.com/rubocop/rubocop-minitest
3
3
  edit_uri: edit/master/legacy-docs/
4
- copyright: "Copyright &copy; 2021 Bozhidar Batsov, Jonas Arvidsson, Koichi ITO, and RuboCop contributors"
4
+ copyright: "Copyright &copy; 2022 Bozhidar Batsov, Jonas Arvidsson, Koichi ITO, and RuboCop contributors"
5
5
  docs_dir: legacy-docs
6
6
  pages:
7
7
  - Home: index.md
@@ -0,0 +1,5 @@
1
+ ### Bug fixes
2
+
3
+ * [#145](https://github.com/rubocop/rubocop-minitest/pull/145): Mark `Minitest/AssertEmptyLiteral` as safe auto-correction. ([@koic][])
4
+
5
+ [@koic]: https://github.com/koic
@@ -0,0 +1,11 @@
1
+ ### New features
2
+
3
+ * [#147](https://github.com/rubocop/rubocop-minitest/issues/147): Add `EnforcedStyle` config parameter for `Minitest/GlobalExpectations`. ([@gi][])
4
+
5
+ ### Bug fixes
6
+
7
+ * [#142](https://github.com/rubocop/rubocop-minitest/issues/142): Fix `Minitest/GlobalExpectations` autocorrect when receiver is lambda. ([@gi][])
8
+ * [#150](https://github.com/rubocop/rubocop-minitest/issues/150): Fix a false positive for `Minitest/AssertEmpty` and `RefuteEmpty` cops when using `empty` method with any arguments. ([@koic][])
9
+
10
+ [@gi]: https://github.com/gi
11
+ [@koic]: https://github.com/koic
@@ -0,0 +1,5 @@
1
+ ### New features
2
+
3
+ * [#155](https://github.com/rubocop/rubocop-minitest/issues/155): Provide `assert_offense`, `assert_correction`, and `assert_no_offenses` testing APIs for custom Minitest cop development. ([@koic][])
4
+
5
+ [@koic]: https://github.com/koic
@@ -0,0 +1,5 @@
1
+ ### Changes
2
+
3
+ * [#158](https://github.com/rubocop/rubocop-minitest/pull/158): Make `Minitest/UnreachableAssertion` aware of `assert` and `refute` prefix methods. ([@koic][])
4
+
5
+ [@koic]: https://github.com/koic
@@ -22,7 +22,8 @@ Gem::Specification.new do |spec|
22
22
  'changelog_uri' => 'https://github.com/rubocop/rubocop-minitest/blob/master/CHANGELOG.md',
23
23
  'source_code_uri' => 'https://github.com/rubocop/rubocop-minitest',
24
24
  'documentation_uri' => "https://docs.rubocop.org/rubocop-minitest/#{RuboCop::Minitest::Version.document_version}",
25
- 'bug_tracker_uri' => 'https://github.com/rubocop/rubocop-minitest/issues'
25
+ 'bug_tracker_uri' => 'https://github.com/rubocop/rubocop-minitest/issues',
26
+ 'rubygems_mfa_required' => 'true'
26
27
  }
27
28
 
28
29
  # Specify which files should be added to the gem when it is released.
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ autoload :Changelog, "#{__dir__}/changelog"
4
+
5
+ namespace :changelog do
6
+ %i[new fix change].each do |type|
7
+ desc "Create a Changelog entry (#{type})"
8
+ task type, [:id] do |_task, args|
9
+ ref_type = :pull if args[:id]
10
+ path = Changelog::Entry.new(type: type, ref_id: args[:id], ref_type: ref_type).write
11
+ cmd = "git add #{path}"
12
+ system cmd
13
+ puts "Entry '#{path}' created and added to git index"
14
+ end
15
+ end
16
+
17
+ desc 'Merge entries and delete them'
18
+ task :merge do
19
+ raise 'No entries!' unless Changelog.pending?
20
+
21
+ Changelog.new.merge!.and_delete!
22
+ cmd = "git commit -a -m 'Update Changelog'"
23
+ puts cmd
24
+ system cmd
25
+ end
26
+
27
+ task :check_clean do
28
+ next unless Changelog.pending?
29
+
30
+ puts '*** Pending changelog entries!'
31
+ puts 'Do `bundle exec rake changelog:merge`'
32
+ exit(1)
33
+ end
34
+ end