branchproof 0.11.1 → 0.12.0

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.
Files changed (75) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +82 -0
  3. data/README.md +440 -29
  4. data/doc/Branchproof/CLI.md +1 -1
  5. data/doc/Branchproof/ChangedCoverage.md +19 -0
  6. data/doc/Branchproof/ChangedCoveragePolicy.md +24 -0
  7. data/doc/Branchproof/ChangedDecisionMap.md +20 -0
  8. data/doc/Branchproof/ChangedScope.md +18 -0
  9. data/doc/Branchproof/Collation.md +23 -0
  10. data/doc/Branchproof/CollationManifest.md +29 -0
  11. data/doc/Branchproof/CoverageSummary.md +20 -0
  12. data/doc/Branchproof/Doctor/TerminalRenderer.md +15 -0
  13. data/doc/Branchproof/Doctor.md +34 -0
  14. data/doc/Branchproof/FallbackInstrumentation.md +8 -0
  15. data/doc/Branchproof/FallbackRuntime.md +12 -0
  16. data/doc/Branchproof/FallbackSyntax.md +13 -0
  17. data/doc/Branchproof/GitChanges.md +19 -0
  18. data/doc/Branchproof/GithubReport.md +25 -0
  19. data/doc/Branchproof/HtmlReport.md +22 -0
  20. data/doc/Branchproof/Instrumenter.md +1 -1
  21. data/doc/Branchproof/MinitestAdapter.md +4 -0
  22. data/doc/Branchproof/RakeTask.md +121 -0
  23. data/doc/Branchproof/Report.md +41 -3
  24. data/doc/Branchproof/ReportSelection.md +10 -1
  25. data/doc/Branchproof/Runtime.md +4 -1
  26. data/doc/Branchproof/Source.md +1 -1
  27. data/doc/Branchproof/SummaryRanking/Decision.md +86 -0
  28. data/doc/Branchproof/SummaryRanking/SourceFile.md +43 -0
  29. data/doc/Branchproof/SummaryRanking.md +36 -0
  30. data/doc/Branchproof/SummaryReport.md +42 -0
  31. data/doc/Branchproof/Worker.md +1 -1
  32. data/doc/Branchproof.md +21 -1
  33. data/doc/CHANGELOG.md +82 -0
  34. data/doc/README.md +440 -29
  35. data/lib/branchproof/analyzer.rb +2 -52
  36. data/lib/branchproof/changed_coverage.rb +177 -0
  37. data/lib/branchproof/changed_decision_map.rb +359 -0
  38. data/lib/branchproof/changed_scope.rb +93 -0
  39. data/lib/branchproof/cli.rb +242 -21
  40. data/lib/branchproof/collation.rb +525 -0
  41. data/lib/branchproof/collation_manifest.rb +129 -0
  42. data/lib/branchproof/comparison.rb +11 -3
  43. data/lib/branchproof/comparison_report.rb +2 -0
  44. data/lib/branchproof/configuration.rb +12 -2
  45. data/lib/branchproof/coverage_policy.rb +122 -0
  46. data/lib/branchproof/coverage_summary.rb +94 -0
  47. data/lib/branchproof/doctor.rb +241 -0
  48. data/lib/branchproof/evidence.rb +5 -1
  49. data/lib/branchproof/exception_instrumentation.rb +4 -3
  50. data/lib/branchproof/exception_syntax.rb +15 -1
  51. data/lib/branchproof/fallback_instrumentation.rb +25 -0
  52. data/lib/branchproof/fallback_runtime.rb +14 -0
  53. data/lib/branchproof/fallback_syntax.rb +90 -0
  54. data/lib/branchproof/focused_report.rb +81 -22
  55. data/lib/branchproof/git_changes.rb +188 -0
  56. data/lib/branchproof/github_report.rb +292 -0
  57. data/lib/branchproof/html_report.rb +412 -0
  58. data/lib/branchproof/instrumenter.rb +2 -0
  59. data/lib/branchproof/minimizer.rb +4 -2
  60. data/lib/branchproof/minitest_adapter.rb +45 -5
  61. data/lib/branchproof/rails_support.rb +2 -2
  62. data/lib/branchproof/rake_task.rb +186 -0
  63. data/lib/branchproof/report.rb +383 -71
  64. data/lib/branchproof/report_selection.rb +26 -5
  65. data/lib/branchproof/runtime.rb +2 -0
  66. data/lib/branchproof/saved_report.rb +323 -5
  67. data/lib/branchproof/source.rb +15 -3
  68. data/lib/branchproof/summary_ranking.rb +165 -0
  69. data/lib/branchproof/summary_report.rb +241 -0
  70. data/lib/branchproof/version.rb +1 -1
  71. data/lib/branchproof/worker.rb +14 -9
  72. data/lib/branchproof.rb +12 -0
  73. data/llms.txt +21 -1
  74. data/sig/branchproof.rbs +206 -5
  75. metadata +39 -23
