jiminy 0.1.0.pre1 → 0.2.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: ed071d31bf26f4a0beaed34426656d9afbf1a99122b57d27aabcf58a03439910
4
- data.tar.gz: 59c64b03f115328b28da4eb4fda6e573891c770845e97a584cc3d4e400816e37
3
+ metadata.gz: 0c2d7c5c5ca5c253b2c1c9962f4d2c846a1bf2ebb9ecbcfa4d5a254832dae2ec
4
+ data.tar.gz: 77f60976535c54d0e42205aedbca82067164b15956d1b0b7a44a49791a7d4de5
5
5
  SHA512:
6
- metadata.gz: 7ec6ebd488281ebf37f80885fd7e824f9cccbcd076447afc91551d9a96791db4e801ae413305b565f920d090b3c79afda48f8cdceae8fd9d9f7cee3ce0c76f05
7
- data.tar.gz: e02a8751271ccd992976f7a98b5fd636d7e3d369d4f1cf9e3eecead0fcc0a29fc56126e9181b526bfc1205c933436506de04d4c409593a13a72b8c2b92c2b1a6
6
+ metadata.gz: 8ee0316e181e196a2a091e68aedbfded41f0a4834f7c71370a71ba7fcaa512380a58d300aa79fae33e8302c13b7c1dd4796dce0027a3218ef25722d5c7983135
7
+ data.tar.gz: f15f0f87a0b1bdd97eed27e250f23b51c72049b8b86fcd9468a226b559a379d10ef0cc30deb7697dfe9c62b86b3e6a009984a7e698e4f4607dd99800566126d7
data/.rubocop.yml CHANGED
@@ -1,3 +1,6 @@
1
+ inherit_from:
2
+ - https://raw.githubusercontent.com/cookpad/global-style-guides/main/.rubocop.ruby.yml
3
+
1
4
  AllCops:
2
5
  TargetRubyVersion: 2.6
3
6
  NewCops: enable
data/Gemfile.lock CHANGED
@@ -1,91 +1,21 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- jiminy (0.1.0.pre1)
5
- octokit (>= 4, < 5)
6
- prosopite (>= 1, < 2)
7
- rails (>= 5, < 7.2)
8
- thor (>= 1.2, < 2)
4
+ jiminy (0.2.0)
5
+ octokit (>= 4)
6
+ prosopite (>= 1)
7
+ rspec
8
+ thor (>= 1.2)
9
9
 
10
10
  GEM
11
11
  remote: https://rubygems.org/
12
12
  specs:
13
- actioncable (7.0.2.2)
14
- actionpack (= 7.0.2.2)
15
- activesupport (= 7.0.2.2)
16
- nio4r (~> 2.0)
17
- websocket-driver (>= 0.6.1)
18
- actionmailbox (7.0.2.2)
19
- actionpack (= 7.0.2.2)
20
- activejob (= 7.0.2.2)
21
- activerecord (= 7.0.2.2)
22
- activestorage (= 7.0.2.2)
23
- activesupport (= 7.0.2.2)
24
- mail (>= 2.7.1)
25
- net-imap
26
- net-pop
27
- net-smtp
28
- actionmailer (7.0.2.2)
29
- actionpack (= 7.0.2.2)
30
- actionview (= 7.0.2.2)
31
- activejob (= 7.0.2.2)
32
- activesupport (= 7.0.2.2)
33
- mail (~> 2.5, >= 2.5.4)
34
- net-imap
35
- net-pop
36
- net-smtp
37
- rails-dom-testing (~> 2.0)
38
- actionpack (7.0.2.2)
39
- actionview (= 7.0.2.2)
40
- activesupport (= 7.0.2.2)
41
- rack (~> 2.0, >= 2.2.0)
42
- rack-test (>= 0.6.3)
43
- rails-dom-testing (~> 2.0)
44
- rails-html-sanitizer (~> 1.0, >= 1.2.0)
45
- actiontext (7.0.2.2)
46
- actionpack (= 7.0.2.2)
47
- activerecord (= 7.0.2.2)
48
- activestorage (= 7.0.2.2)
49
- activesupport (= 7.0.2.2)
50
- globalid (>= 0.6.0)
51
- nokogiri (>= 1.8.5)
52
- actionview (7.0.2.2)
53
- activesupport (= 7.0.2.2)
54
- builder (~> 3.1)
55
- erubi (~> 1.4)
56
- rails-dom-testing (~> 2.0)
57
- rails-html-sanitizer (~> 1.1, >= 1.2.0)
58
- activejob (7.0.2.2)
59
- activesupport (= 7.0.2.2)
60
- globalid (>= 0.3.6)
61
- activemodel (7.0.2.2)
62
- activesupport (= 7.0.2.2)
63
- activerecord (7.0.2.2)
64
- activemodel (= 7.0.2.2)
65
- activesupport (= 7.0.2.2)
66
- activestorage (7.0.2.2)
67
- actionpack (= 7.0.2.2)
68
- activejob (= 7.0.2.2)
69
- activerecord (= 7.0.2.2)
70
- activesupport (= 7.0.2.2)
71
- marcel (~> 1.0)
72
- mini_mime (>= 1.1.0)
73
- activesupport (7.0.2.2)
74
- concurrent-ruby (~> 1.0, >= 1.0.2)
75
- i18n (>= 1.6, < 2)
76
- minitest (>= 5.1)
77
- tzinfo (~> 2.0)
78
13
  addressable (2.8.0)
79
14
  public_suffix (>= 2.0.2, < 5.0)
80
15
  ast (2.4.2)
81
- builder (3.2.4)
82
16
  byebug (11.1.3)
83
- concurrent-ruby (1.1.9)
84
- crass (1.0.6)
85
17
  diff-lcs (1.5.0)
86
- digest (3.1.0)
87
- erubi (1.10.0)
88
- faraday (1.9.3)
18
+ faraday (1.10.0)
89
19
  faraday-em_http (~> 1.0)
90
20
  faraday-em_synchrony (~> 1.0)
91
21
  faraday-excon (~> 1.1)
@@ -108,124 +38,54 @@ GEM
108
38
  faraday-patron (1.0.0)
109
39
  faraday-rack (1.0.0)
110
40
  faraday-retry (1.0.3)
