jiminy 0.1.0.pre1 → 0.1.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: ed071d31bf26f4a0beaed34426656d9afbf1a99122b57d27aabcf58a03439910
4
- data.tar.gz: 59c64b03f115328b28da4eb4fda6e573891c770845e97a584cc3d4e400816e37
3
+ metadata.gz: 26a3afee95d97458dce74ea14893726e5edb8111c2db4bb362c557536fb3be44
4
+ data.tar.gz: 034cb7891aee6949d476dd8b519cca22764ff09623e92f10d0e576cef5b42753
5
5
  SHA512:
6
- metadata.gz: 7ec6ebd488281ebf37f80885fd7e824f9cccbcd076447afc91551d9a96791db4e801ae413305b565f920d090b3c79afda48f8cdceae8fd9d9f7cee3ce0c76f05
7
- data.tar.gz: e02a8751271ccd992976f7a98b5fd636d7e3d369d4f1cf9e3eecead0fcc0a29fc56126e9181b526bfc1205c933436506de04d4c409593a13a72b8c2b92c2b1a6
6
+ metadata.gz: 75ead9773e96336b05bf9fc72fa1757efc3860215b4fd585948bc3fa17df5cc9ea46f273b5cab66cbce951e7796641695c5be94444db145ca507008bea70cb4f
7
+ data.tar.gz: a0857f6f144d878d86f04eba20f040919eef944cb3bfcbbab698b2e32953410bccb627235f45b38f97613a1cb6bfca461682d56c066bd3f49cdff009d7839598
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.1.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
@@ -11,29 +11,39 @@ module Jiminy
11
11
  array = YAML.safe_load(yaml_content)
12
12
  n_plus_one = NPlusOne.new(location: location, queries: queries)
13
13
 
14
- array << n_plus_one.to_h unless location_in_array?(location, array) || filepath_ignored?(n_plus_one.file)
14
+ if filepath_ignored?(n_plus_one.file)
15
+ Jiminy.logger.debug("Ignoring n+1 instance #{n_plus_one}")
16
+ return
17
+ end
18
+
19
+ if location_in_array?(location, array)
20
+ Jiminy.logger.debug("Already reported n+1 instance #{n_plus_one}")
21
+ return
22
+ end
23
+
24
+ array << n_plus_one.to_h
15
25
  File.write(Jiminy.config.temp_file_location, array.to_yaml)
16
26
  end
17
27
 
18
28
  private
19
29
 
20
- def location_in_array?(location, array)
21
- array.detect { |hash| hash.key?(location) }
22
- end
30
+ def location_in_array?(location, array)
31
+ array.detect { |hash| hash.key?(location) }
32
+ end
23
33
 
24
- def filepath_ignored?(filepath)
25
- ignored_files.include?(filepath)
26
- end
34
+ def filepath_ignored?(filepath)
35
+ ignored_files.include?(filepath)
36
+ end
27
37
 
28
- def ignored_files
29
- @_ignored_files ||= load_ignored_files
30
- end
38
+ def ignored_files
39
+ @_ignored_files ||= load_ignored_files
40
+ end
31
41
 
32
- def load_ignored_files
33
- return [] unless File.exist?(Jiminy.config.ignore_file_path)
42
+ def load_ignored_files
43
+ return [] unless File.exist?(Jiminy.config.ignore_file_path)
34
44
 
35
- YAML.load_file(Jiminy.config.ignore_file_path) || []
36
- end
45
+ YAML.load_file(Jiminy.config.ignore_file_path) || []
46
+ end
37
47
  end
38
48
  end
39
49
  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
@@ -25,7 +25,7 @@ module Jiminy
25
25
  Reporters::DryRunReporter.new(header: COMMENT_HEADER, body: comment_content).report!
26
26
  else
27
27
  Reporters::GithubReporter.new(header: COMMENT_HEADER, body: comment_content,
28
- pr_number: options[:pr_number]).report!
28
+ pr_number: options[:pr_number]).report!
29
29
  end
30
30
  end
31
31
  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.1.0"
5
5
  end
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.1.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-09 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: