quality_gate 0.2.1 → 0.3.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 (59) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +26 -0
  3. data/README.md +292 -21
  4. data/docs/codex.md +37 -9
  5. data/docs/releasing.md +16 -4
  6. data/lib/generators/quality_gate/install/install_generator.rb +18 -3
  7. data/lib/generators/quality_gate/install/templates/agents_section.md.tt +13 -2
  8. data/lib/generators/quality_gate/install/templates/codex_fast.rb.tt +36 -0
  9. data/lib/generators/quality_gate/install/templates/codex_hooks.json.tt +1 -0
  10. data/lib/generators/quality_gate/install/templates/codex_verify_stop.rb.tt +28 -0
  11. data/lib/generators/quality_gate/install/templates/quality_gate.yml.tt +6 -0
  12. data/lib/generators/quality_gate/install/templates/quality_gate_fast.rb.tt +16 -3
  13. data/lib/generators/quality_gate/install/templates/quality_gate_workflow.yml.tt +28 -0
  14. data/lib/generators/quality_gate/install/templates/rails_quality_gate.yml.tt +45 -0
  15. data/lib/generators/quality_gate/install/templates/ruby_agents_section.md.tt +13 -2
  16. data/lib/generators/quality_gate/install/templates/ruby_quality_gate.yml.tt +5 -0
  17. data/lib/generators/quality_gate/install/templates/simplecov.rb.tt +1 -0
  18. data/lib/quality_gate/adapters/bundler_audit.rb +28 -3
  19. data/lib/quality_gate/adapters/database_consistency.rb +51 -0
  20. data/lib/quality_gate/adapters/database_consistency_report.rb +61 -0
  21. data/lib/quality_gate/adapters/database_consistency_report_item.rb +122 -0
  22. data/lib/quality_gate/adapters/debride.rb +67 -0
  23. data/lib/quality_gate/adapters/debride_report.rb +80 -0
  24. data/lib/quality_gate/adapters/herb.rb +303 -0
  25. data/lib/quality_gate/adapters/reek.rb +21 -2
  26. data/lib/quality_gate/adapters/rubocop.rb +23 -9
  27. data/lib/quality_gate/adapters/rubycritic.rb +74 -0
  28. data/lib/quality_gate/adapters/rubycritic_report.rb +112 -0
  29. data/lib/quality_gate/adapters/test_suite.rb +12 -1
  30. data/lib/quality_gate/baseline.rb +269 -0
  31. data/lib/quality_gate/baseline_run.rb +80 -0
  32. data/lib/quality_gate/cli.rb +129 -6
  33. data/lib/quality_gate/codex_fast_hook.rb +163 -0
  34. data/lib/quality_gate/codex_patch_files.rb +95 -0
  35. data/lib/quality_gate/codex_stop_hook.rb +102 -0
  36. data/lib/quality_gate/config.rb +20 -6
  37. data/lib/quality_gate/database_consistency_runner.rb +124 -0
  38. data/lib/quality_gate/doctor.rb +217 -0
  39. data/lib/quality_gate/doctor_bounded_file.rb +74 -0
  40. data/lib/quality_gate/doctor_command.rb +130 -0
  41. data/lib/quality_gate/doctor_coverage.rb +113 -0
  42. data/lib/quality_gate/doctor_git.rb +121 -0
  43. data/lib/quality_gate/doctor_hooks.rb +99 -0
  44. data/lib/quality_gate/doctor_launchers.rb +300 -0
  45. data/lib/quality_gate/doctor_path_lookup.rb +52 -0
  46. data/lib/quality_gate/doctor_report.rb +38 -0
  47. data/lib/quality_gate/init_command.rb +3 -1
  48. data/lib/quality_gate/installation.rb +72 -96
  49. data/lib/quality_gate/installer.rb +3 -0
  50. data/lib/quality_gate/reporters/doctor.rb +57 -0
  51. data/lib/quality_gate/reporters/json.rb +18 -1
  52. data/lib/quality_gate/reporters/markdown.rb +11 -1
  53. data/lib/quality_gate/reporters/text.rb +14 -6
  54. data/lib/quality_gate/ruby_profile.rb +121 -2
  55. data/lib/quality_gate/runner.rb +15 -17
  56. data/lib/quality_gate/version.rb +1 -1
  57. data/lib/quality_gate.rb +15 -0
  58. data/sig/quality_gate.rbs +117 -2
  59. metadata +44 -1
@@ -23,22 +23,24 @@ module QualityGate
23
23
 
24
24
  # Runs adapters in order and reduces their findings into one immutable result.
25
25
  class Runner