111
- globalid (1.0.0)
112
- activesupport (>= 5.0)
113
- i18n (1.10.0)
114
- concurrent-ruby (~> 1.0)
115
- io-wait (0.2.1)
116
- loofah (2.14.0)
117
- crass (~> 1.0.2)
118
- nokogiri (>= 1.5.9)
119
- mail (2.7.1)
120
- mini_mime (>= 0.1.1)
121
- marcel (1.0.2)
122
- method_source (1.0.0)
123
- mini_mime (1.1.2)
124
- minitest (5.15.0)
125
41
  multipart-post (2.1.1)
126
- net-imap (0.2.3)
127
- digest
128
- net-protocol
129
- strscan
130
- net-pop (0.1.1)
131
- digest
132
- net-protocol
133
- timeout
134
- net-protocol (0.1.2)
135
- io-wait
136
- timeout
137
- net-smtp (0.3.1)
138
- digest
139
- net-protocol
140
- timeout
141
- nio4r (2.5.8)
142
- nokogiri (1.13.3-x86_64-darwin)
143
- racc (~> 1.4)
144
42
  octokit (4.22.0)
145
43
  faraday (>= 0.9)
146
44
  sawyer (~> 0.8.0, >= 0.5.3)
147
- parallel (1.21.0)
148
- parser (3.1.0.0)
45
+ parallel (1.22.1)
46
+ parser (3.1.1.0)
149
47
  ast (~> 2.4.1)
150
- prosopite (1.0.7)
151
- public_suffix (4.0.6)
152
- racc (1.6.0)
153
- rack (2.2.3)
154
- rack-test (1.1.0)
155
- rack (>= 1.0, < 3)
156
- rails (7.0.2.2)
157
- actioncable (= 7.0.2.2)
158
- actionmailbox (= 7.0.2.2)
159
- actionmailer (= 7.0.2.2)
160
- actionpack (= 7.0.2.2)
161
- actiontext (= 7.0.2.2)
162
- actionview (= 7.0.2.2)
163
- activejob (= 7.0.2.2)
164
- activemodel (= 7.0.2.2)
165
- activerecord (= 7.0.2.2)
166
- activestorage (= 7.0.2.2)
167
- activesupport (= 7.0.2.2)
168
- bundler (>= 1.15.0)
169
- railties (= 7.0.2.2)
170
- rails-dom-testing (2.0.3)
171
- activesupport (>= 4.2.0)
172
- nokogiri (>= 1.6)
173
- rails-html-sanitizer (1.4.2)
174
- loofah (~> 2.3)
175
- railties (7.0.2.2)
176
- actionpack (= 7.0.2.2)
177
- activesupport (= 7.0.2.2)
178
- method_source
179
- rake (>= 12.2)
180
- thor (~> 1.0)
181
- zeitwerk (~> 2.5)
48
+ prosopite (1.0.8)
49
+ public_suffix (4.0.7)
182
50
  rainbow (3.1.1)
183
51
  rake (13.0.6)
184
- regexp_parser (2.2.1)
52
+ regexp_parser (2.3.0)
185
53
  rexml (3.2.5)
186
- rspec (3.10.0)
187
- rspec-core (~> 3.10.0)
188
- rspec-expectations (~> 3.10.0)
189
- rspec-mocks (~> 3.10.0)
190
- rspec-core (3.10.2)
191
- rspec-support (~> 3.10.0)
192
- rspec-expectations (3.10.2)
54
+ rspec (3.11.0)
55
+ rspec-core (~> 3.11.0)
56
+ rspec-expectations (~> 3.11.0)
57
+ rspec-mocks (~> 3.11.0)
58
+ rspec-core (3.11.0)
59
+ rspec-support (~> 3.11.0)
60
+ rspec-expectations (3.11.0)
193
61
  diff-lcs (>= 1.2.0, < 2.0)
194
- rspec-support (~> 3.10.0)
195
- rspec-mocks (3.10.3)
62
+ rspec-support (~> 3.11.0)
63
+ rspec-mocks (3.11.1)
196
64
  diff-lcs (>= 1.2.0, < 2.0)
197
- rspec-support (~> 3.10.0)
198
- rspec-support (3.10.3)
199
- rubocop (1.25.1)
65
+ rspec-support (~> 3.11.0)
66
+ rspec-support (3.11.0)
67
+ rubocop (1.27.0)
200
68
  parallel (~> 1.10)
201
69
  parser (>= 3.1.0.0)
202
70
  rainbow (>= 2.2.2, < 4.0)
203
71
  regexp_parser (>= 1.8, < 3.0)
204
72
  rexml
205
- rubocop-ast (>= 1.15.1, < 2.0)
73
+ rubocop-ast (>= 1.16.0, < 2.0)
206
74
  ruby-progressbar (~> 1.7)
207
75
  unicode-display_width (>= 1.4.0, < 3.0)
208
- rubocop-ast (1.15.2)
209
- parser (>= 3.0.1.1)
76
+ rubocop-ast (1.17.0)
77
+ parser (>= 3.1.1.0)
210
78
  rubocop-rake (0.6.0)
211
79
  rubocop (~> 1.0)
212
- rubocop-rspec (2.8.0)
80
+ rubocop-rspec (2.9.0)
213
81
  rubocop (~> 1.19)
214
82
  ruby-progressbar (1.11.0)
215
83
  ruby2_keywords (0.0.5)
216
84
  sawyer (0.8.2)
217
85
  addressable (>= 2.3.5)
218
86
  faraday (> 0.8, < 2.0)
219
- strscan (3.0.1)
220
87
  thor (1.2.1)
221
- timeout (0.2.0)
222
- tzinfo (2.0.4)
223
- concurrent-ruby (~> 1.0)
224
88
  unicode-display_width (2.1.0)
225
- websocket-driver (0.7.5)
226
- websocket-extensions (>= 0.1.0)
227
- websocket-extensions (0.1.5)
228
- zeitwerk (2.5.4)
229
89
 
230
90
  PLATFORMS
231
91
  x86_64-darwin-20
@@ -238,8 +98,8 @@ DEPENDENCIES
238
98
  rake (~> 13.0)
239
99
  rspec (~> 3.0)
240
100
  rubocop (~> 1.21)
241
- rubocop-rake (>= 0.6.0, < 1)
242
- rubocop-rspec (>= 2, < 3)
101
+ rubocop-rake (>= 0.6.0)
102
+ rubocop-rspec (>= 2)
243
103
 
244
104
  BUNDLED WITH
245
105
  2.3.7
data/README.md CHANGED
@@ -67,21 +67,39 @@ group :development, :test do
67
67
  end
68
68
  ```
69
69
 
70
- ### Extending your test suite
70
+ Then run the following command:
71
71
 
72
- Add the following in `spec/support.rb`:
72
+ ``` ruby
73
+ $ bundle exec jiminy init
74
+ ```
75
+
76
+ ## Configuration
77
+
78
+ The init command will add this file to your repo. Change the settings here to configure Jiminy:
73
79
 
74
80
  ``` ruby
75
- require "jiminy/rspec"
81
+ # config/jiminy.rb
82
+ Jiminy.configure do |config|
83
+ config.ci_workflow_name = "build_and_test"
76
84
 
77
- RSpec.configure do |config|
78
- config.before(:suite) { Jiminy.reset_results_file! }
79
- config.around do |example|
80
- Jiminy.wrap_rspec_example(example)
81
- end
85
+ config.project_username = "bodacious"
86
+
87
+ # NOTE: This is case sensitive on CircleCI
88
+ config.project_reponame = "jiminy"
89
+
90
+ config.circle_ci_api_token = ENV["CIRCLE_CI_API_TOKEN"]
91
+
92
+ config.github_token = ENV["GITHUB_TOKEN"]
93
+
94
+ # config.ignore_file_path = File.join("./.jiminy_ignores.yml")
95
+
96
+ # config.temp_file_location = File.join("./tmp/jiminy/results.yml")
82
97
  end
83
98
  ```
84
99
 
100
+ _NOTE: This file must be named `config/jiminy.rb` or **the gem will not detect the configuration**._
101
+
102
+
85
103
  ### Running the CLI
86
104
 
87
105
  ``` bash
@@ -114,32 +132,6 @@ Call the Jiminy CLI from a GitHub action:
114
132
  bundle exec jiminy report --commit ${{ github.event.pull_request.head.sha }} --pr-number=$PR_NUMBER --poll-interval=15 --timeout=300
