Module: RSpecTracer::CLI::Doctor Private
- Defined in:
- lib/rspec_tracer/cli/doctor.rb
Overview
This module is part of a private API. You should avoid using this module if possible, as it may be removed or be changed in the future.
rspec-tracer doctor — diagnose config + environment.
Reports Ruby + rspec-tracer versions, project root resolution,
cache / coverage / report directory state, and SimpleCov / Rails
presence. Exits 0 on healthy diagnosis, 1 if any check fails.
Constant Summary collapse
- REMOTE_CACHE_PROBES =
This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.
When remote_cache is configured, verify the backend is reachable from doctor's vantage point so the user catches misconfig (typo'd S3 path / unreachable Redis URL / unwritable local-fs dir) BEFORE the next CI run fails. Best-effort: never FAIL the gate, just surface a WARN/INFO line.
{ s3: ->(opts) { remote_cache_s3_check(opts) }, local_fs: ->(opts) { remote_cache_local_fs_check(opts) }, redis: ->(opts) { remote_cache_redis_check(opts) } }.freeze
- CI_ENV_VARS =
This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.
Environment variables that signal a CI environment, one per major provider plus the near-universal
CIconvention. %w[CI GITHUB_ACTIONS GITLAB_CI CIRCLECI BUILDKITE TF_BUILD].freeze
Class Method Summary collapse
-
.ar_schema_enabled? ⇒ Boolean
private
Internal helper for the tracer pipeline.
-
.ar_schema_narrow_attribution_check ⇒ Object
private
Surface the narrow-attribution precondition at diagnostic time.
-
.cache_path_check ⇒ Object
private
Internal helper for the tracer pipeline.
-
.cache_schema_version_check ⇒ Object
private
Surface a 1.x->2.0 cache mismatch BEFORE the user runs rspec and watches everything re-run mysteriously.
-
.ci_environment_check ⇒ Object
private
Surface the cache-persistence recipes exactly when the user is on CI, where a non-persisted cache silently degrades every run to a cold run.
-
.coverage_path_check ⇒ Object
private
Internal helper for the tracer pipeline.
-
.git_check ⇒ Object
private
Internal helper for the tracer pipeline.
-
.path_check(label, path) ⇒ Object
private
Internal helper for the tracer pipeline.
-
.print_help(stdout) ⇒ Object
private
Internal helper for the tracer pipeline.
-
.project_root_check ⇒ Object
private
Internal helper for the tracer pipeline.
-
.rails_check ⇒ Object
private
See Doctor.simplecov_check for the doctor-runs-in-its-own- process rationale.
-
.rails_loaded? ⇒ Boolean
private
Internal helper for the tracer pipeline.
-
.remote_cache_check ⇒ Object
private
Internal helper for the tracer pipeline.
-
.remote_cache_entry ⇒ Object
private
Internal helper for the tracer pipeline.
-
.remote_cache_local_fs_check(opts) ⇒ Object
private
Internal helper for the tracer pipeline.
-
.remote_cache_redis_check(opts) ⇒ Object
private
Internal helper for the tracer pipeline.
-
.remote_cache_s3_check(opts) ⇒ Object
private
Internal helper for the tracer pipeline.
-
.report_path_check ⇒ Object
private
Internal helper for the tracer pipeline.
-
.ruby_version_check ⇒ Object
private
Internal helper for the tracer pipeline.
-
.run(args, stdout: $stdout, stderr: $stderr) ⇒ Integer
private
Exit status (0 = healthy, 1 = any check FAILed; warnings keep status 0).
-
.simplecov_check ⇒ Object
private
bundle exec rspec-tracer doctorruns in its own process via the gem'sbin/rspec-tracerbinstub, NOT inside the user's rspec boot — so app code never loads here and a baredefined?(::SimpleCov)check would falsely report "not loaded" on projects that DO have SimpleCov in their Gemfile. -
.tracer_version_check ⇒ Object
private
Internal helper for the tracer pipeline.
-
.transactional_fixtures_default? ⇒ Boolean
private
Internal helper for the tracer pipeline.
Class Method Details
.ar_schema_enabled? ⇒ Boolean
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Internal helper for the tracer pipeline.
281 282 283 284 285 |
# File 'lib/rspec_tracer/cli/doctor.rb', line 281 def self.ar_schema_enabled? return false unless RSpecTracer.respond_to?(:track_ar_schema_notifications?) RSpecTracer.track_ar_schema_notifications? end |
.ar_schema_narrow_attribution_check ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Surface the narrow-attribution precondition at diagnostic time.
When track_ar_schema_notifications is enabled AND Rails is
loaded AND the rspec-rails default `use_transactional_fixtures
true` is in effect, per-example BEGIN/COMMIT fires
sql.active_record inside the rspec-tracer bucket and
attribution silently widens. Same shape as the boot-time warn
in RSpecTracer.start, surfaced here so users running
rspec-tracer doctor see the issue without having to boot a
full rspec run first.
246 247 248 249 250 251 252 253 254 255 256 257 |
# File 'lib/rspec_tracer/cli/doctor.rb', line 246 def self.ar_schema_narrow_attribution_check return 'INFO AR schema: track_ar_schema_notifications not enabled' unless ar_schema_enabled? return 'INFO AR schema: Rails not loaded' unless rails_loaded? if transactional_fixtures_default? 'WARN AR schema: track_ar_schema_notifications + use_transactional_fixtures=true ' \ 'silently widens to whole-suite-on-schema-change. See README ' \ 'section "Narrow AR schema attribution".' else 'OK AR schema: narrow attribution preconditions look good' end end |
.cache_path_check ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Internal helper for the tracer pipeline.
79 80 81 |
# File 'lib/rspec_tracer/cli/doctor.rb', line 79 def self.cache_path_check path_check('cache_path:', RSpecTracer.cache_path) end |
.cache_schema_version_check ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Surface a 1.x->2.0 cache mismatch BEFORE the user runs
rspec and watches everything re-run mysteriously. Reads the
cached last_run.json (if any) and compares its
schema_version against the gem's Schema::CURRENT. Three
outcomes: no cache yet (INFO), match (OK), or mismatch (WARN
with the cold-run note). Never FAIL - schema mismatches are
the documented cold-run path, not a hard error.
151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 |
# File 'lib/rspec_tracer/cli/doctor.rb', line 151 def self.cache_schema_version_check require 'rspec_tracer/storage/schema' require 'json' cache_path = RSpecTracer.cache_path last_run_path = File.join(cache_path.to_s, 'last_run.json') unless File.file?(last_run_path) return 'INFO schema: no cache yet (next rspec run is cold; expected on first install)' end manifest = JSON.parse(File.read(last_run_path, encoding: 'UTF-8')) stored = manifest['schema_version'] current = RSpecTracer::Storage::Schema::CURRENT if stored == current "OK schema: v#{current} (matches gem)" else "WARN schema: stored v#{stored.inspect} != gem v#{current} " \ '(next rspec run is a cold run; expected on 1.x->2.0 upgrade)' end rescue StandardError => e "WARN schema: could not read cache manifest: #{e.class}: #{e.}" end |
.ci_environment_check ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Surface the cache-persistence recipes exactly when the user is
on CI, where a non-persisted cache silently degrades every run
to a cold run. Detection requires a var that is set AND
non-empty: ENV['CI'] == "" is truthy in Ruby (a shell with
CI= exported would otherwise read as detected). INFO both
ways - being in or out of CI is not a health state, so this
check never affects the exit status.
270 271 272 273 274 275 276 277 |
# File 'lib/rspec_tracer/cli/doctor.rb', line 270 def self.ci_environment_check var = CI_ENV_VARS.find { |v| !ENV[v].to_s.empty? } if var "INFO ci: detected via ENV[#{var}] -- cache persistence recipes: docs/CI_RECIPES.md" else 'INFO ci: not detected (local run)' end end |
.coverage_path_check ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Internal helper for the tracer pipeline.
85 86 87 |
# File 'lib/rspec_tracer/cli/doctor.rb', line 85 def self.coverage_path_check path_check('coverage_path:', RSpecTracer.coverage_path) end |
.git_check ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Internal helper for the tracer pipeline.
107 108 109 110 111 112 113 |
# File 'lib/rspec_tracer/cli/doctor.rb', line 107 def self.git_check if system('git', 'rev-parse', 'HEAD', out: File::NULL, err: File::NULL) 'OK git: HEAD reachable (remote_cache will work)' else 'WARN git: not in a git repo (remote_cache will degrade gracefully)' end end |
.path_check(label, path) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Internal helper for the tracer pipeline.
97 98 99 100 101 102 103 |
# File 'lib/rspec_tracer/cli/doctor.rb', line 97 def self.path_check(label, path) return "FAIL #{label} <missing>" if path.nil? || path.empty? return "FAIL #{label} #{path} (does not exist)" unless File.directory?(path) return "FAIL #{label} #{path} (not writable)" unless File.writable?(path) "OK #{label} #{path}" end |
.print_help(stdout) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Internal helper for the tracer pipeline.
49 50 51 52 53 54 55 56 57 |
# File 'lib/rspec_tracer/cli/doctor.rb', line 49 def self.print_help(stdout) stdout.puts <<~HELP Usage: rspec-tracer doctor Diagnose rspec-tracer config and environment. Prints a checklist of versions, paths, and integrations; exits 0 if all checks pass. HELP 0 end |
.project_root_check ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Internal helper for the tracer pipeline.
73 74 75 |
# File 'lib/rspec_tracer/cli/doctor.rb', line 73 def self.project_root_check "OK root: #{RSpecTracer.root}" end |
.rails_check ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
See simplecov_check for the doctor-runs-in-its-own- process rationale. Same three-state probe shape: loaded in this process / installed but not loaded / not installed.
135 136 137 138 139 140 141 142 |
# File 'lib/rspec_tracer/cli/doctor.rb', line 135 def self.rails_check return "OK Rails: #{::Rails::VERSION::STRING}" if defined?(::Rails::VERSION) && !::Rails::VERSION.nil? spec = Gem.loaded_specs['rails'] return "INFO Rails: installed (v#{spec.version}; not loaded in doctor's process)" if spec 'INFO Rails: not installed (this is fine for non-Rails projects)' end |
.rails_loaded? ⇒ Boolean
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Internal helper for the tracer pipeline.
289 290 291 |
# File 'lib/rspec_tracer/cli/doctor.rb', line 289 def self.rails_loaded? defined?(::Rails::VERSION) && !::Rails::VERSION.nil? end |
.remote_cache_check ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Internal helper for the tracer pipeline.
187 188 189 190 191 192 193 194 195 196 |
# File 'lib/rspec_tracer/cli/doctor.rb', line 187 def self.remote_cache_check entry = remote_cache_entry return 'INFO remote_cache: not configured (skip)' unless entry backend, opts = entry probe = REMOTE_CACHE_PROBES[backend] return "INFO remote_cache: custom backend #{backend.inspect} (skipping reachability probe)" if probe.nil? instance_exec(opts, &probe) end |
.remote_cache_entry ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Internal helper for the tracer pipeline.
200 201 202 203 204 |
# File 'lib/rspec_tracer/cli/doctor.rb', line 200 def self.remote_cache_entry return nil unless RSpecTracer.respond_to?(:remote_cache_backend_entry) RSpecTracer.remote_cache_backend_entry end |
.remote_cache_local_fs_check(opts) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Internal helper for the tracer pipeline.
218 219 220 221 222 223 224 225 |
# File 'lib/rspec_tracer/cli/doctor.rb', line 218 def self.remote_cache_local_fs_check(opts) path = opts[:path] || opts['path'] return 'WARN remote_cache: :local_fs configured without :path' if path.nil? || path.empty? return "WARN remote_cache: :local_fs path #{path} does not exist" unless File.directory?(path) return "WARN remote_cache: :local_fs path #{path} not writable" unless File.writable?(path) "OK remote_cache: :local_fs path=#{path}" end |
.remote_cache_redis_check(opts) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Internal helper for the tracer pipeline.
229 230 231 232 233 234 235 |
# File 'lib/rspec_tracer/cli/doctor.rb', line 229 def self.remote_cache_redis_check(opts) url = opts[:url] || opts['url'] || ENV.fetch('RSPEC_TRACER_REMOTE_CACHE_URI', nil) return 'WARN remote_cache: :redis configured without :url' if url.nil? || url.empty? "OK remote_cache: :redis url=#{url} " \ '(reachability not probed locally; verified end-to-end on next CI run)' end |
.remote_cache_s3_check(opts) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Internal helper for the tracer pipeline.
208 209 210 211 212 213 214 |
# File 'lib/rspec_tracer/cli/doctor.rb', line 208 def self.remote_cache_s3_check(opts) bucket = opts[:bucket] || opts['bucket'] return 'WARN remote_cache: :s3 configured without :bucket' if bucket.nil? || bucket.empty? "OK remote_cache: :s3 bucket=#{bucket} " \ '(reachability not probed locally; verified end-to-end on next CI run)' end |
.report_path_check ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Internal helper for the tracer pipeline.
91 92 93 |
# File 'lib/rspec_tracer/cli/doctor.rb', line 91 def self.report_path_check path_check('report_path:', RSpecTracer.report_path) end |
.ruby_version_check ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Internal helper for the tracer pipeline.
61 62 63 |
# File 'lib/rspec_tracer/cli/doctor.rb', line 61 def self.ruby_version_check "OK ruby: #{RUBY_DESCRIPTION}" end |
.run(args, stdout: $stdout, stderr: $stderr) ⇒ Integer
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Returns exit status (0 = healthy, 1 = any check FAILed; warnings keep status 0).
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
# File 'lib/rspec_tracer/cli/doctor.rb', line 17 def self.run(args, stdout: $stdout, stderr: $stderr) return print_help(stdout) if args.include?('-h') || args.include?('--help') checks = [ ruby_version_check, tracer_version_check, project_root_check, cache_path_check, coverage_path_check, report_path_check, git_check, simplecov_check, rails_check, cache_schema_version_check, remote_cache_check, ar_schema_narrow_attribution_check, ci_environment_check ] checks.each { |line| stdout.puts line } ok = checks.none? { |line| line.start_with?('FAIL') } ok ? 0 : 1 rescue Errno::EPIPE # Downstream pipe (`... | head`) closed early -- routine in # shell pipelines, not a failure. Exit 0 silently. 0 rescue StandardError => e stderr.puts "doctor: #{e.class}: #{e.}" 1 end |
.simplecov_check ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
bundle exec rspec-tracer doctor runs in its own process via
the gem's bin/rspec-tracer binstub, NOT inside the user's
rspec boot — so app code never loads here and a bare
defined?(::SimpleCov) check would falsely report "not
loaded" on projects that DO have SimpleCov in their
Gemfile. Probe Gem.loaded_specs first to surface the
"installed but not loaded in doctor's process" case
separately from "actually not installed."
123 124 125 126 127 128 129 130 |
# File 'lib/rspec_tracer/cli/doctor.rb', line 123 def self.simplecov_check return 'OK SimpleCov: loaded (interop active)' if defined?(::SimpleCov) spec = Gem.loaded_specs['simplecov'] return "INFO SimpleCov: installed (v#{spec.version}; not loaded in doctor's process)" if spec 'INFO SimpleCov: not installed (this is fine; SimpleCov is optional)' end |
.tracer_version_check ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Internal helper for the tracer pipeline.
67 68 69 |
# File 'lib/rspec_tracer/cli/doctor.rb', line 67 def self.tracer_version_check "OK rspec-tracer: #{RSpecTracer::VERSION}" end |
.transactional_fixtures_default? ⇒ Boolean
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Internal helper for the tracer pipeline.
295 296 297 298 299 300 301 302 303 304 |
# File 'lib/rspec_tracer/cli/doctor.rb', line 295 def self.transactional_fixtures_default? return false unless defined?(::RSpec) && ::RSpec.respond_to?(:configuration) cfg = ::RSpec.configuration return false unless cfg.respond_to?(:use_transactional_fixtures) cfg.use_transactional_fixtures != false rescue StandardError false end |