Class: Pinspec::CLI

Inherits:
Thor
  • Object
show all
Defined in:
lib/pinspec/cli.rb

Constant Summary collapse

ORDER =
%w[pin verify init analyze validate report plan capture version].freeze

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.exit_on_failure?Boolean

Returns:

  • (Boolean)


10
11
12
# File 'lib/pinspec/cli.rb', line 10

def self.exit_on_failure?
  true
end

.sort_commands!(list) ⇒ Object

Thor sorts the command list alphabetically after building it, which buries the one verb most people want between init and plan, and gives the two diagnostics equal billing with the product.



19
20
21
22
23
24
# File 'lib/pinspec/cli.rb', line 19

def self.sort_commands!(list)
  list.sort_by! do |usage, _|
    name = usage.to_s.split(/\s+/)[1].to_s
    [ORDER.index(name) || ORDER.size, name]
  end
end

Instance Method Details

#analyze(app_path = ".") ⇒ Object



126
127
128
129
130
131
132
133
134
135
136
137
138
# File 'lib/pinspec/cli.rb', line 126

def analyze(app_path = ".")
  guarded do
    Config.load(app_path)
    profile = Analyzer::AppProfileReader.read(app_path)

    print_app(profile)
    puts
    print_schema(profile.schema)
    puts
    print_factories(profile.factories)
    print_warnings(profile)
  end
end

#capture(*args) ⇒ Object



155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
# File 'lib/pinspec/cli.rb', line 155

def capture(*args)
  guarded do
    target = target_from(args)
    file, method = resolve_target(target)

    result = Runner::Capture.new(
      app_root:    options[:app],
      target:      file,
      method:      method,
      max_cases:   setting('cases', Inputs::Corpus::DEFAULT_MAX_CASES),
      boots:       setting('boots', 2),
      compare_sql: setting('compare-sql', false),
      sandbox_env: app_env,
      sample:      setting('sample', false),
      redact:      !options[:"no-redact"]
    ).run

    print_capture(result)

    raise NothingStableToPin, nothing_stable_message(result.stability) if result.stability.nothing_to_pin?
  end
end

#init(app_path = ".") ⇒ Object



61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
# File 'lib/pinspec/cli.rb', line 61

def init(app_path = ".")
  guarded do
    path = File.join(app_path, Config::FILENAME)

    if File.file?(path) && !options[:force]
      raise ConfigInvalid, "#{path} already exists. Pass --force to overwrite it."
    end

    runtime = Runner::Runtime.for(app_path)
    profile = Analyzer::AppProfileReader.read(app_path)

    # Deliberately NOT the detected environment. Writing the resolved PATH into a
    # committed file bakes in one machine's layout and goes stale the moment the
    # app changes Ruby - and detection re-runs on every invocation anyway. `env`
    # is for what pinspec cannot work out: database credentials, feature flags.
    File.write(path, Config.new(app_path, {
                                  "cases" => Inputs::Corpus::DEFAULT_MAX_CASES,
                                  "boots" => 2
                                }, path).to_yaml_document)

    row "wrote", path
    row "rails", profile.rails_version || "unknown"
    row "ruby", runtime.ruby_version
    row "runtime", runtime.detected? ? "#{runtime.manager}, detected on each run" : "this shell's Ruby"
    puts

    if runtime.note
      puts runtime.note
      puts
    end

    puts "Now: pinspec pin app/services/your_service.rb"
  end
end

#pin(*args) ⇒ Object



195
196
197
198
199
200
201
202
203
204
205
# File 'lib/pinspec/cli.rb', line 195

def pin(*args)
  guarded do
    target = target_from(args)
    warn_about_retired_flags!
    warn_about_redaction!

    return pin_directory(target) if File.directory?(target)

    pin_one(target)
  end
end

#plan(target) ⇒ Object



102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
# File 'lib/pinspec/cli.rb', line 102

def plan(target)
  guarded do
    file, method = resolve_target(target)
    target_profile = Analyzer::TargetParser.parse(file, method)

    print_profile(target_profile)
    puts

    app_profile = Analyzer::AppProfileReader.read(options[:app])
    setup_plan  = Setup::ContextBuilder.build(target: target_profile, profile: app_profile)
    corpus     = Inputs::Corpus.build(
      target:    target_profile,
      plan:      setup_plan,
      schema:    app_profile.schema,
      max_cases: setting('cases', Inputs::Corpus::DEFAULT_MAX_CASES)
    )

    print_plan(setup_plan)
    puts
    print_corpus(corpus)
  end
end

#reportObject



334
335
336
337
338
339
340
341
342
343
344
345
346
# File 'lib/pinspec/cli.rb', line 334

def report
  guarded do
    path = File.join(options[:app], Report::Summary::OUTPUT)

    unless File.file?(path)
      raise TargetNotFound,
            "no report at #{path}. Run `pinspec pin` first (or `pinspec validate`, " \
            "which adds the mutation scores); either writes one every time."
    end

    puts Analyzer::Source.read(path)
  end
end

#validate(*args) ⇒ Object



298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
# File 'lib/pinspec/cli.rb', line 298

def validate(*args)
  guarded do
    target = target_from(args)
    file, method = resolve_target(target)

    capture = Runner::Capture.new(
      app_root: options[:app], target: file, method: method,
      max_cases: setting('cases', Inputs::Corpus::DEFAULT_MAX_CASES), sandbox_env: app_env
    ).run

    raise NothingStableToPin, nothing_stable_message(capture.stability) if capture.stability.nothing_to_pin?

    profile = Analyzer::AppProfileReader.read(options[:app])

    report = Validate::PinScorer.new(
      app_root: options[:app], target: capture.target, plan: capture.plan,
      corpus: capture.corpus, stability: capture.stability,
      fk_map: profile.schema.fk_map,
      env: app_env, test_command: setting("test-command", nil)
    ).run

    print_scores(report)

    summary = Report::Summary.new(
      app_root: options[:app], profile: profile, target: capture.target,
      plan: capture.plan, corpus: capture.corpus, stability: capture.stability,
      scores: report
    )

    puts
    puts "  report         #{summary.write!}"
  end
end

#verify(*args) ⇒ Object



38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# File 'lib/pinspec/cli.rb', line 38

def verify(*args)
  guarded do
    spec_file = target_from(args)
    path = File.expand_path(spec_file, options[:app])

    unless File.file?(path)
      raise TargetNotFound, "no spec file at #{path}"
    end

    outcomes = Verify::Verifier.new(
      app_root: options[:app], spec_path: path, env: app_env,
      level: setting("verify-level", "full").to_sym
    ).verify

    puts "verify   #{spec_file}"
    print_verification(outcomes)

    raise VerifyFailed, verify_failed_message(outcomes) unless outcomes.all?(&:green?)
  end
end

#versionObject



27
28
29
30
# File 'lib/pinspec/cli.rb', line 27

def version
  puts "pinspec #{Pinspec::VERSION} " \
       "(probe v#{Pinspec::PROBE_VERSION}, serializer v#{Pinspec::SERIALIZER_VERSION})"
end