whoosh 1.2.1 → 1.2.2

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: f647a91b22fcee8322c6af7bf0ab0ac0108aa3113e2d4db89a3f187bb6a1caa3
4
- data.tar.gz: e46c07b2f4fbaf1233bb52a321ff161952989313adbbfebdb5b7bf1c08675f08
3
+ metadata.gz: 336c26ea284a871dafa4b1d26f173293947e30470000539dbf3e0da799063bea
4
+ data.tar.gz: 6acf7d5007d008070aaddb543a42de5bddf3b3aa2de838e1be5ccba5d18b9442
5
5
  SHA512:
6
- metadata.gz: 1ca2dbc63ab481d5d61260cfbfca37768a9498fa74fe90d68f039884f05ca2a79f0fa08da07fdca04ba175e95ab0997d5cc00acf0e8b2bbceef5ca576f8fef91
7
- data.tar.gz: b0361d6a812b80e093125f7be7d5681977ab615b6ebccacbe87753352223e6058e0076dd2eace8e3092a60a57e3387b9ebe0baed4b2f623befeb8f1c38d9435e
6
+ metadata.gz: 86aebc850e904960fe87d08aa7c6e8d6aba82c5e411e66e0e9ae65fb64d5510b8ddae4125099554fecac71907c163ce854d1adc416bd8e84ee5f6f3376556809
7
+ data.tar.gz: 99c067d27463fff53a3cb5818836a11e90e0e86d1f886d211770a6407725b6ca0ba72cd3e52c94bd4b204d57e5d282ff89d102863c9410c545c63e3d60bcf332
@@ -224,45 +224,67 @@ module Whoosh
224
224
  end
225
225
  }
226
226
 
227
- desc "ci", "Run full CI pipeline (lint + security + tests)"
227
+ desc "ci", "Run full CI pipeline (lint + security + audit + tests + coverage)"
228
228
  def ci
229
229
  puts "=> Whoosh CI Pipeline"
230
230
  puts "=" * 50
231
231
  puts ""
232
232
 
233
233
  steps = []
234
+ skipped = []
234
235
 
235
236
  # Step 1: Rubocop (lint)
236
237
  if system("bundle exec rubocop --version > /dev/null 2>&1")
237
238
  steps << { name: "Rubocop (lint)", cmd: "bundle exec rubocop --format simple" }
238
239
  else
239
- puts " [skip] Rubocop not installed (add rubocop to Gemfile)"
240
+ skipped << "Rubocop (add rubocop to Gemfile)"
240
241
  end
241
242
 
242
- # Step 2: Brakeman (security)
243
+ # Step 2: Brakeman (security scan)
243
244
  if system("bundle exec brakeman --version > /dev/null 2>&1")
244
245
  steps << { name: "Brakeman (security)", cmd: "bundle exec brakeman -q --no-pager" }
245
246
  else
246
- puts " [skip] Brakeman not installed (add brakeman to Gemfile)"
247
+ skipped << "Brakeman (add brakeman to Gemfile)"
247
248
  end
248
249
 
249
- # Step 3: RSpec (tests)
250
+ # Step 3: Bundle audit (CVE check)
251
+ if system("bundle exec bundle-audit version > /dev/null 2>&1")
252
+ steps << { name: "Bundle Audit (CVE)", cmd: "bundle exec bundle-audit check --update" }
253
+ elsif system("bundle audit --help > /dev/null 2>&1")
254
+ steps << { name: "Bundle Audit (CVE)", cmd: "bundle audit" }
255
+ else
256
+ skipped << "Bundle Audit (add bundler-audit to Gemfile)"
257
+ end
258
+
259
+ # Step 4: Secret leak scan (built-in, no gem needed)
260
+ steps << { name: "Secret Scan", type: :secret_scan }
261
+
262
+ # Step 5: RSpec (tests)
250
263
  if system("bundle exec rspec --version > /dev/null 2>&1")
251
264
  steps << { name: "RSpec (tests)", cmd: "bundle exec rspec --format progress" }
252
265
  else
253
- puts " [skip] RSpec not installed"
266
+ skipped << "RSpec (add rspec to Gemfile)"
254
267
  end
255
268
 
256
- # Step 4: Gem build check
257
- gemspec = Dir.glob("*.gemspec").first
258
- if gemspec
259
- steps << { name: "Gem build", cmd: "gem build #{gemspec} --quiet" }
260
- end
269
+ # Step 6: Coverage check (built-in)
270
+ steps << { name: "Coverage Check", type: :coverage_check }
271
+
272
+ skipped.each { |s| puts " [skip] #{s}" }
273
+ puts "" unless skipped.empty?
261
274
 