@@ -0,0 +1,186 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "English"
4
+ require "rake"
5
+ require "rake/tasklib"
6
+ require "rbconfig"
7
+ require_relative "../branchproof"
8
+
9
+ module Branchproof
10
+ # Defines a Rake task that runs `branchproof analyze` in a subprocess.
11
+ #
12
+ # The task shells out to the `branchproof` executable, so the isolated
13
+ # worker model is unchanged. Only options the Rakefile sets are passed as
14
+ # CLI flags. Everything else keeps the CLI default, and `.branchproof.json`
15
+ # precedence stays intact.
16
+ #
17
+ # @example Minitest project
18
+ # require "branchproof/rake_task"
19
+ #
20
+ # Branchproof::RakeTask.new(:branchproof) do |t|
21
+ # t.sources = ["lib/**/*.rb"]
22
+ # t.tests = ["test/**/*_test.rb"]
23
+ # end
24
+ #
25
+ # @example RSpec project with a coverage gate
26
+ # Branchproof::RakeTask.new(:branchproof) do |t|
27
+ # t.sources = ["app/**/*.rb"]
28
+ # t.framework = "rspec"
29
+ # t.minimum = ["mcdc=100"]
30
+ # end
31
+ class RakeTask < ::Rake::TaskLib
32
+ # Option names in the order they appear in the built argv.
33
+ OPTIONS = %i[tests project framework view level missing_only minimum format output].freeze
34
+
35
+ # Options that repeat one flag per array item.
36
+ REPEATED_FLAGS = { tests: "--test", minimum: "--minimum" }.freeze
37
+
38
+ # CLI defaults. The task only passes a flag when the Rakefile sets it.
39
+ DEFAULTS = { sources: [], tests: [], project: "auto", framework: "auto", view: "decisions", level: 3,
40
+ missing_only: false, minimum: [], format: "terminal", output: nil, runner_args: [] }.freeze
41
+
42
+ # @return [Symbol] the Rake task name
43
+ attr_reader :name
44
+
45
+ # @return [String] the task description shown by `rake -T`
46
+ attr_accessor :description
47
+
48
+ # Source globs passed as positional arguments.
49
+ # @return [Array<String>]
50
+ attr_reader :sources
51
+
52
+ # Test globs, one `--test` flag each.
53
+ # @return [Array<String>]
54
+ attr_reader :tests
55
+
56
+ # Project mode: `"auto"`, `"ruby"`, or `"rails"`.
57
+ # @return [String]
58
+ attr_reader :project
59
+
60
+ # Test framework: `"auto"`, `"minitest"`, or `"rspec"`.
61
+ # @return [String]
62
+ attr_reader :framework
63
+
64
+ # Terminal view: `"decisions"`, `"conditions"`, `"tests"`, or `"decision-tables"`.
65
+ # @return [String]
66
+ attr_reader :view
67
+
68
+ # Report level: 1, 2, or 3.
69
+ # @return [Integer]
70
+ attr_reader :level
71
+
72
+ # When true, terminal output lists only missing evidence.
73
+ # @return [Boolean]
74
+ attr_reader :missing_only
75
+
76
+ # Coverage gates as `"criterion=threshold"` strings, one `--minimum` flag each.
77
+ # @return [Array<String>]
78
+ attr_reader :minimum
79
+
80
+ # Output format: `"terminal"` or `"json"`.
81
+ # @return [String]
82
+ attr_reader :format
83
+
84
+ # Output path, or nil to write to stdout.
85
+ # @return [String, nil]
86
+ attr_reader :output
87
+
88
+ # Extra arguments appended after `--` and passed to the test runner.
89
+ # @return [Array<String>]
90
+ attr_reader :runner_args
91
+
92
+ # Defines the task. The block receives the task so a Rakefile can set options.
93
+ #
94
+ # @param name [Symbol, String] the Rake task name
95
+ # @yieldparam task [RakeTask] this task, before it is defined
96
+ def initialize(name = :branchproof)
97
+ super()
98
+ @name = name.to_sym
99
+ @description = "Run branchproof analyze"
100
+ @explicit = {}
101
+ DEFAULTS.each { |key, value| instance_variable_set(:"@#{key}", value.dup) }
102
+ yield self if block_given?
103
+ define
104
+ end
105
+
106
+ OPTIONS.each do |option|
107
+ # Each writer records that the Rakefile set the option, so #argv passes its flag.
108
+ define_method(:"#{option}=") do |value|
109
+ @explicit[option] = true
110
+ instance_variable_set(:"@#{option}", value)
111
+ end
112
+ end
113
+
114
+ # @param value [Array<String>] source globs
115
+ def sources=(value)
116
+ @sources = Array(value)
117
+ end
118
+
119
+ # @param value [Array<String>] runner arguments appended after `--`
120
+ def runner_args=(value)
121
+ @runner_args = Array(value)
122
+ end
123
+
124
+ # Builds the CLI arguments without running anything.
125
+ #
126
+ # @return [Array<String>] arguments for `branchproof analyze`
127
+ def argv
128
+ args = ["analyze", *sources]
129
+ OPTIONS.each do |option|
130
+ value = instance_variable_get(:"@#{option}")
131
+ next unless @explicit[option] || (REPEATED_FLAGS.key?(option) && !Array(value).empty?)
132
+
133
+ args.concat(flags_for(option))
134
+ end
135
+ args.push("--", *runner_args) unless runner_args.empty?
136
+ args.map(&:to_s)
137
+ end
138
+
139
+ # Full command line, starting with the current Ruby and the gem executable.
140
+ #
141
+ # @return [Array<String>]
142
+ def command
143
+ [RbConfig.ruby, executable, *argv]
144
+ end
145
+
146
+ # Path to the `branchproof` executable. Works inside this repository and as an installed gem.
147
+ #
148
+ # @return [String]
149
+ def executable
150
+ local = File.expand_path("../../exe/branchproof", __dir__)
151
+ return local if File.file?(local)
152
+
153
+ Gem.bin_path("branchproof", "branchproof")
154
+ end
155
+
156
+ # Runs the analysis in a subprocess. A non-zero exit status ends the Rake
157
+ # process with the same status so CI fails on coverage gate failures.
158
+ #
159
+ # @return [void]
160
+ def run
161
+ ran = system(*command)
162
+ status = $CHILD_STATUS
163
+ raise Error, "branchproof could not start: #{command.first(2).join(" ")}" if ran.nil? || status.nil?
164
+ return if status.success?
165
+
166
+ warn "branchproof exited with status #{status.exitstatus}"
167
+ exit status.exitstatus
168
+ end
169
+
170
+ private
171
+
172
+ def define
173
+ desc description
174
+ task(name) { run }
175
+ end
176
+
177
+ def flags_for(option)
178
+ value = instance_variable_get(:"@#{option}")
179
+ return Array(value).flat_map { |item| [REPEATED_FLAGS.fetch(option), item] } if REPEATED_FLAGS.key?(option)
180
+ return (value ? ["--missing-only"] : []) if option == :missing_only
181
+ return [] if value.nil?
182
+
183
+ ["--#{option}", value]
184
+ end
185
+ end
186
+ end