115
133
  ```
116
134
 
117
- ## Configuration
118
-
119
- Add an initializer to your Rails initializers directory:
120
-
121
- ``` ruby
122
- # config/initializers/jiminy.rb
123
- Jiminy.configure do |config|
124
- config.ci_workflow_name = "build_and_test"
125
-
126
- config.project_username = "bodacious"
127
-
128
- # NOTE: This is case sensitive on CircleCI
129
- config.project_reponame = "jiminy"
130
-
131
- config.circle_ci_api_token = ENV["CIRCLE_CI_API_TOKEN"]
132
-
133
- config.github_token = ENV["GITHUB_TOKEN"]
134
-
135
- # config.ignore_file_path = File.join("./.jiminy_ignores.yml")
136
-
137
- # config.temp_file_location = File.join("./tmp/jiminy/results.yml")
138
- end
139
- ```
140
-
141
- _NOTE: This file must be named `config/initializers/jiminy.rb` or **the gem will not detect the configuration**._
142
-
143
135
  ## Ignoring instances
144
136
 
145
137
  If you're adding Jiminy to an existing app, you might want to silence some of the existing warnings and focus on preventing new n+1s being introduced.
data/Rakefile CHANGED
@@ -9,4 +9,4 @@ require "rubocop/rake_task"
9
9
 
10
10
  RuboCop::RakeTask.new
11
11
 
12
- task default: %i[spec rubocop]
12
+ task default: %i(spec rubocop)
data/exe/jiminy CHANGED
@@ -1,7 +1,6 @@
1
1
  #!/usr/bin/env ruby
2
2
  # frozen_string_literal: true
3
3
 
4
- require "bundler/setup"
5
4
  require "jiminy/setup"
6
5
  require "jiminy/cli"
7
6
  require "jiminy/reporting"
data/jiminy.gemspec CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  require_relative "lib/jiminy/version"
4
4
 
5
+ # rubocop:disable Metrics/BlockLength
5
6
  Gem::Specification.new do |spec|
6
7
  spec.name = "jiminy"
7
8
  spec.version = Jiminy::VERSION
@@ -30,13 +31,14 @@ Gem::Specification.new do |spec|
30
31
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
31
32
  spec.require_paths = ["lib"]
32
33
 
33
- spec.add_runtime_dependency "octokit", ">= 4", "< 5"
34
- spec.add_runtime_dependency "prosopite", ">= 1", "< 2"
35
- spec.add_runtime_dependency "rails", ">= 5", "< 7.2"
36
- spec.add_runtime_dependency "thor", ">= 1.2", "< 2"
34
+ spec.add_runtime_dependency "octokit", ">= 4"
35
+ spec.add_runtime_dependency "prosopite", ">= 1"
36
+ spec.add_runtime_dependency "rspec"
37
+ spec.add_runtime_dependency "thor", ">= 1.2"
37
38
  spec.add_development_dependency "byebug"
38
- spec.add_development_dependency "rubocop", ">= 1", "< 2"
39
- spec.add_development_dependency "rubocop-rake", ">= 0.6.0", "< 1"
40
- spec.add_development_dependency "rubocop-rspec", ">= 2", "< 3"
39
+ spec.add_development_dependency "rubocop", ">= 1"
40
+ spec.add_development_dependency "rubocop-rake", ">= 0.6.0"
41
+ spec.add_development_dependency "rubocop-rspec", ">= 2"
41
42
  spec.metadata["rubygems_mfa_required"] = "true"
42
43
  end
44
+ # rubocop:enable Metrics/BlockLength
data/lib/jiminy/cli.rb CHANGED
@@ -2,16 +2,18 @@
2
2
 
3
3
  module Jiminy
4
4
  require "thor"
5
- require "byebug"
6
5
  class CLI < Thor
7
6
  require "jiminy/reporting/ci_providers/circle_ci"
7
+ include Thor::Actions
8
8
  include Jiminy::Reporting::CIProviders
9
9
 
10
10
  class WorkflowStillRunningError < StandardError; end
11
11
  private_constant :WorkflowStillRunningError
12
12
 
13
- MAX_TIMEOUT = 1800 # 1 hour
14
- POLL_INTERVAL = 60 # 1 min
13
+ MAX_TIMEOUT_SECONDS = 1800
14
+ POLL_INTERVAL_SECONDS = 60
15
+
16
+ source_root File.expand_path("templates/", __dir__)
15
17
 
16
18
  def self.exit_on_failure?
17
19
  false
@@ -19,37 +21,41 @@ module Jiminy
19
21
 
20
22
  desc "Report results", "Reports the results of tests"
21
23
  method_option :commit, type: :string, aliases: "c", required: true,
22
- banner: "3e078f8770743549b722382ec5d412a30b9fdcc5",
23
- desc: "The full SHA for the current HEAD commit"
24
- method_option :pr_number, type: :numeric, aliases: %w[pr p], required: true,
25
- banner: "1",
26
- desc: "The GitHub PR number"
24
+ banner: "3e078f8770743549b722382ec5d412a30b9fdcc5",
25
+ desc: "The full SHA for the current HEAD commit"
26
+ method_option :pr_number, type: :numeric, aliases: %w(pr p), required: true,
27
+ banner: "1",
28
+ desc: "The GitHub PR number"
27
29
  method_option :dry_run, type: :boolean, default: false, lazy_default: true,
28
- desc: "Print to STDOUT instead of leaving a comment on GitHub"
29
- method_option :timeout, type: :numeric, aliases: %w[max-timeout], default: MAX_TIMEOUT,
30
- desc: "How long to poll CircleCI before timing out (in seconds)"
31
- method_option :poll_interval, type: :numeric, aliases: %w[poll-interval], default: POLL_INTERVAL,
32
- desc: "How frequently to poll CircleCI (in seconds)"
30
+ desc: "Print to STDOUT instead of leaving a comment on GitHub"
31
+ method_option :timeout, type: :numeric, aliases: %w(max-timeout), default: MAX_TIMEOUT_SECONDS,
32
+ desc: "How long to poll CircleCI before timing out (in seconds)"
33
+ method_option :poll_interval, type: :numeric, aliases: %w(poll-interval), default: POLL_INTERVAL_SECONDS,
34
+ desc: "How frequently to poll CircleCI (in seconds)"
33
35
  method_option :source, type: :string, default: "circleci",
34
- desc: "Where are the results.yml files we should report?"
36
+ desc: "Where are the results.yml files we should report?"
35
37
  def report
36
38
  self.start_time = Time.now
37
39
  artifact_urls = artifacts.map(&:url)
38
-
39
40
  Jiminy::Reporting.report!(*artifact_urls,
40
- pr_number: options[:pr_number],
41
- dry_run: options[:dry_run])
41
+ pr_number: options[:pr_number],
42
+ dry_run: options[:dry_run])
42
43
 
43
44
  $stdout.puts "Reported N+1s successfully"
44
45
  exit(0)
45
46
  end
46
47
 
48
+ desc "Install Jiminy", "Installs jiminy configuration files in your app"
49
+ def init
50
+ template("config.rb", "./config/jiminy.rb")
51
+ end
52
+
47
53
  # rubocop:disable Metrics/BlockLength
48
54
  no_tasks do
49
55
  attr_accessor :start_time
50
56
 
51
57
  def poll_interval
52
- options[:poll_interval] || POLL_INTERVAL
58
+ options[:poll_interval] || POLL_INTERVAL_SECONDS
53
59
  end
54
60
 
55
61
  def source
@@ -57,7 +63,7 @@ module Jiminy
57
63
  end
58
64
 
59
65
  def max_timeout
60
- options[:timeout] || MAX_TIMEOUT
66
+ options[:timeout] || MAX_TIMEOUT_SECONDS
61
67
  end
62
68
 
63
69
  def timed_out?
@@ -84,7 +90,7 @@ module Jiminy
84
90
  end
85
91
  end
86
92
 
87
- # rubocop:disable Metrics/AbcSize, Metrics/MethodLength, Metrics/CyclomaticComplexity
93
+ # rubocop:disable Metrics/AbcSize
88
94
  def workflow
89
95
  @_workflow ||= begin
90
96
  result = CircleCI::Workflow.find(pipeline_id: pipeline.id, workflow_name: Jiminy.config.ci_workflow_name)
@@ -107,7 +113,7 @@ module Jiminy
107
113
  abort("Process timed out after #{Time.now - start_time} seconds")
108
114
  end
109
115
  end
110
- # rubocop:enable Metrics/AbcSize, Metrics/MethodLength, Metrics/CyclomaticComplexity
116
+ # rubocop:enable Metrics/AbcSize
111
117
 
112
118
  def jobs
113
119
  @_jobs ||= CircleCI::Job.all(workflow_id: workflow.id)
@@ -127,8 +133,12 @@ module Jiminy
127
133
  Local::Artifact.all
128
134
  end
129
135
 
136
+ def test_job
137
+ @_test_job ||= jobs.detect { |job| job.name == Jiminy.config.ci_job_name }
138
+ end
139
+
130
140
  def artifacts_from_circle_ci
131
- CircleCI::Artifact.all(job_number: jobs.first.job_number)
141
+ CircleCI::Artifact.all(job_number: test_job.job_number)
132
142
  end
133
143
  alias_method :artifacts_from_circleci, :artifacts_from_circle_ci
134
144
  end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "logger"
4
+
3
5
  module Jiminy
4
6
  class Configuration
5
7
  DEFAULT_CONFIG_READER = -> { instance_variable_get(:"@_#{__callee__}" || raise_missing_required(__callee__)) }
@@ -34,6 +36,8 @@ module Jiminy
34
36
  end
35
37
  end
36
38
 
39
+ define_config :ci_job_name, default: "test"
40
+
37
41
  define_config :ci_workflow_name, default: "build"
38
42
 
39
43
  define_config :circle_ci_api_token
@@ -42,6 +46,8 @@ module Jiminy
42
46
 
43
47
  define_config :ignore_file_path, default: File.join("./jiminy_ignores.yml")
44
48
 
49
+ define_config :logger, default: Logger.new(IO::NULL)
50
+
45
51
  define_config :project_reponame
46
52
 
47
53
  define_config :project_username
@@ -58,25 +64,25 @@ module Jiminy
58
64
 
59
65
  private
60
66
 
61
- def defined_configs_with_defaults
62
- self.class.defined_configs.select { |_config_name, options| options[:default] }
63
- end
67
+ def defined_configs_with_defaults
68
+ self.class.defined_configs.select { |_config_name, options| options[:default] }
69
+ end
64
70
 
65
- def required_configs
66
- self.class.defined_configs.select { |_config_name, options| options[:required] }
67
- end
71
+ def required_configs
72
+ self.class.defined_configs.select { |_config_name, options| options[:required] }
73
+ end
68
74
 
69
- def apply_defaults!
70
- defined_configs_with_defaults.each do |config_name, options|
71
- public_send(:"#{config_name}=", options[:default])
75
+ def apply_defaults!
76
+ defined_configs_with_defaults.each do |config_name, options|
77
+ public_send(:"#{config_name}=", options[:default])
78
+ end
72
79
  end
73
- end
74
80
 
75
- def raise_missing_required(config_name)
76
- return unless required_configs.key?(config_name)
81
+ def raise_missing_required(config_name)
82
+ return unless required_configs.key?(config_name)
77
83
 
78
- raise MissingConfigError, config_name
79
- end
84
+ raise MissingConfigError, config_name
85
+ end
80
86
  end
81
87
 
82
88
  module ConfigurationMethods
@@ -93,5 +99,9 @@ module Jiminy
93
99
  def configured?
94
100
  !!configuration
95
101
  end
102
+
103
+ def logger
104
+ configuration.logger
105
+ end
96
106
  end
97
107
  end
@@ -6,12 +6,12 @@ module Jiminy
6
6
 
7
7
  private
8
8
 
9
- def env_config
10
- @_env_config ||= Reporting::CIProviders::Github::Configuration.new
11
- end
9
+ def env_config
10
+ @_env_config ||= Reporting::CIProviders::Github::Configuration.new
11
+ end
12
12
 
13
- def client
14
- @_client ||= Octokit::Client.new(access_token: env_config.github_token)
15
- end
13
+ def client
14
+ @_client ||= Octokit::Client.new(access_token: env_config.github_token)
15
+ end
16
16
  end
17
17
  end
@@ -5,21 +5,19 @@ module Jiminy
5
5
  class NPlusOne
6
6
  attr_reader :file, :location
7
7
 
8
- LOCATION_MATCHER = /(?<file>.+\.rb):
9
- (?<line>\d+):in\s`
10
- (?:block\sin\s)?
11
- (?<method_name>.+)'
12
- /x.freeze
8
+ LOCATION_REGEXP = /^(?<file>[\w_\-\/.]+\.e?rb):(?<line>\d+):in\s`(?:block\sin\s)?(?<method>.+)'/x.freeze
13
9
 
14
10
  EXAMPLES_COUNT = 3
15
11
 
16
12
  def initialize(location:, queries: [])
17
13
  @location = location.to_s.strip
18
14
  @queries = queries
19
- match = location.match(LOCATION_MATCHER)
20
- @line = match[:line]
21
- @method_name = match[:method_name]
22
- @file = match[:file]
15
+ match_result = location.match(LOCATION_REGEXP)
16
+
17
+ @file = match_result[:file]
18
+ @line = match_result[:line]
19
+ @method_name = match_result[:method]
20
+
23
21
  freeze
24
22
  end
25
23
 
@@ -35,16 +33,16 @@ module Jiminy
35
33
 
36
34
  private
37
35
 
38
- attr_reader :queries, :line, :method_name
36
+ attr_reader :queries, :line, :method_name
39
37
 
40
- def attributes
41
- {
42
- "file" => file,
43
- "line" => line,
44
- "method" => method_name,
45
- "examples" => queries.take(EXAMPLES_COUNT)
46
- }
47
- end
38
+ def attributes
39
+ {
40
+ "file" => file,
41
+ "line" => line,
42
+ "method" => method_name,
43
+ "examples" => queries.take(EXAMPLES_COUNT)
44
+ }
45
+ end
48
46
  end
49
47
  end
50
48
  end
@@ -35,9 +35,9 @@ module Jiminy
35
35
 
36
36
  private
37
37
 
38
- def tmp_file_recorder
39
- @_tmp_file_recorder ||= TmpFileRecorder.new
40
- end
38
+ def tmp_file_recorder
39
+ @_tmp_file_recorder ||= TmpFileRecorder.new
40
+ end
41
41
  end
42
42
  end
43
43
  end
@@ -6,34 +6,46 @@ module Jiminy
6
6
  class TmpFileRecorder
7
7
  require_relative "../n_plus_one"
8
8
 
9
+ # rubocop:disable Metrics/AbcSize
9
10
  def record(location:, queries:)
10
11
  yaml_content = File.read(Jiminy.config.temp_file_location)
11
12
  array = YAML.safe_load(yaml_content)
12
13
  n_plus_one = NPlusOne.new(location: location, queries: queries)
13
14
 
14
- array << n_plus_one.to_h unless location_in_array?(location, array) || filepath_ignored?(n_plus_one.file)
15
+ if filepath_ignored?(n_plus_one.file)
16
+ Jiminy.logger.debug { "Ignoring n+1 instance #{n_plus_one}" }
17
+ return
18
+ end
19
+
20
+ if location_in_array?(location, array)
21
+ Jiminy.logger.debug { "Already reported n+1 instance #{n_plus_one}" }
22
+ return
23
+ end
24
+
25
+ array << n_plus_one.to_h
15
26
  File.write(Jiminy.config.temp_file_location, array.to_yaml)
16
27
  end
28
+ # rubocop:enable Metrics/AbcSize
17
29
 
18
30
  private
19
31
 
20
- def location_in_array?(location, array)
21
- array.detect { |hash| hash.key?(location) }
22
- end
32
+ def location_in_array?(location, array)
33
+ array.detect { |hash| hash.key?(location) }
34
+ end
23
35
 
24
- def filepath_ignored?(filepath)
25
- ignored_files.include?(filepath)
26
- end
36
+ def filepath_ignored?(filepath)
37
+ ignored_files.include?(filepath)
38
+ end
27
39
 
28
- def ignored_files
29
- @_ignored_files ||= load_ignored_files
30
- end
40
+ def ignored_files
41
+ @_ignored_files ||= load_ignored_files
42
+ end
31
43
 
32
- def load_ignored_files
33
- return [] unless File.exist?(Jiminy.config.ignore_file_path)
44
+ def load_ignored_files
45
+ return [] unless File.exist?(Jiminy.config.ignore_file_path)
34
46
 
35
- YAML.load_file(Jiminy.config.ignore_file_path) || []
36
- end
47
+ YAML.load_file(Jiminy.config.ignore_file_path) || []
48
+ end
37
49
  end
38
50
  end
39
51
  end
@@ -20,3 +20,9 @@ module Jiminy
20
20
  end
21
21
  end
22
22
  end
23
+ RSpec.configure do |config|
24
+ config.before(:suite) { Jiminy.reset_results_file! }
25
+ config.around do |example|
26
+ Jiminy.wrap_rspec_example(example)
27
+ end
28
+ end
@@ -11,16 +11,16 @@ module Jiminy
11
11
 
12
12
  private
13
13
 
14
- def _test_n_plus_one_detection
15
- yield and return if Prosopite.scan?
14
+ def _test_n_plus_one_detection
15
+ yield and return if Prosopite.scan?
16
16
 
17
- begin
18
- Prosopite.scan
19
- yield
20
- ensure
21
- Prosopite.finish
17
+ begin
18
+ Prosopite.scan
19
+ yield
20
+ ensure
21
+ Prosopite.finish
22
+ end
22
23
  end
23
- end
24
24
  end
25
25
  end
26
26
  end
@@ -14,30 +14,30 @@ module Jiminy
14
14
  end
15
15
 
16
16
  def perform!
17
- puts url
17
+ Jiminy.logger.debug { "API request: #@url" }
18
18
  response
19
19
  end
20
20
 
21
21
  private
22
22
 
23
- attr_reader :url
23
+ attr_reader :url
24
24
 
25
- def response
26
- @_response ||= http.request(request)
27
- end
25
+ def response
26
+ @_response ||= http.request(request)
27
+ end
28
28
 
29
- def request
30
- @_request ||= Net::HTTP::Get.new(url).tap do |req|
31
- req[CIRCLE_TOKEN_HEADER] = Jiminy.config.circle_ci_api_token
29
+ def request
30
+ @_request ||= Net::HTTP::Get.new(url).tap do |req|
31
+ req[CIRCLE_TOKEN_HEADER] = Jiminy.config.circle_ci_api_token
32
+ end
32
33
  end
33
- end
34
34
 
35
- def http
36
- @_http ||= Net::HTTP.new(url.host, url.port).tap do |http_instance|
37
- http_instance.use_ssl = true
38
- http_instance.verify_mode = OpenSSL::SSL::VERIFY_NONE
35
+ def http
36
+ @_http ||= Net::HTTP.new(url.host, url.port).tap do |http_instance|
37
+ http_instance.use_ssl = true
38
+ http_instance.verify_mode = OpenSSL::SSL::VERIFY_NONE
39
+ end
39
40
  end
40
- end
41
41
  end
42
42
  end
43
43
  end
@@ -8,7 +8,9 @@ module Jiminy
8
8
  define_attribute_readers :url
9
9
 
10
10
  def self.all(job_number:)
11
- fetch_api_resource("project/gh/#{Jiminy.config.repo_path}/#{job_number}/artifacts")
11
+ # TODO: Support different results names here
12
+ fetch_api_resource("project/gh/#{Jiminy.config.repo_path}/#{job_number}/artifacts").
13
+ select { |artifact| artifact.url.end_with?("jiminy/results.yml") }
12
14
  end
13
15
  end
14
16
  end
@@ -5,7 +5,7 @@ module Jiminy
5
5
  module CIProviders
6
6
  module CircleCI
7
7
  class Job < Base
8
- define_attribute_readers :job_number
8
+ define_attribute_readers :job_number, :name
9
9
 
10
10
  def self.all(workflow_id:)
11
11
  fetch_api_resource("workflow/#{workflow_id}/job")
@@ -44,9 +44,9 @@ module Jiminy
44
44
 
45
45
  private
46
46
 
47
- def match_data
48
- @_match_data ||= ensure_env_variable("CIRCLE_PULL_REQUEST").match(PR_URL_MATCHERS)
49
- end
47
+ def match_data
48
+ @_match_data ||= ensure_env_variable("CIRCLE_PULL_REQUEST").match(PR_URL_MATCHERS)
49
+ end
50
50
  end
51
51
  end
52
52
  end
@@ -16,12 +16,12 @@ module Jiminy
16
16
 
17
17
  private
18
18
 
19
- def ensure_configuration(name)
20
- value = Jiminy.config.public_send(name)
21
- return value unless value.empty?
19
+ def ensure_configuration(name)
20
+ value = Jiminy.config.public_send(name)
21
+ return value unless value.empty?
22
22
 
23
- raise("Please provide a value for Jiminy.config.#{name}")
24
- end
23
+ raise("Please provide a value for Jiminy.config.#{name}")
24
+ end
25
25
  end
26
26
  end
27
27
  end
@@ -31,9 +31,9 @@ module Jiminy
31
31
 
32
32
  private
33
33
 
34
- def markdown_template
35
- @_markdown_template ||= File.read(File.join(TEMPLATES_DIR, "n_plus_one.md.erb"))
36
- end
34
+ def markdown_template
35
+ @_markdown_template ||= File.read(File.join(TEMPLATES_DIR, "n_plus_one.md.erb"))
36
+ end
37
37
  end
38
38
  end
39
39
  end
@@ -15,11 +15,11 @@ module Jiminy
15
15
 
16
16
  private
17
17
 
18
- attr_reader :header, :body
18
+ attr_reader :header, :body
19
19
 
20
- def report_body
21
- "#{header}\n\n#{body}"
22
- end
20
+ def report_body
21
+ "#{header}\n\n#{body}"
22
+ end
23
23
  end
24
24
  end
25
25
  end
@@ -19,9 +19,9 @@ module Jiminy
19
19
 
20
20
  private
21
21
 
22
- attr_reader :pr_number
22
+ attr_reader :pr_number
23
23
 
24
- alias comment_body report_body
24
+ alias comment_body report_body
25
25
  end
26
26
  end
27
27
  end
@@ -7,8 +7,6 @@ module Jiminy
7
7
  require "yaml"
8
8
  require "jiminy/github_apiable"
9
9
 
10
- class MissingFileError < StandardError; end
11
-
12
10
  include GithubAPIable
13
11
 
14
12
  INSTANCE_SEPARATOR = "\n"
@@ -26,46 +24,46 @@ module Jiminy
26
24
 
27
25
  private
28
26
 
29
- attr_reader :source_filepath, :pr_number
27
+ attr_reader :source_filepath, :pr_number
30
28
 
31
- def instances
32
- @_instances ||= YAML.safe_load(file_content).map do |hash|
33
- options = hash.values.first.transform_keys!(&:to_sym)
34
- NPlusOne.new(**options)
29
+ def instances
30
+ @_instances ||= YAML.safe_load(file_content).map do |hash|
31
+ options = hash.values.first.transform_keys!(&:to_sym)
32
+ NPlusOne.new(**options)
33
+ end
35
34
  end
36
- end
37
35
 
38
- def file_content
39
- @_file_content ||= file_content_for_filepath(source_filepath)
40
- end
36
+ def file_content
37
+ @_file_content ||= file_content_for_filepath(source_filepath)
38
+ end
41
39
 
42
- def file_content_for_filepath(source_filepath)
43
- return file_content_for_remote_file(source_filepath) if source_filepath.start_with?("https://")
40
+ def file_content_for_filepath(source_filepath)
41
+ return file_content_for_remote_file(source_filepath) if source_filepath.start_with?("https://")
44
42
 
45
- file_content_for_local_file(source_filepath)
46
- end
43
+ file_content_for_local_file(source_filepath)
44
+ end
47
45
 
48
- def file_content_for_remote_file(source_filepath)
49
- URI.parse(source_filepath).open({ "Circle-Token" => ENV["CIRCLE_CI_API_TOKEN"] }).read
50
- end
46
+ def file_content_for_remote_file(source_filepath)
47
+ URI.parse(source_filepath).open({ "Circle-Token" => ENV["CIRCLE_CI_API_TOKEN"] }).read
48
+ end
51
49
 
52
- def file_content_for_local_file(source_filepath)
53
- File.read(source_filepath)
54
- end
50
+ def file_content_for_local_file(source_filepath)
51
+ File.read(source_filepath)
52
+ end
55
53
 
56
- def build_comment_body
57
- instances.map do |instance|
58
- file = file_from_instance(instance)
59
- instance.blob_url = file.blob_url
60
- instance.to_markdown
61
- end.join(INSTANCE_SEPARATOR)
62
- end
54
+ def build_comment_body
55
+ Array(instances).map do |instance|
56
+ file = file_from_instance(instance) || next
57
+ instance.blob_url = file.blob_url
58
+ instance.to_markdown
59
+ end.compact.join(INSTANCE_SEPARATOR)
60
+ end
63
61
 
64
- def file_from_instance(instance)
65
- client.pull_request_files(env_config.repo_path, pr_number).detect do |file|
66
- file.filename == instance.file
67
- end or raise(MissingFileError)
68
- end
62
+ def file_from_instance(instance)
63
+ client.pull_request_files(env_config.repo_path, pr_number).detect do |file|
64
+ file.filename == instance.file
65
+ end
66
+ end
69
67
  end
70
68
  end
71
69
  end
@@ -21,11 +21,14 @@ module Jiminy
21
21
  comment_content = yaml_files.map do |yaml_file|
22
22
  YAMLFileCommentPresenter.new(source_filepath: yaml_file, pr_number: options[:pr_number]).to_s
23
23
  end.join(LINE_SEPARATOR)
24
+
25
+ return if comment_content.strip.empty?
26
+
24
27
  if options[:dry_run]
25
28
  Reporters::DryRunReporter.new(header: COMMENT_HEADER, body: comment_content).report!
26
29
  else
27
30
  Reporters::GithubReporter.new(header: COMMENT_HEADER, body: comment_content,
28
- pr_number: options[:pr_number]).report!
31
+ pr_number: options[:pr_number]).report!
29
32
  end
30
33
  end
31
34
  end
data/lib/jiminy/setup.rb CHANGED
@@ -5,6 +5,6 @@ require_relative "configuration"
5
5
  Jiminy.extend(Jiminy::ConfigurationMethods)
6
6
 
7
7
  begin
8
- load "./config/initializers/jiminy.rb"
8
+ load "./config/jiminy.rb"
9
9
  rescue LoadError; nil
10
10
  end
@@ -0,0 +1,15 @@
1
+ Jiminy.configure do |config|
2
+ config.ci_workflow_name = "build_and_test"
3
+
4
+ config.project_username = "<your github username>"
5
+
6
+ config.project_reponame = Rails.application.name
7
+
8
+ config.circle_ci_api_token = ENV["CIRCLE_CI_API_TOKEN"]
9
+
10
+ config.github_token = ENV["GITHUB_TOKEN"]
11
+
12
+ config.ignore_file_path = File.join("./.jiminy_ignores.yml")
13
+
14
+ config.temp_file_location = File.join("./tmp/jiminy/results.yml")
15
+ end
@@ -2,7 +2,7 @@
2
2
  - [<%= file %>](<%= blob_url_with_line %>)
3
3
 
4
4
  <details>
5
- <summary>SQL sample from the issue detected</summary>
5
+ <summary>Click to view samples of this issue</summary>
6
6
 
7
7
  ``` sql
