yamlint 0.1.0 → 1.0.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 +4 -4
- data/.rubocop.yml +31 -9
- data/.serena/.gitignore +1 -0
- data/.serena/memories/project_overview.md +25 -0
- data/.serena/memories/style_and_conventions.md +7 -0
- data/.serena/memories/suggested_commands.md +17 -0
- data/.serena/memories/task_completion.md +5 -0
- data/.serena/project.yml +89 -0
- data/CHANGELOG.md +6 -2
- data/Gemfile +9 -7
- data/LICENSE.txt +1 -1
- data/README.md +155 -17
- data/Rakefile +10 -3
- data/docs/Yamlint/Cli.html +403 -0
- data/docs/Yamlint/Config.html +1034 -0
- data/docs/Yamlint/Formatter.html +433 -0
- data/docs/Yamlint/Linter.html +423 -0
- data/docs/Yamlint/Models/LintContext.html +993 -0
- data/docs/Yamlint/Models/Problem.html +951 -0
- data/docs/Yamlint/Models/Token.html +713 -0
- data/docs/Yamlint/Models.html +117 -0
- data/docs/Yamlint/Output/Base.html +290 -0
- data/docs/Yamlint/Output/Colored.html +293 -0
- data/docs/Yamlint/Output/Github.html +260 -0
- data/docs/Yamlint/Output/Parsable.html +258 -0
- data/docs/Yamlint/Output/Standard.html +270 -0
- data/docs/Yamlint/Output.html +208 -0
- data/docs/Yamlint/Parser/Comment.html +795 -0
- data/docs/Yamlint/Parser/CommentExtractor.html +287 -0
- data/docs/Yamlint/Parser/LineParser.html +423 -0
- data/docs/Yamlint/Parser/TokenParser/Handler.html +910 -0
- data/docs/Yamlint/Parser/TokenParser.html +291 -0
- data/docs/Yamlint/Parser.html +117 -0
- data/docs/Yamlint/Presets.html +266 -0
- data/docs/Yamlint/Rules/Anchors/AnchorHandler.html +678 -0
- data/docs/Yamlint/Rules/Anchors.html +314 -0
- data/docs/Yamlint/Rules/Base.html +968 -0
- data/docs/Yamlint/Rules/Braces.html +335 -0
- data/docs/Yamlint/Rules/Brackets.html +335 -0
- data/docs/Yamlint/Rules/Colons.html +313 -0
- data/docs/Yamlint/Rules/Commas.html +313 -0
- data/docs/Yamlint/Rules/CommentRule.html +298 -0
- data/docs/Yamlint/Rules/Comments.html +317 -0
- data/docs/Yamlint/Rules/CommentsIndentation.html +328 -0
- data/docs/Yamlint/Rules/DocumentEnd.html +332 -0
- data/docs/Yamlint/Rules/DocumentStart.html +332 -0
- data/docs/Yamlint/Rules/EmptyLines.html +300 -0
- data/docs/Yamlint/Rules/EmptyValues.html +273 -0
- data/docs/Yamlint/Rules/FloatValues.html +383 -0
- data/docs/Yamlint/Rules/Hyphens.html +337 -0
- data/docs/Yamlint/Rules/Indentation.html +329 -0
- data/docs/Yamlint/Rules/KeyDuplicates/DuplicateKeyHandler.html +716 -0
- data/docs/Yamlint/Rules/KeyDuplicates.html +258 -0
- data/docs/Yamlint/Rules/KeyOrdering/KeyOrderHandler.html +694 -0
- data/docs/Yamlint/Rules/KeyOrdering.html +258 -0
- data/docs/Yamlint/Rules/LineLength.html +251 -0
- data/docs/Yamlint/Rules/LineRule.html +304 -0
- data/docs/Yamlint/Rules/NewLineAtEndOfFile.html +308 -0
- data/docs/Yamlint/Rules/NewLines.html +310 -0
- data/docs/Yamlint/Rules/OctalValues.html +270 -0
- data/docs/Yamlint/Rules/QuotedStrings.html +381 -0
- data/docs/Yamlint/Rules/Registry.html +492 -0
- data/docs/Yamlint/Rules/TokenRule.html +298 -0
- data/docs/Yamlint/Rules/TrailingSpaces.html +321 -0
- data/docs/Yamlint/Rules/Truthy.html +288 -0
- data/docs/Yamlint/Rules.html +190 -0
- data/docs/Yamlint/Runner.html +551 -0
- data/docs/Yamlint.html +135 -0
- data/docs/_index.html +643 -0
- data/docs/class_list.html +54 -0
- data/docs/css/common.css +1 -0
- data/docs/css/full_list.css +58 -0
- data/docs/css/style.css +490 -0
- data/docs/file.README.html +276 -0
- data/docs/file_list.html +59 -0
- data/docs/frames.html +22 -0
- data/docs/index.html +276 -0
- data/docs/js/app.js +395 -0
- data/docs/js/full_list.js +244 -0
- data/docs/js/jquery.js +4 -0
- data/docs/method_list.html +1582 -0
- data/docs/top-level-namespace.html +110 -0
- data/exe/yamlint +7 -0
- data/lib/yamlint/cli.rb +162 -0
- data/lib/yamlint/config.rb +113 -0
- data/lib/yamlint/formatter.rb +140 -0
- data/lib/yamlint/linter.rb +130 -0
- data/lib/yamlint/models/lint_context.rb +43 -0
- data/lib/yamlint/models/problem.rb +44 -0
- data/lib/yamlint/models/token.rb +22 -0
- data/lib/yamlint/models.rb +9 -0
- data/lib/yamlint/output/base.rb +15 -0
- data/lib/yamlint/output/colored.rb +54 -0
- data/lib/yamlint/output/github.rb +20 -0
- data/lib/yamlint/output/parsable.rb +19 -0
- data/lib/yamlint/output/standard.rb +25 -0
- data/lib/yamlint/output.rb +26 -0
- data/lib/yamlint/parser/comment_extractor.rb +78 -0
- data/lib/yamlint/parser/line_parser.rb +30 -0
- data/lib/yamlint/parser/token_parser.rb +92 -0
- data/lib/yamlint/parser.rb +10 -0
- data/lib/yamlint/presets/default.rb +35 -0
- data/lib/yamlint/presets/relaxed.rb +34 -0
- data/lib/yamlint/presets.rb +19 -0
- data/lib/yamlint/rules/anchors.rb +133 -0
- data/lib/yamlint/rules/base.rb +102 -0
- data/lib/yamlint/rules/braces.rb +105 -0
- data/lib/yamlint/rules/brackets.rb +105 -0
- data/lib/yamlint/rules/colons.rb +73 -0
- data/lib/yamlint/rules/commas.rb +85 -0
- data/lib/yamlint/rules/comments.rb +77 -0
- data/lib/yamlint/rules/comments_indentation.rb +66 -0
- data/lib/yamlint/rules/document_end.rb +45 -0
- data/lib/yamlint/rules/document_start.rb +45 -0
- data/lib/yamlint/rules/empty_lines.rb +107 -0
- data/lib/yamlint/rules/empty_values.rb +43 -0
- data/lib/yamlint/rules/float_values.rb +67 -0
- data/lib/yamlint/rules/hyphens.rb +42 -0
- data/lib/yamlint/rules/indentation.rb +39 -0
- data/lib/yamlint/rules/key_duplicates.rb +127 -0
- data/lib/yamlint/rules/key_ordering.rb +126 -0
- data/lib/yamlint/rules/line_length.rb +57 -0
- data/lib/yamlint/rules/new_line_at_end_of_file.rb +31 -0
- data/lib/yamlint/rules/new_lines.rb +59 -0
- data/lib/yamlint/rules/octal_values.rb +62 -0
- data/lib/yamlint/rules/quoted_strings.rb +73 -0
- data/lib/yamlint/rules/registry.rb +48 -0
- data/lib/yamlint/rules/trailing_spaces.rb +31 -0
- data/lib/yamlint/rules/truthy.rb +48 -0
- data/lib/yamlint/rules.rb +41 -0
- data/lib/yamlint/runner.rb +80 -0
- data/lib/yamlint/version.rb +1 -1
- data/lib/yamlint.rb +11 -3
- metadata +131 -11
- data/CODE_OF_CONDUCT.md +0 -84
- data/sig/yamlint.rbs +0 -4
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html>
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>
|
|
7
|
+
Top Level Namespace
|
|
8
|
+
|
|
9
|
+
— Documentation by YARD 0.9.38
|
|
10
|
+
|
|
11
|
+
</title>
|
|
12
|
+
|
|
13
|
+
<link rel="stylesheet" href="css/style.css" type="text/css" />
|
|
14
|
+
|
|
15
|
+
<link rel="stylesheet" href="css/common.css" type="text/css" />
|
|
16
|
+
|
|
17
|
+
<script type="text/javascript">
|
|
18
|
+
pathId = "";
|
|
19
|
+
relpath = '';
|
|
20
|
+
</script>
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
<script type="text/javascript" charset="utf-8" src="js/jquery.js"></script>
|
|
24
|
+
|
|
25
|
+
<script type="text/javascript" charset="utf-8" src="js/app.js"></script>
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
</head>
|
|
29
|
+
<body>
|
|
30
|
+
<div class="nav_wrap">
|
|
31
|
+
<iframe id="nav" src="class_list.html?1"></iframe>
|
|
32
|
+
<div id="resizer"></div>
|
|
33
|
+
</div>
|
|
34
|
+
|
|
35
|
+
<div id="main" tabindex="-1">
|
|
36
|
+
<div id="header">
|
|
37
|
+
<div id="menu">
|
|
38
|
+
|
|
39
|
+
<a href="_index.html">Index</a> »
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
<span class="title">Top Level Namespace</span>
|
|
43
|
+
|
|
44
|
+
</div>
|
|
45
|
+
|
|
46
|
+
<div id="search">
|
|
47
|
+
|
|
48
|
+
<a class="full_list_link" id="class_list_link"
|
|
49
|
+
href="class_list.html">
|
|
50
|
+
|
|
51
|
+
<svg width="24" height="24">
|
|
52
|
+
<rect x="0" y="4" width="24" height="4" rx="1" ry="1"></rect>
|
|
53
|
+
<rect x="0" y="12" width="24" height="4" rx="1" ry="1"></rect>
|
|
54
|
+
<rect x="0" y="20" width="24" height="4" rx="1" ry="1"></rect>
|
|
55
|
+
</svg>
|
|
56
|
+
</a>
|
|
57
|
+
|
|
58
|
+
</div>
|
|
59
|
+
<div class="clear"></div>
|
|
60
|
+
</div>
|
|
61
|
+
|
|
62
|
+
<div id="content"><h1>Top Level Namespace
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
</h1>
|
|
67
|
+
<div class="box_info">
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
</div>
|
|
80
|
+
|
|
81
|
+
<h2>Defined Under Namespace</h2>
|
|
82
|
+
<p class="children">
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
<strong class="modules">Modules:</strong> <span class='object_link'><a href="Yamlint.html" title="Yamlint (module)">Yamlint</a></span>
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
</p>
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
</div>
|
|
101
|
+
|
|
102
|
+
<div id="footer">
|
|
103
|
+
Generated on Sun Jan 25 08:00:08 2026 by
|
|
104
|
+
<a href="https://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
|
|
105
|
+
0.9.38 (ruby-4.0.0).
|
|
106
|
+
</div>
|
|
107
|
+
|
|
108
|
+
</div>
|
|
109
|
+
</body>
|
|
110
|
+
</html>
|
data/exe/yamlint
ADDED
data/lib/yamlint/cli.rb
ADDED
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'optparse'
|
|
4
|
+
|
|
5
|
+
module Yamlint
|
|
6
|
+
class Cli
|
|
7
|
+
attr_reader :options
|
|
8
|
+
|
|
9
|
+
def initialize(argv = ARGV)
|
|
10
|
+
@argv = argv
|
|
11
|
+
@options = {
|
|
12
|
+
config: nil,
|
|
13
|
+
format: 'colored',
|
|
14
|
+
fix: false,
|
|
15
|
+
dry_run: false,
|
|
16
|
+
strict: false
|
|
17
|
+
}
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def run
|
|
21
|
+
parse_options
|
|
22
|
+
command = @argv.shift || 'lint'
|
|
23
|
+
|
|
24
|
+
case command
|
|
25
|
+
when 'lint', 'l'
|
|
26
|
+
run_lint
|
|
27
|
+
when 'format', 'fmt', 'fix'
|
|
28
|
+
run_format
|
|
29
|
+
when 'version', '-v', '--version'
|
|
30
|
+
puts "yamlint #{VERSION}"
|
|
31
|
+
0
|
|
32
|
+
when 'help', '-h', '--help'
|
|
33
|
+
puts help_text
|
|
34
|
+
0
|
|
35
|
+
else
|
|
36
|
+
if File.exist?(command) || File.directory?(command)
|
|
37
|
+
@argv.unshift(command)
|
|
38
|
+
run_lint
|
|
39
|
+
else
|
|
40
|
+
warn "Unknown command: #{command}"
|
|
41
|
+
warn help_text
|
|
42
|
+
1
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
private
|
|
48
|
+
|
|
49
|
+
def parse_options
|
|
50
|
+
parser = OptionParser.new do |opts|
|
|
51
|
+
opts.banner = 'Usage: yamlint [command] [options] [files...]'
|
|
52
|
+
|
|
53
|
+
opts.on('-c', '--config FILE', 'Configuration file') do |file|
|
|
54
|
+
@options[:config] = file
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
opts.on('-f', '--format FORMAT', 'Output format (standard, parsable, colored, github)') do |format|
|
|
58
|
+
@options[:format] = format
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
opts.on('--fix', 'Auto-fix problems') do
|
|
62
|
+
@options[:fix] = true
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
opts.on('--dry-run', 'Show what would be fixed without making changes') do
|
|
66
|
+
@options[:dry_run] = true
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
opts.on('-s', '--strict', 'Treat warnings as errors') do
|
|
70
|
+
@options[:strict] = true
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
opts.on('-v', '--version', 'Show version') do
|
|
74
|
+
puts "yamlint #{VERSION}"
|
|
75
|
+
exit 0
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
opts.on('-h', '--help', 'Show help') do
|
|
79
|
+
puts opts
|
|
80
|
+
exit 0
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
parser.order!(@argv)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def run_lint
|
|
88
|
+
paths = @argv.empty? ? ['.'] : @argv
|
|
89
|
+
config = load_config
|
|
90
|
+
runner = Runner.new(config:, output_format: @options[:format])
|
|
91
|
+
|
|
92
|
+
result = runner.lint(paths)
|
|
93
|
+
|
|
94
|
+
puts result[:output] unless result[:output].empty?
|
|
95
|
+
puts result[:summary] unless result[:summary].empty?
|
|
96
|
+
|
|
97
|
+
result[:exit_code]
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def run_format
|
|
101
|
+
paths = @argv.empty? ? ['.'] : @argv
|
|
102
|
+
config = load_config
|
|
103
|
+
runner = Runner.new(config:, output_format: @options[:format])
|
|
104
|
+
|
|
105
|
+
result = runner.format(paths, dry_run: @options[:dry_run])
|
|
106
|
+
|
|
107
|
+
if @options[:dry_run]
|
|
108
|
+
if result[:changed].positive?
|
|
109
|
+
puts "Would fix #{result[:changed]} file(s):"
|
|
110
|
+
result[:changed_files].each { |f| puts " #{f}" }
|
|
111
|
+
else
|
|
112
|
+
puts 'No files would be changed'
|
|
113
|
+
end
|
|
114
|
+
elsif result[:changed].positive?
|
|
115
|
+
puts "Fixed #{result[:changed]} file(s)"
|
|
116
|
+
else
|
|
117
|
+
puts 'No files changed'
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
result[:exit_code]
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def load_config
|
|
124
|
+
if @options[:config]
|
|
125
|
+
Config.load(@options[:config])
|
|
126
|
+
else
|
|
127
|
+
Config.load_default
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def help_text
|
|
132
|
+
<<~HELP
|
|
133
|
+
yamlint - A YAML file linter and formatter
|
|
134
|
+
|
|
135
|
+
Usage:
|
|
136
|
+
yamlint [command] [options] [files...]
|
|
137
|
+
|
|
138
|
+
Commands:
|
|
139
|
+
lint, l Lint YAML files (default)
|
|
140
|
+
format, fmt Format YAML files
|
|
141
|
+
version Show version
|
|
142
|
+
help Show help
|
|
143
|
+
|
|
144
|
+
Options:
|
|
145
|
+
-c, --config FILE Configuration file
|
|
146
|
+
-f, --format FORMAT Output format (standard, parsable, colored, github)
|
|
147
|
+
--fix Auto-fix problems
|
|
148
|
+
--dry-run Show what would be fixed without making changes
|
|
149
|
+
-s, --strict Treat warnings as errors
|
|
150
|
+
-v, --version Show version
|
|
151
|
+
-h, --help Show help
|
|
152
|
+
|
|
153
|
+
Examples:
|
|
154
|
+
yamlint .
|
|
155
|
+
yamlint file.yaml
|
|
156
|
+
yamlint -c .yamllint.yml .
|
|
157
|
+
yamlint format --dry-run .
|
|
158
|
+
yamlint -f github .
|
|
159
|
+
HELP
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
end
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'yaml'
|
|
4
|
+
|
|
5
|
+
module Yamlint
|
|
6
|
+
class Config
|
|
7
|
+
DEFAULT_CONFIG_FILES = %w[
|
|
8
|
+
.yamllint
|
|
9
|
+
.yamllint.yaml
|
|
10
|
+
.yamllint.yml
|
|
11
|
+
].freeze
|
|
12
|
+
|
|
13
|
+
YAML_FILE_PATTERNS = %w[
|
|
14
|
+
*.yaml
|
|
15
|
+
*.yml
|
|
16
|
+
].freeze
|
|
17
|
+
|
|
18
|
+
attr_reader :rules, :yaml_files, :ignore, :extends
|
|
19
|
+
|
|
20
|
+
def initialize(options = {})
|
|
21
|
+
@extends = options[:extends]
|
|
22
|
+
@yaml_files = options[:'yaml-files'] || options['yaml-files'] || YAML_FILE_PATTERNS
|
|
23
|
+
@ignore = options[:ignore] || options['ignore'] || []
|
|
24
|
+
@rules = normalize_rules(options[:rules] || options['rules'] || {})
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def self.load(path = nil)
|
|
28
|
+
if path
|
|
29
|
+
load_from_file(path)
|
|
30
|
+
else
|
|
31
|
+
load_default
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def self.load_from_file(path)
|
|
36
|
+
content = File.read(path)
|
|
37
|
+
data = YAML.safe_load(content, permitted_classes: [Symbol]) || {}
|
|
38
|
+
build_from_hash(data)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def self.load_default
|
|
42
|
+
config_file = find_config_file
|
|
43
|
+
if config_file
|
|
44
|
+
load_from_file(config_file)
|
|
45
|
+
else
|
|
46
|
+
new_with_preset('default')
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def self.find_config_file
|
|
51
|
+
DEFAULT_CONFIG_FILES.find { |f| File.exist?(f) }
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def self.new_with_preset(preset_name)
|
|
55
|
+
preset = Presets.get(preset_name)
|
|
56
|
+
new(preset)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def self.build_from_hash(data)
|
|
60
|
+
base_config = if data['extends']
|
|
61
|
+
Presets.get(data['extends'])
|
|
62
|
+
else
|
|
63
|
+
{}
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
merged = deep_merge(base_config, symbolize_keys(data))
|
|
67
|
+
new(merged)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def rule_config(rule_id)
|
|
71
|
+
@rules[rule_id.to_s] || @rules[rule_id.to_sym] || {}
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def rule_enabled?(rule_id)
|
|
75
|
+
config = rule_config(rule_id)
|
|
76
|
+
config != 'disable' && config != false
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
private
|
|
80
|
+
|
|
81
|
+
def normalize_rules(rules)
|
|
82
|
+
normalized = {}
|
|
83
|
+
rules.each do |key, value|
|
|
84
|
+
normalized[key.to_s] = value
|
|
85
|
+
end
|
|
86
|
+
normalized
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
class << self
|
|
90
|
+
private
|
|
91
|
+
|
|
92
|
+
def deep_merge(base, override)
|
|
93
|
+
result = base.dup
|
|
94
|
+
override.each do |key, value|
|
|
95
|
+
result[key] = if value.is_a?(Hash) && result[key].is_a?(Hash)
|
|
96
|
+
deep_merge(result[key], value)
|
|
97
|
+
else
|
|
98
|
+
value
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
result
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def symbolize_keys(hash)
|
|
105
|
+
return hash unless hash.is_a?(Hash)
|
|
106
|
+
|
|
107
|
+
hash.transform_keys(&:to_sym).transform_values do |v|
|
|
108
|
+
v.is_a?(Hash) ? symbolize_keys(v) : v
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'date'
|
|
4
|
+
require 'yaml'
|
|
5
|
+
|
|
6
|
+
module Yamlint
|
|
7
|
+
class Formatter
|
|
8
|
+
attr_reader :config
|
|
9
|
+
|
|
10
|
+
def initialize(config = nil)
|
|
11
|
+
@config = config || Config.load_default
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def format(content, dry_run: false)
|
|
15
|
+
original = content.dup
|
|
16
|
+
result = content
|
|
17
|
+
|
|
18
|
+
result = fix_trailing_spaces(result)
|
|
19
|
+
result = fix_new_line_at_end(result)
|
|
20
|
+
result = fix_new_lines(result)
|
|
21
|
+
result = fix_empty_lines(result)
|
|
22
|
+
|
|
23
|
+
return original if dry_run
|
|
24
|
+
|
|
25
|
+
if safe_fix?(original, result)
|
|
26
|
+
result
|
|
27
|
+
else
|
|
28
|
+
original
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def format_file(filepath, dry_run: false)
|
|
33
|
+
content = File.read(filepath)
|
|
34
|
+
formatted = format(content, dry_run:)
|
|
35
|
+
|
|
36
|
+
File.write(filepath, formatted) unless dry_run || content == formatted
|
|
37
|
+
|
|
38
|
+
formatted
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
private
|
|
42
|
+
|
|
43
|
+
def fix_trailing_spaces(content)
|
|
44
|
+
content.gsub(/[ \t]+$/, '')
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def fix_new_line_at_end(content)
|
|
48
|
+
return content if content.empty?
|
|
49
|
+
return content if content.end_with?("\n")
|
|
50
|
+
|
|
51
|
+
"#{content}\n"
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def fix_new_lines(content)
|
|
55
|
+
new_line_type = @config.rules['new-lines']
|
|
56
|
+
return content unless new_line_type.is_a?(Hash)
|
|
57
|
+
|
|
58
|
+
type = new_line_type['type'] || new_line_type[:type] || 'unix'
|
|
59
|
+
case type
|
|
60
|
+
when 'unix'
|
|
61
|
+
content.gsub("\r\n", "\n")
|
|
62
|
+
when 'dos'
|
|
63
|
+
content.gsub(/(?<!\r)\n/, "\r\n")
|
|
64
|
+
else
|
|
65
|
+
content
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def fix_empty_lines(content)
|
|
70
|
+
lines = content.lines
|
|
71
|
+
return content if lines.empty?
|
|
72
|
+
|
|
73
|
+
empty_lines_config = @config.rules['empty-lines'] || {}
|
|
74
|
+
return content unless empty_lines_config.is_a?(Hash)
|
|
75
|
+
|
|
76
|
+
max_start = empty_lines_config['max-start'] || empty_lines_config[:'max-start'] || 0
|
|
77
|
+
max_end = empty_lines_config['max-end'] || empty_lines_config[:'max-end'] || 0
|
|
78
|
+
max = empty_lines_config['max'] || empty_lines_config[:max] || 2
|
|
79
|
+
|
|
80
|
+
lines = remove_start_empty_lines(lines, max_start)
|
|
81
|
+
lines = remove_end_empty_lines(lines, max_end)
|
|
82
|
+
lines = limit_consecutive_empty_lines(lines, max)
|
|
83
|
+
|
|
84
|
+
lines.join
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def remove_start_empty_lines(lines, max)
|
|
88
|
+
count = 0
|
|
89
|
+
lines.each do |line|
|
|
90
|
+
break unless line.strip.empty?
|
|
91
|
+
|
|
92
|
+
count += 1
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
lines.drop([count - max, 0].max)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def remove_end_empty_lines(lines, max)
|
|
99
|
+
count = 0
|
|
100
|
+
lines.reverse_each do |line|
|
|
101
|
+
break unless line.strip.empty?
|
|
102
|
+
|
|
103
|
+
count += 1
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
to_remove = [count - max, 0].max
|
|
107
|
+
to_remove.positive? ? lines[0...-to_remove] : lines
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def limit_consecutive_empty_lines(lines, max)
|
|
111
|
+
result = []
|
|
112
|
+
consecutive_empty = 0
|
|
113
|
+
|
|
114
|
+
lines.each do |line|
|
|
115
|
+
if line.strip.empty?
|
|
116
|
+
consecutive_empty += 1
|
|
117
|
+
result << line if consecutive_empty <= max
|
|
118
|
+
else
|
|
119
|
+
consecutive_empty = 0
|
|
120
|
+
result << line
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
result
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def safe_fix?(original, fixed)
|
|
128
|
+
return true if original == fixed
|
|
129
|
+
return false if original.empty? || fixed.empty?
|
|
130
|
+
|
|
131
|
+
begin
|
|
132
|
+
original_data = YAML.safe_load(original, permitted_classes: [Symbol, Date, Time])
|
|
133
|
+
fixed_data = YAML.safe_load(fixed, permitted_classes: [Symbol, Date, Time])
|
|
134
|
+
original_data == fixed_data
|
|
135
|
+
rescue Psych::SyntaxError
|
|
136
|
+
false
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
end
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'set'
|
|
4
|
+
require 'yaml'
|
|
5
|
+
|
|
6
|
+
module Yamlint
|
|
7
|
+
class Linter
|
|
8
|
+
attr_reader :config
|
|
9
|
+
|
|
10
|
+
def initialize(config = nil)
|
|
11
|
+
@config = config || Config.load_default
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def lint(content, filepath: nil)
|
|
15
|
+
context = build_context(content, filepath)
|
|
16
|
+
|
|
17
|
+
syntax_problems = check_syntax(content, filepath)
|
|
18
|
+
return syntax_problems unless syntax_problems.empty?
|
|
19
|
+
|
|
20
|
+
parse_content(context)
|
|
21
|
+
|
|
22
|
+
rules = build_rules
|
|
23
|
+
rules.each do |rule|
|
|
24
|
+
problems = rule.check(context)
|
|
25
|
+
context.problems.concat(Array(problems))
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
filter_disabled_problems(context)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def lint_file(filepath)
|
|
32
|
+
content = File.read(filepath)
|
|
33
|
+
lint(content, filepath:)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
private
|
|
37
|
+
|
|
38
|
+
def build_context(content, filepath)
|
|
39
|
+
Models::LintContext.new(
|
|
40
|
+
filepath: filepath || '<string>',
|
|
41
|
+
content:
|
|
42
|
+
)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def parse_content(context)
|
|
46
|
+
token_parser = Parser::TokenParser.new(context.content)
|
|
47
|
+
context.tokens.concat(token_parser.parse)
|
|
48
|
+
|
|
49
|
+
comment_extractor = Parser::CommentExtractor.new(context.content)
|
|
50
|
+
context.comments.concat(comment_extractor.extract)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def check_syntax(content, _filepath)
|
|
54
|
+
Psych.parse(content)
|
|
55
|
+
[]
|
|
56
|
+
rescue Psych::SyntaxError => e
|
|
57
|
+
[Models::Problem.new(
|
|
58
|
+
line: e.line,
|
|
59
|
+
column: e.column,
|
|
60
|
+
rule: 'syntax',
|
|
61
|
+
level: :error,
|
|
62
|
+
message: e.message.sub(/^\([^)]+\): /, ''),
|
|
63
|
+
fixable: false
|
|
64
|
+
)]
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def build_rules
|
|
68
|
+
Rules.load_all
|
|
69
|
+
|
|
70
|
+
enabled_rules = []
|
|
71
|
+
Rules::Registry.all.each do |rule_class|
|
|
72
|
+
rule_id = rule_class.id
|
|
73
|
+
next unless @config.rule_enabled?(rule_id)
|
|
74
|
+
|
|
75
|
+
rule_config = @config.rule_config(rule_id)
|
|
76
|
+
rule_options = rule_config.is_a?(Hash) ? normalize_options(rule_config) : {}
|
|
77
|
+
enabled_rules << rule_class.new(rule_options)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
enabled_rules
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def normalize_options(options)
|
|
84
|
+
normalized = {}
|
|
85
|
+
options.each do |key, value|
|
|
86
|
+
normalized[key.to_s.tr('_', '-').to_sym] = value
|
|
87
|
+
end
|
|
88
|
+
normalized
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def filter_disabled_problems(context)
|
|
92
|
+
disabled_rules = Set.new
|
|
93
|
+
line_disabled = {}
|
|
94
|
+
|
|
95
|
+
context.comments.each do |comment|
|
|
96
|
+
if comment.disable_directive?
|
|
97
|
+
rules = comment.disabled_rules
|
|
98
|
+
if comment.text.include?('disable-line')
|
|
99
|
+
line_disabled[comment.line_number] ||= Set.new
|
|
100
|
+
if rules.empty?
|
|
101
|
+
line_disabled[comment.line_number] = :all
|
|
102
|
+
else
|
|
103
|
+
line_disabled[comment.line_number].merge(rules) unless line_disabled[comment.line_number] == :all
|
|
104
|
+
end
|
|
105
|
+
else
|
|
106
|
+
rules.empty? ? disabled_rules = :all : disabled_rules.merge(rules)
|
|
107
|
+
end
|
|
108
|
+
elsif comment.enable_directive?
|
|
109
|
+
rules = comment.disabled_rules
|
|
110
|
+
if rules.empty?
|
|
111
|
+
disabled_rules = Set.new
|
|
112
|
+
elsif disabled_rules.is_a?(Set)
|
|
113
|
+
disabled_rules.subtract(rules)
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
context.problems.reject do |problem|
|
|
119
|
+
next true if disabled_rules == :all
|
|
120
|
+
next true if disabled_rules.include?(problem.rule)
|
|
121
|
+
|
|
122
|
+
line_disable = line_disabled[problem.line]
|
|
123
|
+
next true if line_disable == :all
|
|
124
|
+
next true if line_disable&.include?(problem.rule)
|
|
125
|
+
|
|
126
|
+
false
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Yamlint
|
|
4
|
+
module Models
|
|
5
|
+
class LintContext
|
|
6
|
+
attr_reader :filepath, :content, :lines, :tokens, :comments
|
|
7
|
+
attr_accessor :problems
|
|
8
|
+
|
|
9
|
+
def initialize(filepath:, content:)
|
|
10
|
+
@filepath = filepath
|
|
11
|
+
@content = content
|
|
12
|
+
@lines = content.lines
|
|
13
|
+
@tokens = []
|
|
14
|
+
@comments = []
|
|
15
|
+
@problems = []
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def add_problem(problem)
|
|
19
|
+
@problems << problem
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def add_token(token)
|
|
23
|
+
@tokens << token
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def add_comment(comment)
|
|
27
|
+
@comments << comment
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def line_count
|
|
31
|
+
@lines.length
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def line_at(line_number)
|
|
35
|
+
@lines[line_number - 1]
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def empty?
|
|
39
|
+
@content.empty?
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|