whoosh 1.2.0 → 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 +4 -4
- data/lib/whoosh/cli/main.rb +135 -0
- data/lib/whoosh/cli/project_generator.rb +43 -0
- data/lib/whoosh/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 336c26ea284a871dafa4b1d26f173293947e30470000539dbf3e0da799063bea
|
|
4
|
+
data.tar.gz: 6acf7d5007d008070aaddb543a42de5bddf3b3aa2de838e1be5ccba5d18b9442
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 86aebc850e904960fe87d08aa7c6e8d6aba82c5e411e66e0e9ae65fb64d5510b8ddae4125099554fecac71907c163ce854d1adc416bd8e84ee5f6f3376556809
|
|
7
|
+
data.tar.gz: 99c067d27463fff53a3cb5818836a11e90e0e86d1f886d211770a6407725b6ca0ba72cd3e52c94bd4b204d57e5d282ff89d102863c9410c545c63e3d60bcf332
|
data/lib/whoosh/cli/main.rb
CHANGED
|
@@ -224,6 +224,141 @@ module Whoosh
|
|
|
224
224
|
end
|
|
225
225
|
}
|
|
226
226
|
|
|
227
|
+
desc "ci", "Run full CI pipeline (lint + security + audit + tests + coverage)"
|
|
228
|
+
def ci
|
|
229
|
+
puts "=> Whoosh CI Pipeline"
|
|
230
|
+
puts "=" * 50
|
|
231
|
+
puts ""
|
|
232
|
+
|
|
233
|
+
steps = []
|
|
234
|
+
skipped = []
|
|
235
|
+
|
|
236
|
+
# Step 1: Rubocop (lint)
|
|
237
|
+
if system("bundle exec rubocop --version > /dev/null 2>&1")
|
|
238
|
+
steps << { name: "Rubocop (lint)", cmd: "bundle exec rubocop --format simple" }
|
|
239
|
+
else
|
|
240
|
+
skipped << "Rubocop (add rubocop to Gemfile)"
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
# Step 2: Brakeman (security scan)
|
|
244
|
+
if system("bundle exec brakeman --version > /dev/null 2>&1")
|
|
245
|
+
steps << { name: "Brakeman (security)", cmd: "bundle exec brakeman -q --no-pager" }
|
|
246
|
+
else
|
|
247
|
+
skipped << "Brakeman (add brakeman to Gemfile)"
|
|
248
|
+
end
|
|
249
|
+
|
|
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)
|
|
263
|
+
if system("bundle exec rspec --version > /dev/null 2>&1")
|
|
264
|
+
steps << { name: "RSpec (tests)", cmd: "bundle exec rspec --format progress" }
|
|
265
|
+
else
|
|
266
|
+
skipped << "RSpec (add rspec to Gemfile)"
|
|
267
|
+
end
|
|
268
|
+
|
|
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?
|
|
274
|
+
|
|
275
|
+
failed = []
|
|
276
|
+
steps.each_with_index do |step, i|
|
|
277
|
+
puts "--- [#{i + 1}/#{steps.length}] #{step[:name]} ---"
|
|
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
|
+
|
|
288
|
+
if success
|
|
289
|
+
puts " ✓ #{step[:name]} passed"
|
|
290
|
+
else
|
|
291
|
+
puts " ✗ #{step[:name]} FAILED"
|
|
292
|
+
failed << step[:name]
|
|
293
|
+
end
|
|
294
|
+
puts ""
|
|
295
|
+
end
|
|
296
|
+
|
|
297
|
+
puts "=" * 50
|
|
298
|
+
if failed.empty?
|
|
299
|
+
puts "=> All #{steps.length} checks passed! ✓"
|
|
300
|
+
exit 0
|
|
301
|
+
else
|
|
302
|
+
puts "=> FAILED: #{failed.join(', ')}"
|
|
303
|
+
exit 1
|
|
304
|
+
end
|
|
305
|
+
end
|
|
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
|
+
|
|
227
362
|
desc "new NAME", "Create a new Whoosh project"
|
|
228
363
|
option :minimal, type: :boolean, default: false
|
|
229
364
|
option :full, type: :boolean, default: false
|
|
@@ -31,6 +31,7 @@ module Whoosh
|
|
|
31
31
|
write(dir, ".rspec", rspec_config)
|
|
32
32
|
write(dir, "Dockerfile", dockerfile)
|
|
33
33
|
write(dir, ".dockerignore", dockerignore)
|
|
34
|
+
write(dir, ".rubocop.yml", rubocop_config)
|
|
34
35
|
write(dir, "README.md", readme(name))
|
|
35
36
|
|
|
36
37
|
# Create empty SQLite DB directory
|
|
@@ -55,6 +56,8 @@ module Whoosh
|
|
|
55
56
|
puts " http://localhost:9292/docs # Swagger UI"
|
|
56
57
|
puts " http://localhost:9292/metrics # Prometheus metrics"
|
|
57
58
|
puts ""
|
|
59
|
+
puts " whoosh ci # run lint + security + tests"
|
|
60
|
+
puts ""
|
|
58
61
|
end
|
|
59
62
|
|
|
60
63
|
class << self
|
|
@@ -145,6 +148,10 @@ module Whoosh
|
|
|
145
148
|
group :development, :test do
|
|
146
149
|
gem "rspec"
|
|
147
150
|
gem "rack-test"
|
|
151
|
+
gem "rubocop", require: false
|
|
152
|
+
gem "brakeman", require: false
|
|
153
|
+
gem "bundler-audit", require: false
|
|
154
|
+
gem "simplecov", require: false
|
|
148
155
|
end
|
|
149
156
|
GEM
|
|
150
157
|
|
|
@@ -251,6 +258,13 @@ module Whoosh
|
|
|
251
258
|
<<~RUBY
|
|
252
259
|
# frozen_string_literal: true
|
|
253
260
|
|
|
261
|
+
require "simplecov"
|
|
262
|
+
SimpleCov.start do
|
|
263
|
+
add_filter "/test/"
|
|
264
|
+
add_filter "/spec/"
|
|
265
|
+
minimum_coverage 80
|
|
266
|
+
end
|
|
267
|
+
|
|
254
268
|
require "whoosh/test"
|
|
255
269
|
require_relative "../app"
|
|
256
270
|
|
|
@@ -349,6 +363,35 @@ module Whoosh
|
|
|
349
363
|
DOCKERFILE
|
|
350
364
|
end
|
|
351
365
|
|
|
366
|
+
def rubocop_config
|
|
367
|
+
<<~YAML
|
|
368
|
+
AllCops:
|
|
369
|
+
TargetRubyVersion: 3.4
|
|
370
|
+
NewCops: enable
|
|
371
|
+
SuggestExtensions: false
|
|
372
|
+
Exclude:
|
|
373
|
+
- db/migrations/**/*
|
|
374
|
+
- vendor/**/*
|
|
375
|
+
|
|
376
|
+
Style/FrozenStringLiteralComment:
|
|
377
|
+
Enabled: true
|
|
378
|
+
|
|
379
|
+
Style/StringLiterals:
|
|
380
|
+
EnforcedStyle: double_quotes
|
|
381
|
+
|
|
382
|
+
Layout/LineLength:
|
|
383
|
+
Max: 120
|
|
384
|
+
|
|
385
|
+
Metrics/MethodLength:
|
|
386
|
+
Max: 25
|
|
387
|
+
|
|
388
|
+
Metrics/BlockLength:
|
|
389
|
+
Exclude:
|
|
390
|
+
- spec/**/*
|
|
391
|
+
- test/**/*
|
|
392
|
+
YAML
|
|
393
|
+
end
|
|
394
|
+
|
|
352
395
|
def dockerignore
|
|
353
396
|
<<~IGNORE
|
|
354
397
|
.git
|
data/lib/whoosh/version.rb
CHANGED