ace-lint 0.25.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.
- checksums.yaml +7 -0
- data/.ace-defaults/lint/config.yml +5 -0
- data/.ace-defaults/lint/kramdown.yml +23 -0
- data/.ace-defaults/lint/markdown.yml +16 -0
- data/.ace-defaults/lint/ruby.yml +67 -0
- data/.ace-defaults/lint/skills.yml +138 -0
- data/.ace-defaults/nav/protocols/wfi-sources/ace-lint.yml +11 -0
- data/CHANGELOG.md +584 -0
- data/LICENSE +21 -0
- data/README.md +40 -0
- data/Rakefile +14 -0
- data/exe/ace-lint +14 -0
- data/handbook/skills/as-lint-fix-issue-from/SKILL.md +34 -0
- data/handbook/skills/as-lint-process-report/SKILL.md +29 -0
- data/handbook/skills/as-lint-run/SKILL.md +27 -0
- data/handbook/workflow-instructions/lint/process-report.wf.md +175 -0
- data/handbook/workflow-instructions/lint/run.wf.md +145 -0
- data/lib/ace/lint/atoms/allowed_tools_validator.rb +100 -0
- data/lib/ace/lint/atoms/base_runner.rb +239 -0
- data/lib/ace/lint/atoms/comment_validator.rb +63 -0
- data/lib/ace/lint/atoms/config_locator.rb +162 -0
- data/lib/ace/lint/atoms/frontmatter_extractor.rb +74 -0
- data/lib/ace/lint/atoms/kramdown_parser.rb +81 -0
- data/lib/ace/lint/atoms/pattern_matcher.rb +96 -0
- data/lib/ace/lint/atoms/rubocop_runner.rb +67 -0
- data/lib/ace/lint/atoms/skill_schema_loader.rb +83 -0
- data/lib/ace/lint/atoms/standardrb_runner.rb +45 -0
- data/lib/ace/lint/atoms/type_detector.rb +121 -0
- data/lib/ace/lint/atoms/validator_registry.rb +113 -0
- data/lib/ace/lint/atoms/yaml_parser.rb +11 -0
- data/lib/ace/lint/atoms/yaml_validator.rb +69 -0
- data/lib/ace/lint/cli/commands/lint.rb +318 -0
- data/lib/ace/lint/cli.rb +25 -0
- data/lib/ace/lint/models/lint_result.rb +87 -0
- data/lib/ace/lint/models/validation_error.rb +31 -0
- data/lib/ace/lint/molecules/frontmatter_validator.rb +131 -0
- data/lib/ace/lint/molecules/group_resolver.rb +122 -0
- data/lib/ace/lint/molecules/kramdown_formatter.rb +66 -0
- data/lib/ace/lint/molecules/markdown_linter.rb +249 -0
- data/lib/ace/lint/molecules/offense_deduplicator.rb +65 -0
- data/lib/ace/lint/molecules/ruby_linter.rb +205 -0
- data/lib/ace/lint/molecules/skill_validator.rb +462 -0
- data/lib/ace/lint/molecules/validator_chain.rb +150 -0
- data/lib/ace/lint/molecules/yaml_linter.rb +53 -0
- data/lib/ace/lint/organisms/lint_doctor.rb +289 -0
- data/lib/ace/lint/organisms/lint_orchestrator.rb +294 -0
- data/lib/ace/lint/organisms/report_generator.rb +213 -0
- data/lib/ace/lint/organisms/result_reporter.rb +130 -0
- data/lib/ace/lint/version.rb +7 -0
- data/lib/ace/lint.rb +141 -0
- metadata +248 -0
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../atoms/validator_registry"
|
|
4
|
+
require_relative "../atoms/config_locator"
|
|
5
|
+
require_relative "offense_deduplicator"
|
|
6
|
+
|
|
7
|
+
module Ace
|
|
8
|
+
module Lint
|
|
9
|
+
module Molecules
|
|
10
|
+
# Executes multiple validators sequentially and aggregates results
|
|
11
|
+
# Handles validator availability, fallbacks, and result deduplication
|
|
12
|
+
class ValidatorChain
|
|
13
|
+
attr_reader :validators, :fallback_validators, :warnings
|
|
14
|
+
|
|
15
|
+
# Initialize with validators to run
|
|
16
|
+
# @param validators [Array<Symbol>] Primary validators to run
|
|
17
|
+
# @param fallback_validators [Array<Symbol>] Fallback validators if primary unavailable
|
|
18
|
+
# @param project_root [String, nil] Project root for config lookup
|
|
19
|
+
def initialize(validators, fallback_validators: [], project_root: nil)
|
|
20
|
+
@validators = Array(validators)
|
|
21
|
+
@fallback_validators = Array(fallback_validators)
|
|
22
|
+
@project_root = project_root || Dir.pwd
|
|
23
|
+
@warnings = []
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Run validators on file(s)
|
|
27
|
+
# @param file_paths [Array<String>] Paths to lint
|
|
28
|
+
# @param fix [Boolean] Apply autofix
|
|
29
|
+
# @return [Hash] Aggregated result with :success, :errors, :warnings, :runners
|
|
30
|
+
def run(file_paths, fix: false)
|
|
31
|
+
paths = Array(file_paths)
|
|
32
|
+
return empty_result if paths.empty?
|
|
33
|
+
|
|
34
|
+
# Determine which validators to actually run
|
|
35
|
+
active_validators = resolve_validators
|
|
36
|
+
|
|
37
|
+
if active_validators.empty?
|
|
38
|
+
return unavailable_result
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Run each validator and collect results
|
|
42
|
+
all_results = []
|
|
43
|
+
runners_used = []
|
|
44
|
+
|
|
45
|
+
active_validators.each do |validator_name|
|
|
46
|
+
runner = Atoms::ValidatorRegistry.runner_for(validator_name)
|
|
47
|
+
next unless runner
|
|
48
|
+
|
|
49
|
+
# Look up config for this validator using ConfigLocator
|
|
50
|
+
config = Atoms::ConfigLocator.locate(validator_name, project_root: @project_root)
|
|
51
|
+
config_path = config[:exists] ? config[:path] : nil
|
|
52
|
+
|
|
53
|
+
result = runner.run(paths, fix: fix, config_path: config_path)
|
|
54
|
+
all_results << result
|
|
55
|
+
runners_used << validator_name
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Aggregate and deduplicate results
|
|
59
|
+
aggregate_results(all_results, runners_used)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
private
|
|
63
|
+
|
|
64
|
+
# Resolve which validators to run based on availability
|
|
65
|
+
# @return [Array<Symbol>] Validators to run
|
|
66
|
+
def resolve_validators
|
|
67
|
+
active = []
|
|
68
|
+
|
|
69
|
+
# Check primary validators
|
|
70
|
+
@validators.each do |name|
|
|
71
|
+
if Atoms::ValidatorRegistry.available?(name)
|
|
72
|
+
active << name
|
|
73
|
+
else
|
|
74
|
+
@warnings << "Validator '#{name}' is not available, skipping"
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# If no primary validators available, try fallbacks
|
|
79
|
+
if active.empty?
|
|
80
|
+
@fallback_validators.each do |name|
|
|
81
|
+
if Atoms::ValidatorRegistry.available?(name)
|
|
82
|
+
active << name
|
|
83
|
+
@warnings << "Using fallback validator '#{name}'"
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
active
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# Aggregate results from multiple validators
|
|
92
|
+
# @param results [Array<Hash>] Results from each validator
|
|
93
|
+
# @param runners [Array<Symbol>] Validators that were run
|
|
94
|
+
# @return [Hash] Aggregated result
|
|
95
|
+
def aggregate_results(results, runners)
|
|
96
|
+
return empty_result if results.empty?
|
|
97
|
+
|
|
98
|
+
all_errors = []
|
|
99
|
+
all_warnings = []
|
|
100
|
+
|
|
101
|
+
results.each do |result|
|
|
102
|
+
all_errors.concat(result[:errors] || [])
|
|
103
|
+
all_warnings.concat(result[:warnings] || [])
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
# Deduplicate by line:column:message using OffenseDeduplicator
|
|
107
|
+
deduped_errors = OffenseDeduplicator.deduplicate(all_errors)
|
|
108
|
+
deduped_warnings = OffenseDeduplicator.deduplicate(all_warnings)
|
|
109
|
+
|
|
110
|
+
# Success only if all validators succeeded
|
|
111
|
+
success = results.all? { |r| r[:success] }
|
|
112
|
+
|
|
113
|
+
{
|
|
114
|
+
success: success,
|
|
115
|
+
errors: deduped_errors,
|
|
116
|
+
warnings: deduped_warnings,
|
|
117
|
+
runners: runners,
|
|
118
|
+
chain_warnings: @warnings
|
|
119
|
+
}
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
# Empty result when no files to lint
|
|
123
|
+
# @return [Hash] Empty success result
|
|
124
|
+
def empty_result
|
|
125
|
+
{
|
|
126
|
+
success: true,
|
|
127
|
+
errors: [],
|
|
128
|
+
warnings: [],
|
|
129
|
+
runners: [],
|
|
130
|
+
chain_warnings: @warnings
|
|
131
|
+
}
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# Result when no validators are available
|
|
135
|
+
# @return [Hash] Error result
|
|
136
|
+
def unavailable_result
|
|
137
|
+
{
|
|
138
|
+
success: false,
|
|
139
|
+
errors: [{
|
|
140
|
+
message: "No validators available. Install StandardRB: gem install standardrb"
|
|
141
|
+
}],
|
|
142
|
+
warnings: [],
|
|
143
|
+
runners: [],
|
|
144
|
+
chain_warnings: @warnings
|
|
145
|
+
}
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
end
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../atoms/yaml_validator"
|
|
4
|
+
require_relative "../models/lint_result"
|
|
5
|
+
require_relative "../models/validation_error"
|
|
6
|
+
|
|
7
|
+
module Ace
|
|
8
|
+
module Lint
|
|
9
|
+
module Molecules
|
|
10
|
+
# Validates YAML syntax via Psych
|
|
11
|
+
class YamlLinter
|
|
12
|
+
# Validate YAML file
|
|
13
|
+
# @param file_path [String] Path to YAML file
|
|
14
|
+
# @return [Models::LintResult] Validation result
|
|
15
|
+
def self.lint(file_path)
|
|
16
|
+
content = File.read(file_path)
|
|
17
|
+
lint_content(file_path, content)
|
|
18
|
+
rescue Errno::ENOENT
|
|
19
|
+
Models::LintResult.new(
|
|
20
|
+
file_path: file_path,
|
|
21
|
+
success: false,
|
|
22
|
+
errors: [Models::ValidationError.new(message: "File not found: #{file_path}")]
|
|
23
|
+
)
|
|
24
|
+
rescue => e
|
|
25
|
+
Models::LintResult.new(
|
|
26
|
+
file_path: file_path,
|
|
27
|
+
success: false,
|
|
28
|
+
errors: [Models::ValidationError.new(message: "Error reading file: #{e.message}")]
|
|
29
|
+
)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Validate YAML content
|
|
33
|
+
# @param file_path [String] Path for reference
|
|
34
|
+
# @param content [String] YAML content
|
|
35
|
+
# @return [Models::LintResult] Validation result
|
|
36
|
+
def self.lint_content(file_path, content)
|
|
37
|
+
result = Atoms::YamlValidator.validate(content)
|
|
38
|
+
|
|
39
|
+
errors = result[:errors].map do |msg|
|
|
40
|
+
Models::ValidationError.new(message: msg, severity: :error)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
Models::LintResult.new(
|
|
44
|
+
file_path: file_path,
|
|
45
|
+
success: result[:valid],
|
|
46
|
+
errors: errors,
|
|
47
|
+
warnings: []
|
|
48
|
+
)
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "date"
|
|
4
|
+
require "yaml"
|
|
5
|
+
|
|
6
|
+
require_relative "../atoms/validator_registry"
|
|
7
|
+
require_relative "../atoms/config_locator"
|
|
8
|
+
require_relative "../molecules/group_resolver"
|
|
9
|
+
|
|
10
|
+
module Ace
|
|
11
|
+
module Lint
|
|
12
|
+
module Organisms
|
|
13
|
+
# Diagnostic tool for checking linting configuration health
|
|
14
|
+
# Checks validator availability, config files, and pattern coverage
|
|
15
|
+
class LintDoctor
|
|
16
|
+
# Diagnostic result structure
|
|
17
|
+
DiagnosticResult = Struct.new(:category, :level, :message, :details, keyword_init: true) do
|
|
18
|
+
def error?
|
|
19
|
+
level == :error
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def warning?
|
|
23
|
+
level == :warning
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def info?
|
|
27
|
+
level == :info
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
attr_reader :project_root, :groups, :diagnostics
|
|
32
|
+
|
|
33
|
+
# Initialize doctor with configuration
|
|
34
|
+
# @param project_root [String] Project root directory
|
|
35
|
+
# @param groups [Hash, nil] Ruby validator groups configuration
|
|
36
|
+
def initialize(project_root: Dir.pwd, groups: nil)
|
|
37
|
+
@project_root = project_root
|
|
38
|
+
@groups = groups
|
|
39
|
+
@diagnostics = []
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Run all diagnostic checks
|
|
43
|
+
# @return [Array<DiagnosticResult>] All diagnostic results
|
|
44
|
+
def diagnose
|
|
45
|
+
@diagnostics = []
|
|
46
|
+
|
|
47
|
+
check_validator_availability
|
|
48
|
+
check_config_files
|
|
49
|
+
check_pattern_coverage if @groups
|
|
50
|
+
|
|
51
|
+
@diagnostics
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Check if all configured validators are available
|
|
55
|
+
# @return [Array<DiagnosticResult>] Validator availability diagnostics
|
|
56
|
+
def check_validator_availability
|
|
57
|
+
results = []
|
|
58
|
+
|
|
59
|
+
# Check registered validators
|
|
60
|
+
Atoms::ValidatorRegistry.registered_validators.each do |name|
|
|
61
|
+
results << if Atoms::ValidatorRegistry.available?(name)
|
|
62
|
+
DiagnosticResult.new(
|
|
63
|
+
category: :validator,
|
|
64
|
+
level: :info,
|
|
65
|
+
message: "#{name}: available",
|
|
66
|
+
details: {validator: name, status: :available}
|
|
67
|
+
)
|
|
68
|
+
else
|
|
69
|
+
DiagnosticResult.new(
|
|
70
|
+
category: :validator,
|
|
71
|
+
level: :warning,
|
|
72
|
+
message: "#{name}: not installed",
|
|
73
|
+
details: {validator: name, status: :unavailable}
|
|
74
|
+
)
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# Check configured validators in groups
|
|
79
|
+
if @groups
|
|
80
|
+
configured_validators = collect_configured_validators
|
|
81
|
+
configured_validators.each do |name|
|
|
82
|
+
unless Atoms::ValidatorRegistry.available?(name)
|
|
83
|
+
results << DiagnosticResult.new(
|
|
84
|
+
category: :validator,
|
|
85
|
+
level: :warning,
|
|
86
|
+
message: "Configured validator '#{name}' is not available",
|
|
87
|
+
details: {validator: name, status: :configured_unavailable}
|
|
88
|
+
)
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
@diagnostics.concat(results)
|
|
94
|
+
results
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# Check if configured config files exist
|
|
98
|
+
# @return [Array<DiagnosticResult>] Config file diagnostics
|
|
99
|
+
def check_config_files
|
|
100
|
+
results = []
|
|
101
|
+
|
|
102
|
+
# Check for each validator's config
|
|
103
|
+
Atoms::ValidatorRegistry.registered_validators.each do |name|
|
|
104
|
+
config = Atoms::ConfigLocator.locate(name, project_root: @project_root)
|
|
105
|
+
|
|
106
|
+
case config[:source]
|
|
107
|
+
when :explicit
|
|
108
|
+
if config[:exists]
|
|
109
|
+
results << DiagnosticResult.new(
|
|
110
|
+
category: :config,
|
|
111
|
+
level: :info,
|
|
112
|
+
message: "#{name}: using explicit config at #{config[:path]}",
|
|
113
|
+
details: {validator: name, source: :explicit, path: config[:path]}
|
|
114
|
+
)
|
|
115
|
+
# Validate YAML syntax
|
|
116
|
+
yaml_error = validate_yaml_syntax(config[:path], name)
|
|
117
|
+
results << yaml_error if yaml_error
|
|
118
|
+
else
|
|
119
|
+
results << DiagnosticResult.new(
|
|
120
|
+
category: :config,
|
|
121
|
+
level: :error,
|
|
122
|
+
message: "#{name}: explicit config not found at #{config[:path]}",
|
|
123
|
+
details: {validator: name, source: :explicit, path: config[:path], exists: false}
|
|
124
|
+
)
|
|
125
|
+
end
|
|
126
|
+
when :ace_config
|
|
127
|
+
results << DiagnosticResult.new(
|
|
128
|
+
category: :config,
|
|
129
|
+
level: :info,
|
|
130
|
+
message: "#{name}: using .ace/lint config at #{config[:path]}",
|
|
131
|
+
details: {validator: name, source: :ace_config, path: config[:path]}
|
|
132
|
+
)
|
|
133
|
+
# Validate YAML syntax
|
|
134
|
+
yaml_error = validate_yaml_syntax(config[:path], name)
|
|
135
|
+
results << yaml_error if yaml_error
|
|
136
|
+
when :native
|
|
137
|
+
results << DiagnosticResult.new(
|
|
138
|
+
category: :config,
|
|
139
|
+
level: :info,
|
|
140
|
+
message: "#{name}: using native config at #{config[:path]}",
|
|
141
|
+
details: {validator: name, source: :native, path: config[:path]}
|
|
142
|
+
)
|
|
143
|
+
# Validate YAML syntax
|
|
144
|
+
yaml_error = validate_yaml_syntax(config[:path], name)
|
|
145
|
+
results << yaml_error if yaml_error
|
|
146
|
+
when :gem_defaults
|
|
147
|
+
results << DiagnosticResult.new(
|
|
148
|
+
category: :config,
|
|
149
|
+
level: :info,
|
|
150
|
+
message: "#{name}: using gem default config",
|
|
151
|
+
details: {validator: name, source: :gem_defaults, path: config[:path]}
|
|
152
|
+
)
|
|
153
|
+
when :none
|
|
154
|
+
results << DiagnosticResult.new(
|
|
155
|
+
category: :config,
|
|
156
|
+
level: :info,
|
|
157
|
+
message: "#{name}: using tool defaults (no config file)",
|
|
158
|
+
details: {validator: name, source: :none}
|
|
159
|
+
)
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
@diagnostics.concat(results)
|
|
164
|
+
results
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
# Check pattern coverage in groups configuration
|
|
168
|
+
# @return [Array<DiagnosticResult>] Pattern coverage diagnostics
|
|
169
|
+
def check_pattern_coverage
|
|
170
|
+
return [] unless @groups
|
|
171
|
+
|
|
172
|
+
results = []
|
|
173
|
+
|
|
174
|
+
# Check for default group
|
|
175
|
+
unless @groups.key?(:default) || @groups.key?("default")
|
|
176
|
+
results << DiagnosticResult.new(
|
|
177
|
+
category: :pattern,
|
|
178
|
+
level: :warning,
|
|
179
|
+
message: "No 'default' group defined - some files may not be matched",
|
|
180
|
+
details: {issue: :no_default_group}
|
|
181
|
+
)
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
# Check for overlapping patterns (info only)
|
|
185
|
+
all_patterns = []
|
|
186
|
+
@groups.each do |name, config|
|
|
187
|
+
patterns = config[:patterns] || config["patterns"] || []
|
|
188
|
+
patterns.each do |pattern|
|
|
189
|
+
all_patterns << {group: name, pattern: pattern}
|
|
190
|
+
end
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
# Info about configured groups
|
|
194
|
+
@groups.each do |name, config|
|
|
195
|
+
validators = config[:validators] || config["validators"] || []
|
|
196
|
+
patterns = config[:patterns] || config["patterns"] || []
|
|
197
|
+
|
|
198
|
+
results << DiagnosticResult.new(
|
|
199
|
+
category: :pattern,
|
|
200
|
+
level: :info,
|
|
201
|
+
message: "Group '#{name}': #{validators.join(", ")} for #{patterns.size} pattern(s)",
|
|
202
|
+
details: {group: name, validators: validators, pattern_count: patterns.size}
|
|
203
|
+
)
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
@diagnostics.concat(results)
|
|
207
|
+
results
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
# Check if there are any errors
|
|
211
|
+
# @return [Boolean] True if any errors found
|
|
212
|
+
def errors?
|
|
213
|
+
@diagnostics.any?(&:error?)
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
# Check if there are any warnings
|
|
217
|
+
# @return [Boolean] True if any warnings found
|
|
218
|
+
def warnings?
|
|
219
|
+
@diagnostics.any?(&:warning?)
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
# Get all errors
|
|
223
|
+
# @return [Array<DiagnosticResult>] Error diagnostics
|
|
224
|
+
def errors
|
|
225
|
+
@diagnostics.select(&:error?)
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
# Get all warnings
|
|
229
|
+
# @return [Array<DiagnosticResult>] Warning diagnostics
|
|
230
|
+
def warnings
|
|
231
|
+
@diagnostics.select(&:warning?)
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
private
|
|
235
|
+
|
|
236
|
+
# Collect all validators referenced in groups configuration
|
|
237
|
+
# @return [Array<Symbol>] All configured validator names
|
|
238
|
+
def collect_configured_validators
|
|
239
|
+
return [] unless @groups
|
|
240
|
+
|
|
241
|
+
validators = Set.new
|
|
242
|
+
|
|
243
|
+
@groups.each_value do |config|
|
|
244
|
+
(config[:validators] || config["validators"] || []).each do |v|
|
|
245
|
+
validators << v.to_sym
|
|
246
|
+
end
|
|
247
|
+
(config[:fallback_validators] || config["fallback_validators"] || []).each do |v|
|
|
248
|
+
validators << v.to_sym
|
|
249
|
+
end
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
validators.to_a
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
# Validate YAML syntax of a config file
|
|
256
|
+
# @param path [String] Path to the config file
|
|
257
|
+
# @param validator_name [Symbol, String] Name of the validator
|
|
258
|
+
# @return [DiagnosticResult, nil] Error diagnostic if YAML is invalid, nil otherwise
|
|
259
|
+
def validate_yaml_syntax(path, validator_name)
|
|
260
|
+
return nil unless path && File.exist?(path)
|
|
261
|
+
|
|
262
|
+
YAML.safe_load_file(path, permitted_classes: [Date, Symbol], aliases: true)
|
|
263
|
+
nil
|
|
264
|
+
rescue Psych::SyntaxError => e
|
|
265
|
+
DiagnosticResult.new(
|
|
266
|
+
category: :config,
|
|
267
|
+
level: :error,
|
|
268
|
+
message: "#{validator_name}: YAML syntax error in #{path}: #{e.message}",
|
|
269
|
+
details: {validator: validator_name, path: path, error: e.message, line: e.line, column: e.column}
|
|
270
|
+
)
|
|
271
|
+
rescue Psych::BadAlias => e
|
|
272
|
+
DiagnosticResult.new(
|
|
273
|
+
category: :config,
|
|
274
|
+
level: :error,
|
|
275
|
+
message: "#{validator_name}: YAML alias error in #{path}: #{e.message}",
|
|
276
|
+
details: {validator: validator_name, path: path, error: e.message}
|
|
277
|
+
)
|
|
278
|
+
rescue Errno::ENOENT, Errno::EACCES => e
|
|
279
|
+
DiagnosticResult.new(
|
|
280
|
+
category: :config,
|
|
281
|
+
level: :warning,
|
|
282
|
+
message: "#{validator_name}: Could not read #{path}: #{e.message}",
|
|
283
|
+
details: {validator: validator_name, path: path, error: e.message}
|
|
284
|
+
)
|
|
285
|
+
end
|
|
286
|
+
end
|
|
287
|
+
end
|
|
288
|
+
end
|
|
289
|
+
end
|