Module: BundleSafeUpdate::CLI::Output

Included in:
BundleSafeUpdate::CLI
Defined in:
lib/bundle_safe_update/cli/output.rb

Instance Method Summary collapse

Instance Method Details

#blocked_reason(result, config) ⇒ Object



55
56
57
58
# File 'lib/bundle_safe_update/cli/output.rb', line 55

def blocked_reason(result, config)
  age_info = result.age_days ? "published #{result.age_days} days ago" : result.reason
  "#{age_info} (< #{config.cooldown_days} required)"
end

#build_json_output(results, blocked, config) ⇒ Object



33
34
35
36
37
38
39
40
# File 'lib/bundle_safe_update/cli/output.rb', line 33

def build_json_output(results, blocked, config)
  {
    ok: blocked.empty?,
    cooldown_days: config.cooldown_days,
    checked: results.length,
    blocked: blocked.map { |r| { name: r.name, version: r.version, age_days: r.age_days } }
  }
end

#config_lines(config) ⇒ Object



11
12
13
# File 'lib/bundle_safe_update/cli/output.rb', line 11

def config_lines(config)
  config_values(config).map { |label, value| "  #{label}: #{value}" }
end

#config_values(config) ⇒ Object



15
16
17
18
19
20
21
22
23
# File 'lib/bundle_safe_update/cli/output.rb', line 15

def config_values(config)
  { 'Cooldown days' => config.cooldown_days, 'Ignored gems' => format_list(config.ignore_gems),
    'Ignored prefixes' => format_list(config.ignore_prefixes),
    'Trusted sources' => format_list(config.trusted_sources),
    'Trusted owners' => format_list(config.trusted_owners), 'Max threads' => config.max_threads,
    'Audit' => config.audit, 'Update' => config.update, 'Lock only' => config.lock_only,
    'Warn only' => config.warn_only,
    'Verbose' => config.verbose }
end

#dry_run_output(config) ⇒ Object



6
7
8
9
# File 'lib/bundle_safe_update/cli/output.rb', line 6

def dry_run_output(config)
  puts('Configuration (dry-run):')
  config_lines(config).each { |line| puts(line) }
end

#format_list(items) ⇒ Object



25
26
27
# File 'lib/bundle_safe_update/cli/output.rb', line 25

def format_list(items)
  items.empty? ? '(none)' : items.join(', ')
end

#output_audit_result(result) ⇒ Object



69
70
71
72
73
74
# File 'lib/bundle_safe_update/cli/output.rb', line 69

def output_audit_result(result)
  return print_audit_unavailable unless result.available
  return print_audit_error(result.error) if result.error

  print_audit_results(result.vulnerabilities)
end

#output_human(results, blocked, config) ⇒ Object



42
43
44
45
# File 'lib/bundle_safe_update/cli/output.rb', line 42

def output_human(results, blocked, config)
  results.each { |result| print_result(result, config) }
  print_summary(blocked)
end

#output_json(results, blocked, config) ⇒ Object



29
30
31
# File 'lib/bundle_safe_update/cli/output.rb', line 29

def output_json(results, blocked, config)
  puts(JSON.pretty_generate(build_json_output(results, blocked, config)))
end

#output_risk_results(risk_results, options) ⇒ Object



102
103
104
105
106
107
108
109
# File 'lib/bundle_safe_update/cli/output.rb', line 102

def output_risk_results(risk_results, options)
  return if risk_results.empty? || options[:json]

  puts
  puts(cyan('Risk signals:'))
  risk_results.each { |result| print_risk_result(result) }
  print_risk_summary(risk_results)
end


80
81
82
# File 'lib/bundle_safe_update/cli/output.rb', line 80

def print_audit_error(error)
  warn(red("Audit error: #{error}"))
end


84
85
86
87
88
89
90
91
92
93
94
95
# File 'lib/bundle_safe_update/cli/output.rb', line 84