8
8
  <%- for example in examples %>
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Jiminy
4
- VERSION = "0.1.0.pre1"
4
+ VERSION = "0.2.0"
5
5
  end
data/lib/jiminy.rb CHANGED
@@ -3,6 +3,7 @@
3
3
  require_relative "jiminy/version"
4
4
  require_relative "jiminy/setup"
5
5
  require_relative "jiminy/recording" if defined?(Rails)
6
+ require_relative "jiminy/rspec" if defined?(Rspec) && ENV["RAILS_ENV"] == "test"
6
7
 
7
8
  module Jiminy
8
9
  module_function
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jiminy
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0.pre1
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bodacious
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-04-12 00:00:00.000000000 Z
11
+ date: 2022-05-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: octokit
@@ -17,9 +17,6 @@ dependencies:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
19
  version: '4'
20
- - - "<"
21
- - !ruby/object:Gem::Version
22
- version: '5'
23
20
  type: :runtime
24
21
  prerelease: false
25
22
  version_requirements: !ruby/object:Gem::Requirement
@@ -27,9 +24,6 @@ dependencies:
27
24
  - - ">="
28
25
  - !ruby/object:Gem::Version
29
26
  version: '4'
30
- - - "<"
31
- - !ruby/object:Gem::Version
32
- version: '5'
33
27
  - !ruby/object:Gem::Dependency