26
- Result = Data.define(:findings, :failed_tools, :checks) do
27
- def initialize(findings:, checks: [])
26
+ Result = Data.define(:findings, :failed_tools, :checks, :baseline) do
27
+ def initialize(findings:, checks: [], baseline: nil)
28
28
  copied_findings = findings.dup.freeze
29
29
  failed_tools = copied_findings.select(&:tool_failure?).map(&:tool).uniq
30
30
  copied_checks = RunnerValueCopy.freeze_copy(checks)
31
+ copied_baseline = RunnerValueCopy.freeze_copy(baseline)
31
32
 
32
33
  super(
33
34
  findings: copied_findings,
34
35
  failed_tools: failed_tools.map { _1.dup.freeze }.freeze,
35
- checks: copied_checks
36
+ checks: copied_checks,
37
+ baseline: copied_baseline
36
38
  )
37
39
  end
38
40
 
39
41
  # Override Data#with so replacements cannot bypass the derived-state invariant.
40
- def with(findings: self.findings, checks: self.checks)
41
- self.class.new(findings: findings, checks: checks)
42
+ def with(findings: self.findings, checks: self.checks, baseline: self.baseline)
43
+ self.class.new(findings: findings, checks: checks, baseline: baseline)
42
44
  end
43
45
 
44
46
  def exit_code
@@ -52,13 +54,17 @@ module QualityGate
52
54
  CHECK_SCOPES = {
53
55
  "rubocop" => "selected_files",
54
56
  "reek" => "selected_files",
57
+ "herb" => "selected_files",
55
58
  "test_suite" => "test_suite",
56
59
  "undercover" => "git_diff",
57
60
  "simplecov" => "coverage_summary",
58
61
  "brakeman" => "project",
59
- "bundler_audit" => "lockfile"
62
+ "bundler_audit" => "lockfile",
63
+ "rubycritic" => "project",
64
+ "debride" => "project",
65
+ "database_consistency" => "project"
60
66
  }.freeze
61
- SELECTION_SUPPORTED_TOOLS = %w[rubocop reek].freeze
67
+ SELECTION_SUPPORTED_TOOLS = %w[rubocop reek herb].freeze
62
68
  private_constant :CHECK_SCOPES, :SELECTION_SUPPORTED_TOOLS
63
69
 
64
70
  def initialize(adapters:, config:, diagnostic_io: nil)
@@ -72,7 +78,7 @@ module QualityGate
72
78
  checks = []
73
79
 
74
80
  adapters.each do |adapter|
75
- tool = adapter_name(adapter)
81
+ tool = adapter.name.to_s
76
82
  write_progress(tool, scope_for(tool))
77
83
  started_at = monotonic_time
78
84
  adapter_findings = adapter.call
@@ -87,24 +93,16 @@ module QualityGate
87
93
 
88
94
  attr_reader :adapters, :config, :diagnostic_io
89
95
 
90
- def adapter_name(adapter)
91
- adapter.name.to_s
92
- end
93
-
94
96
  def check_for(tool, findings, duration_ms)
95
97
  {
96
98
  tool: tool,
97
99
  status: status_for(findings),
98
100
  scope: scope_for(tool),
99
- requested_files: selection_supported?(tool) ? requested_files : [],
101
+ requested_files: SELECTION_SUPPORTED_TOOLS.include?(tool) ? requested_files : [],
100
102
  duration_ms: duration_ms
101
103
  }
102
104
  end
103
105
 
104
- def selection_supported?(tool)
105
- SELECTION_SUPPORTED_TOOLS.include?(tool)
106
- end
107
-
108
106
  def scope_for(tool)
109
107
  scope = CHECK_SCOPES.fetch(tool, "unknown")
110
108
  return "project" if scope == "selected_files" && requested_files.empty?
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module QualityGate
4
- VERSION = "0.2.1"
4
+ VERSION = "0.3.0"
5
5
  end
data/lib/quality_gate.rb CHANGED
@@ -14,15 +14,30 @@ module QualityGate
14
14
  # Your code goes here...
15
15
  end
16
16
 
17
+ require_relative "quality_gate/baseline"
18
+ require_relative "quality_gate/baseline_run"
17
19
  require_relative "quality_gate/adapter"
18
20
  require_relative "quality_gate/adapters/brakeman"
19
21
  require_relative "quality_gate/adapters/bundler_audit"
22
+ require_relative "quality_gate/adapters/database_consistency"
23
+ require_relative "quality_gate/adapters/herb"
20
24
  require_relative "quality_gate/adapters/reek"
21
25
  require_relative "quality_gate/adapters/rubocop"