def print_audit_results(vulnerabilities)
  puts
  puts(cyan('Checking for vulnerabilities...'))

  if vulnerabilities.empty?
    puts(green('No vulnerabilities found.'))
  else
    vulnerabilities.each { |v| print_vulnerability(v) }
    puts
    puts(yellow("#{vulnerabilities.length} vulnerability(ies) found"))
  end
end


76
77
78
# File 'lib/bundle_safe_update/cli/output.rb', line 76

def print_audit_unavailable
  warn(yellow('Warning: bundler-audit not installed. Run: gem install bundler-audit'))
end


47
48
49
50
51
52
53
# File 'lib/bundle_safe_update/cli/output.rb', line 47

def print_result(result, config)
  if result.allowed
    puts(green("OK: #{result.name} (#{result.version}) - #{result.reason}"))
  else
    puts(yellow("BLOCKED: #{result.name} (#{result.version}) - #{blocked_reason(result, config)}"))
  end
end


111
112
113
# File 'lib/bundle_safe_update/cli/output.rb', line 111

def print_risk_result(result)
  result.signals.each { |signal| print_risk_signal(result, signal) }
end


115
116
117
118
# File 'lib/bundle_safe_update/cli/output.rb', line 115

def print_risk_signal(result, signal)
  prefix = signal.mode == 'block' ? red('BLOCKED') : yellow('WARNING')
  puts("#{prefix}: #{result.gem_name} (#{result.version}) - #{signal.message}")
end


120
121
122
123
124
125
126
127
# File 'lib/bundle_safe_update/cli/output.rb', line 120

def print_risk_summary(risk_results)
  warnings = risk_results.sum { |r| r.signals.count { |s| s.mode == 'warn' } }
  blocks = risk_results.count(&:blocked)

  puts
  puts(yellow("#{blocks} gem(s) blocked by risk signals")) if blocks.positive?
  puts(yellow("#{warnings} risk warning(s)")) if warnings.positive?
end


144
145
146
147
148
149
150
151
152
# File 'lib/bundle_safe_update/cli/output.rb', line 144

def print_skipped(blocked, risk_blocked_names)
  total_skipped = blocked.length + risk_blocked_names.length
  return if total_skipped.zero?

  cooldown_names = blocked.map(&:name)
  all_skipped = (cooldown_names + risk_blocked_names).uniq.join(', ')
  puts
  puts(yellow("Skipped #{total_skipped} blocked gem(s): #{all_skipped}"))
end


60
61
62
63
64
65
66
67
# File 'lib/bundle_safe_update/cli/output.rb', line 60

def print_summary(blocked)
  puts
  if blocked.empty?
    puts(green('All gem versions satisfy minimum age requirements.'))
  else
    puts(yellow("#{blocked.length} gem(s) violate minimum release age"))
  end
end


140
141
142
# File 'lib/bundle_safe_update/cli/output.rb', line 140

def print_update_result(success)
  puts(success ? green('Bundle updated successfully.') : red('Bundle update failed.'))
end


129
130
131
132
133
134
135
136
137
138
# File 'lib/bundle_safe_update/cli/output.rb', line 129

def print_update_start(gem_names, lock_only: false)
  puts
  if lock_only
    puts(cyan("Updating lock file for #{gem_names.length} gem(s): #{gem_names.join(', ')}"))
    puts(cyan("Running: bundle lock --conservative --update #{gem_names.join(' ')}"))
  else
    puts(cyan("Updating #{gem_names.length} gem(s): #{gem_names.join(', ')}"))
    puts(cyan("Running: bundle update --conservative #{gem_names.join(' ')}"))
  end
end


97
98
99
100
# File 'lib/bundle_safe_update/cli/output.rb', line 97

def print_vulnerability(vuln)
  puts(red("VULNERABLE: #{vuln.gem_name} (#{vuln.cve}) - #{vuln.title}"))
  puts("  Solution: #{vuln.solution}") if vuln.solution
end