262
275
  failed = []
263
276
  steps.each_with_index do |step, i|
264
277
  puts "--- [#{i + 1}/#{steps.length}] #{step[:name]} ---"
265
- success = system(step[:cmd])
278
+
279
+ success = case step[:type]
280
+ when :secret_scan
281
+ run_secret_scan
282
+ when :coverage_check
283
+ run_coverage_check
284
+ else
285
+ system(step[:cmd])
286
+ end
287
+
266
288
  if success
267
289
  puts " ✓ #{step[:name]} passed"
268
290
  else
@@ -274,7 +296,7 @@ module Whoosh
274
296
 
275
297
  puts "=" * 50
276
298
  if failed.empty?
277
- puts "=> All checks passed! ✓"
299
+ puts "=> All #{steps.length} checks passed! ✓"
278
300
  exit 0
279
301
  else
280
302
  puts "=> FAILED: #{failed.join(', ')}"
@@ -282,6 +304,61 @@ module Whoosh
282
304
  end
283
305
  end
284
306
 
307
+ private
308
+
309
+ def run_secret_scan
310
+ patterns = [
311
+ /(?:api[_-]?key|secret|password|token)\s*[:=]\s*["'][A-Za-z0-9+\/=]{8,}["']/i,
312
+ /(?:sk-|pk-|rk-)[a-zA-Z0-9]{20,}/,
313
+ /-----BEGIN (?:RSA |EC )?PRIVATE KEY-----/,
314
+ /AKIA[0-9A-Z]{16}/, # AWS access key
315
+ ]
316
+
317
+ leaked = []
318
+ Dir.glob("{app.rb,lib/**/*.rb,endpoints/**/*.rb,config/**/*.rb}").each do |file|
319
+ next if file.include?("spec/") || file.include?("test/")
320
+ content = File.read(file) rescue next
321
+ patterns.each do |pattern|
322
+ content.each_line.with_index do |line, i|
323
+ if line.match?(pattern) && !line.include?("ENV[") && !line.include?("ENV.fetch")
324
+ leaked << "#{file}:#{i + 1}: #{line.strip[0..80]}"
325
+ end
326
+ end
327
+ end
328
+ end
329
+
330
+ if leaked.empty?
331
+ true
332
+ else
333
+ puts " Potential secrets found:"
334
+ leaked.each { |l| puts " #{l}" }
335
+ false
336
+ end
337
+ end
338
+
339
+ def run_coverage_check
340
+ coverage_file = File.join(Dir.pwd, "coverage", ".last_run.json")
341
+ unless File.exist?(coverage_file)
342
+ puts " [info] No coverage data found (add simplecov to test_helper for tracking)"
343
+ true # Don't fail if SimpleCov not set up
344
+ else
345
+ require "json"
346
+ data = JSON.parse(File.read(coverage_file))
347
+ coverage = data.dig("result", "line") || data.dig("result", "covered_percent") || 0
348
+ threshold = 80
349
+
350
+ if coverage >= threshold
351
+ puts " Coverage: #{coverage.round(1)}% (threshold: #{threshold}%)"
352
+ true
353
+ else
354
+ puts " Coverage: #{coverage.round(1)}% — below #{threshold}% threshold"
355
+ false
356
+ end
357
+ end
358
+ end
359
+
360
+ public
361
+
285
362
  desc "new NAME", "Create a new Whoosh project"
286
363
  option :minimal, type: :boolean, default: false
287
364
  option :full, type: :boolean, default: false
@@ -150,6 +150,8 @@ module Whoosh
150
150
  gem "rack-test"
151
151
  gem "rubocop", require: false
152
152
  gem "brakeman", require: false
153
+ gem "bundler-audit", require: false
154
+ gem "simplecov", require: false
153
155
  end
154
156
  GEM
155
157
 
@@ -256,6 +258,13 @@ module Whoosh
256
258
  <<~RUBY
257
259
  # frozen_string_literal: true
258
260
 
261
+ require "simplecov"
262
+ SimpleCov.start do
263
+ add_filter "/test/"
264
+ add_filter "/spec/"
265
+ minimum_coverage 80
266
+ end
267
+
259
268
  require "whoosh/test"
260
269
  require_relative "../app"
261
270
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Whoosh
4
- VERSION = "1.2.1"
4
+ VERSION = "1.2.2"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: whoosh
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.1
4
+ version: 1.2.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Johannes Dwi Cahyo