26
+ require_relative "quality_gate/adapters/rubycritic"
27
+ require_relative "quality_gate/adapters/debride"
22
28
  require_relative "quality_gate/adapters/simplecov"
23
29
  require_relative "quality_gate/adapters/test_suite"
24
30
  require_relative "quality_gate/adapters/undercover"
25
31
  require_relative "quality_gate/config"
26
32
  require_relative "quality_gate/runner"
33
+ require_relative "quality_gate/doctor_report"
34
+ require_relative "quality_gate/reporters/doctor"
35
+ require_relative "quality_gate/doctor_bounded_file"
36
+ require_relative "quality_gate/doctor_launchers"
37
+ require_relative "quality_gate/doctor_git"
38
+ require_relative "quality_gate/doctor_coverage"
39
+ require_relative "quality_gate/doctor_hooks"
40
+ require_relative "quality_gate/doctor"
41
+ require_relative "quality_gate/doctor_command"
27
42
  require_relative "quality_gate/cli"
28
43
  require_relative "quality_gate/railtie" if defined?(Rails::Railtie)
data/sig/quality_gate.rbs CHANGED
@@ -1,4 +1,9 @@
1
1
  module QualityGate
2
+ interface _BaselineMatch
3
+ def findings: () -> Array[Finding]
4
+ def accepted: () -> Array[Finding]
5
+ end
6
+
2
7
  VERSION: String
3
8
 
4
9
  interface _Output
@@ -8,6 +13,22 @@ module QualityGate
8
13
  class Error < StandardError
9
14
  end
10
15
 
16
+ class BaselineError < Error
17
+ end
18
+
19
+ class Baseline
20
+ def self.capture: (gate: String, tools: Array[String], findings: Array[Finding], root: String) -> Baseline
21
+ def self.read: (String path) -> Baseline
22
+ def self.from_h: (Hash[String, untyped] document) -> Baseline
23
+
24
+ def initialize: (gate: String, tools: Array[String], entries: Array[Hash[String, untyped]]) -> void
25
+ def validate_context!: (gate: String, tools: Array[String]) -> Baseline
26
+ def match: (Array[Finding] findings, root: String) -> _BaselineMatch
27
+ def write: (String path, create: bool) -> Integer
28
+ def count: () -> Integer
29
+ def to_h: () -> Hash[String, untyped]
30
+ end
31
+
11
32
  class ParseError < Error
12
33
  attr_reader tool: String
13
34
 
@@ -33,6 +54,7 @@ module QualityGate
33
54
  def create_initializers: () -> void
34
55
  def inject_coverage: () -> void
35
56
  def create_agent_integration: () -> void
57
+ def create_ci_workflow: () -> void
36
58
  def print_summary: () -> void
37
59
  end
38
60
 
@@ -51,6 +73,18 @@ module QualityGate
51
73
  def template_for: (String name) -> String
52
74
  end
53
75
 
76
+ class RailsProfile
77
+ attr_reader destination_root: String
78
+ attr_reader framework: String
79
+ attr_reader test_helper: String
80
+ attr_reader test_command: Array[String]
81
+
82
+ def initialize: (destination_root: String, ?options: Hash[Symbol, untyped]) -> void
83
+ def coverage?: () -> bool
84
+ def missing_default_helper?: () -> bool
85
+ def template_for: (String name) -> String
86
+ end
87
+
54
88
  class Config
55
89
  DEFAULTS: Hash[Symbol, untyped]
56
90
 
@@ -85,6 +119,10 @@ module QualityGate
85
119
  def timeout: () -> Integer
86
120
  end
87
121
 
122
+ class DatabaseConsistencyRunner
123
+ def self.run: (?stdout: _Output, ?stderr: _Output, ?cwd: String) -> (0 | 2)
124
+ end
125
+
88
126
  module Adapters
89
127
  class Brakeman < Adapter
90
128
  CONFIDENCE_MAP: Hash[String, finding_severity]
@@ -108,6 +146,24 @@ module QualityGate
108
146
  def timeout: () -> Integer
109
147
  end
110
148
 
149
+ class DatabaseConsistency < Adapter
150
+ TOOL_NAME: String
151
+ BRIDGE_PATH: String
152
+
153
+ def call: () -> Array[Finding]
154
+ def name: () -> String
155
+ def command: () -> Array[String]
156
+ def parse: (String stdout) -> Array[Finding]
157
+ end
158
+
159
+ class Herb < Adapter
160
+ def initialize: (config: Config, ?files: Array[String], ?diagnostic_io: _Output) -> void
161
+ def call: () -> Array[Finding]
162
+ def name: () -> String
163
+ def command: () -> Array[String]
164
+ def parse: (String stdout) -> Array[Finding]
165
+ end
166
+
111
167
  class RuboCop < Adapter
112
168
  SEVERITY_MAP: Hash[String, finding_severity]
113
169
  CONFIG_PATH: String
@@ -120,6 +176,24 @@ module QualityGate
120
176
  def timeout: () -> Integer
121
177
  end
122
178
 
179
+ class RubyCritic < Adapter
180
+ def initialize: (config: Config, ?files: Array[String], ?diagnostic_io: _Output) -> void
181
+ def call: () -> Array[Finding]
182
+ def name: () -> String
183
+ def command: () -> Array[String]
184
+ def parse: (String json) -> Array[Finding]
185
+ def timeout: () -> Integer
186
+ end
187
+
188
+ class Debride < Adapter
189
+ def initialize: (config: Config, ?files: Array[String], ?diagnostic_io: _Output) -> void
190
+ def call: () -> Array[Finding]
191
+ def name: () -> String
192
+ def command: () -> Array[String]
193
+ def parse: (String json) -> Array[Finding]
194
+ def timeout: () -> Integer
195
+ end
196
+
123
197
  class SimpleCov < Adapter
124
198
  COVERAGE_PATH: String
125
199
 
@@ -162,9 +236,10 @@ module QualityGate
162
236
  attr_reader findings: Array[Finding]
163
237
  attr_reader failed_tools: Array[String]
164
238
  attr_reader checks: Array[Hash[Symbol, untyped]]
239
+ attr_reader baseline: Hash[Symbol, untyped]?
165
240
 
166
- def initialize: (findings: Array[Finding], ?checks: Array[Hash[Symbol, untyped]]) -> void
167
- def with: (?findings: Array[Finding], ?checks: Array[Hash[Symbol, untyped]]) -> Result
241
+ def initialize: (findings: Array[Finding], ?checks: Array[Hash[Symbol, untyped]], ?baseline: Hash[Symbol, untyped]?) -> void
242
+ def with: (?findings: Array[Finding], ?checks: Array[Hash[Symbol, untyped]], ?baseline: Hash[Symbol, untyped]?) -> Result
168
243
  def exit_code: () -> (0 | 1 | 2)
169
244
  end
170
245
  end
@@ -175,6 +250,41 @@ module QualityGate
175
250
  def self.run: (Array[String] argv, ?stdout: _Output, ?stderr: _Output, ?dir: String) -> (0 | 1 | 2)
176
251
  end
177
252
 
253
+ class CodexStopHook
254
+ def initialize: (dir: String) -> void
255
+ def call: (untyped input) -> Hash[String, String]
256
+ end
257
+
258
+ class CodexFastHook
259
+ def initialize: (dir: String) -> void
260
+ def call: (untyped input) -> Hash[String, untyped]
261
+ end
262
+
263
+ class CodexPatchFiles
264
+ def self.parse: (String patch) -> Array[String]
265
+ end
266
+
267
+ class Doctor
268
+ def initialize: (dir: String, registry: Hash[String, untyped]) -> void
269
+ def call: () -> DoctorReport
270
+ end
271
+
272
+ class DoctorReport
273
+ STATUSES: Array[String]
274
+
275
+ attr_reader checks: Array[Hash[String, String]]
276
+ attr_reader summary: Hash[String, Integer]
277
+
278
+ def self.check: (id: String, status: String, message: String) -> Hash[String, String]
279
+ def initialize: (checks: Array[Hash[String, String]]) -> void
280
+ def exit_code: () -> (0 | 1 | 2)
281
+ end
282
+
283
+ class DoctorCommand
284
+ def initialize: (dir: String, registry: Hash[String, untyped]) -> void
285
+ def run: (Array[String] arguments, stdout: _Output, stderr: _Output) -> (0 | 1 | 2)
286
+ end
287
+
178
288
  class InitCommand
179
289
  def self.run: (Array[String] arguments, stdout: _Output, stderr: _Output, dir: String) -> (0 | 1 | 2)
180
290
  end
@@ -194,6 +304,11 @@ module QualityGate
194
304
  def initialize: (io: _Output) -> void
195
305
  def call: (Runner::Result result) -> nil
196
306
  end
307
+
308
+ class Doctor
309
+ def initialize: (io: _Output) -> void
310
+ def call: (DoctorReport report, format: String) -> nil
311
+ end
197
312
  end
198
313
 
199
314
  type finding_severity = :error | :warning | :info
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: quality_gate
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Lucian Ghinda
@@ -51,6 +51,20 @@ dependencies:
51
51
  - - "~>"
52
52
  - !ruby/object:Gem::Version
53
53
  version: 0.9.3
54
+ - !ruby/object:Gem::Dependency
55
+ name: fiddle
56
+ requirement: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '1.1'
61
+ type: :runtime
62
+ prerelease: false
63
+ version_requirements: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: '1.1'
54
68
  - !ruby/object:Gem::Dependency
55
69
  name: reek
56
70
  requirement: !ruby/object:Gem::Requirement
@@ -188,10 +202,15 @@ files:
188
202
  - lib/generators/quality_gate/install/templates/agents_section.md.tt
189
203
  - lib/generators/quality_gate/install/templates/bullet.rb.tt
190
204
  - lib/generators/quality_gate/install/templates/claude_settings.json.tt
205
+ - lib/generators/quality_gate/install/templates/codex_fast.rb.tt
206
+ - lib/generators/quality_gate/install/templates/codex_hooks.json.tt
207
+ - lib/generators/quality_gate/install/templates/codex_verify_stop.rb.tt
191
208
  - lib/generators/quality_gate/install/templates/hook_log_filesystem.rb.tt
192
209
  - lib/generators/quality_gate/install/templates/quality_gate.yml.tt
193
210
  - lib/generators/quality_gate/install/templates/quality_gate_fast.rb.tt
194
211
  - lib/generators/quality_gate/install/templates/quality_gate_verify_stop.rb.tt
212
+ - lib/generators/quality_gate/install/templates/quality_gate_workflow.yml.tt
213
+ - lib/generators/quality_gate/install/templates/rails_quality_gate.yml.tt
195
214
  - lib/generators/quality_gate/install/templates/rubocop.yml.tt
196
215
  - lib/generators/quality_gate/install/templates/ruby_agents_section.md.tt
197
216
  - lib/generators/quality_gate/install/templates/ruby_quality_gate.yml.tt
@@ -203,13 +222,36 @@ files:
203
222
  - lib/quality_gate/adapter.rb
204
223
  - lib/quality_gate/adapters/brakeman.rb
205
224
  - lib/quality_gate/adapters/bundler_audit.rb
225
+ - lib/quality_gate/adapters/database_consistency.rb
226
+ - lib/quality_gate/adapters/database_consistency_report.rb
227
+ - lib/quality_gate/adapters/database_consistency_report_item.rb
228
+ - lib/quality_gate/adapters/debride.rb
229
+ - lib/quality_gate/adapters/debride_report.rb
230
+ - lib/quality_gate/adapters/herb.rb
206
231
  - lib/quality_gate/adapters/reek.rb
207
232
  - lib/quality_gate/adapters/rubocop.rb
233
+ - lib/quality_gate/adapters/rubycritic.rb
234
+ - lib/quality_gate/adapters/rubycritic_report.rb
208
235
  - lib/quality_gate/adapters/simplecov.rb
209
236
  - lib/quality_gate/adapters/test_suite.rb
210
237
  - lib/quality_gate/adapters/undercover.rb
238
+ - lib/quality_gate/baseline.rb
239
+ - lib/quality_gate/baseline_run.rb
211
240
  - lib/quality_gate/cli.rb
241
+ - lib/quality_gate/codex_fast_hook.rb
242
+ - lib/quality_gate/codex_patch_files.rb
243
+ - lib/quality_gate/codex_stop_hook.rb
212
244
  - lib/quality_gate/config.rb
245
+ - lib/quality_gate/database_consistency_runner.rb
246
+ - lib/quality_gate/doctor.rb
247
+ - lib/quality_gate/doctor_bounded_file.rb
248
+ - lib/quality_gate/doctor_command.rb
249
+ - lib/quality_gate/doctor_coverage.rb
250
+ - lib/quality_gate/doctor_git.rb
251
+ - lib/quality_gate/doctor_hooks.rb
252
+ - lib/quality_gate/doctor_launchers.rb
253
+ - lib/quality_gate/doctor_path_lookup.rb
254
+ - lib/quality_gate/doctor_report.rb
213
255
  - lib/quality_gate/exit_code.rb
214
256
  - lib/quality_gate/finding.rb
215
257
  - lib/quality_gate/hook_log.rb
@@ -217,6 +259,7 @@ files:
217
259
  - lib/quality_gate/installation.rb
218
260
  - lib/quality_gate/installer.rb
219
261
  - lib/quality_gate/railtie.rb
262
+ - lib/quality_gate/reporters/doctor.rb
220
263
  - lib/quality_gate/reporters/field_sanitizer.rb
221
264
  - lib/quality_gate/reporters/json.rb
222
265
  - lib/quality_gate/reporters/markdown.rb