34
28
  name: prosopite
35
29
  requirement: !ruby/object:Gem::Requirement
@@ -37,9 +31,6 @@ dependencies:
37
31
  - - ">="
38
32
  - !ruby/object:Gem::Version
39
33
  version: '1'
40
- - - "<"
41
- - !ruby/object:Gem::Version
42
- version: '2'
43
34
  type: :runtime
44
35
  prerelease: false
45
36
  version_requirements: !ruby/object:Gem::Requirement
@@ -47,29 +38,20 @@ dependencies:
47
38
  - - ">="
48
39
  - !ruby/object:Gem::Version
49
40
  version: '1'
50
- - - "<"
51
- - !ruby/object:Gem::Version
52
- version: '2'
53
41
  - !ruby/object:Gem::Dependency
54
- name: rails
42
+ name: rspec
55
43
  requirement: !ruby/object:Gem::Requirement
56
44
  requirements:
57
45
  - - ">="
58
46
  - !ruby/object:Gem::Version
59
- version: '5'
60
- - - "<"
61
- - !ruby/object:Gem::Version
62
- version: '7.2'
47
+ version: '0'
63
48
  type: :runtime
64
49
  prerelease: false
65
50
  version_requirements: !ruby/object:Gem::Requirement
66
51
  requirements:
67
52
  - - ">="
68
53
  - !ruby/object:Gem::Version
69
- version: '5'
70
- - - "<"
71
- - !ruby/object:Gem::Version
72
- version: '7.2'
54
+ version: '0'
73
55
  - !ruby/object:Gem::Dependency
74
56
  name: thor
75
57
  requirement: !ruby/object:Gem::Requirement
@@ -77,9 +59,6 @@ dependencies:
77
59
  - - ">="
78
60
  - !ruby/object:Gem::Version
79
61
  version: '1.2'
80
- - - "<"
81
- - !ruby/object:Gem::Version
82
- version: '2'
83
62
  type: :runtime
84
63
  prerelease: false
85
64
  version_requirements: !ruby/object:Gem::Requirement
@@ -87,9 +66,6 @@ dependencies:
87
66
  - - ">="
88
67
  - !ruby/object:Gem::Version
89
68
  version: '1.2'
90
- - - "<"
91
- - !ruby/object:Gem::Version
92
- version: '2'
93
69
  - !ruby/object:Gem::Dependency
94
70
  name: byebug
95
71
  requirement: !ruby/object:Gem::Requirement
@@ -111,9 +87,6 @@ dependencies:
111
87
  - - ">="
112
88
  - !ruby/object:Gem::Version
113
89
  version: '1'
114
- - - "<"
115
- - !ruby/object:Gem::Version
116
- version: '2'
117
90
  type: :development
118
91
  prerelease: false
119
92
  version_requirements: !ruby/object:Gem::Requirement
@@ -121,9 +94,6 @@ dependencies:
121
94
  - - ">="
122
95
  - !ruby/object:Gem::Version
123
96
  version: '1'
124
- - - "<"
125
- - !ruby/object:Gem::Version
126
- version: '2'
127
97
  - !ruby/object:Gem::Dependency
128
98
  name: rubocop-rake
129
99
  requirement: !ruby/object:Gem::Requirement
@@ -131,9 +101,6 @@ dependencies:
131
101
  - - ">="
132
102
  - !ruby/object:Gem::Version
133
103
  version: 0.6.0
134
- - - "<"
135
- - !ruby/object:Gem::Version
136
- version: '1'
137
104
  type: :development
138
105
  prerelease: false
139
106
  version_requirements: !ruby/object:Gem::Requirement
@@ -141,9 +108,6 @@ dependencies:
141
108
  - - ">="
142
109
  - !ruby/object:Gem::Version
143
110
  version: 0.6.0
144
- - - "<"
145
- - !ruby/object:Gem::Version
146
- version: '1'
147
111
  - !ruby/object:Gem::Dependency
148
112
  name: rubocop-rspec
149
113
  requirement: !ruby/object:Gem::Requirement
@@ -151,9 +115,6 @@ dependencies:
151
115
  - - ">="
152
116
  - !ruby/object:Gem::Version
153
117
  version: '2'
154
- - - "<"
155
- - !ruby/object:Gem::Version
156
- version: '3'
157
118
  type: :development
158
119
  prerelease: false
159
120
  version_requirements: !ruby/object:Gem::Requirement
@@ -161,9 +122,6 @@ dependencies:
161
122
  - - ">="
162
123
  - !ruby/object:Gem::Version
163
124
  version: '2'
164
- - - "<"
165
- - !ruby/object:Gem::Version
166
- version: '3'
167
125
  description: 'Wraps around your CI integration to detect and warn about n+1 queries
168
126
  before they''re merged
169
127
 
@@ -221,6 +179,7 @@ files:
221
179
  - lib/jiminy/reporting/yaml_file_comment_presenter.rb
222
180
  - lib/jiminy/rspec.rb
223
181
  - lib/jiminy/setup.rb
182
+ - lib/jiminy/templates/config.rb
224
183
  - lib/jiminy/templates/reporting/comment_header.md.erb
225
184
  - lib/jiminy/templates/reporting/n_plus_one.md.erb
226
185
  - lib/jiminy/version.rb
@@ -243,9 +202,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
243
202
  version: 2.6.0
244
203
  required_rubygems_version: !ruby/object:Gem::Requirement
245
204
  requirements:
246
- - - ">"
205
+ - - ">="
247
206
  - !ruby/object:Gem::Version
248
- version: 1.3.1
207
+ version: '0'
249
208
  requirements: []
250
209
  rubygems_version: 3.3.7
251
210
  signing_key: