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,107 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Yamlint
|
|
4
|
+
module Rules
|
|
5
|
+
class EmptyLines < Base
|
|
6
|
+
rule_id 'empty-lines'
|
|
7
|
+
desc 'Limit the number of consecutive blank lines.'
|
|
8
|
+
defaults({
|
|
9
|
+
max: 2,
|
|
10
|
+
'max-start': 0,
|
|
11
|
+
'max-end': 0
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
def check(context)
|
|
15
|
+
return [] if context.empty?
|
|
16
|
+
|
|
17
|
+
problems = []
|
|
18
|
+
problems.concat(check_start_empty_lines(context))
|
|
19
|
+
problems.concat(check_end_empty_lines(context))
|
|
20
|
+
problems.concat(check_consecutive_empty_lines(context))
|
|
21
|
+
problems
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def fixable?
|
|
25
|
+
true
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
private
|
|
29
|
+
|
|
30
|
+
def check_start_empty_lines(context)
|
|
31
|
+
max_start = @config[:'max-start']
|
|
32
|
+
count = 0
|
|
33
|
+
|
|
34
|
+
context.lines.each do |line|
|
|
35
|
+
break unless blank_line?(line)
|
|
36
|
+
|
|
37
|
+
count += 1
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
return [] if count <= max_start
|
|
41
|
+
|
|
42
|
+
[problem(
|
|
43
|
+
line: 1,
|
|
44
|
+
column: 1,
|
|
45
|
+
message: "too many blank lines at the beginning of file (#{count} > #{max_start})",
|
|
46
|
+
fixable: true
|
|
47
|
+
)]
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def check_end_empty_lines(context)
|
|
51
|
+
max_end = @config[:'max-end']
|
|
52
|
+
count = 0
|
|
53
|
+
lines = context.lines
|
|
54
|
+
|
|
55
|
+
lines.reverse_each do |line|
|
|
56
|
+
break unless blank_line?(line)
|
|
57
|
+
|
|
58
|
+
count += 1
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
return [] if count <= max_end
|
|
62
|
+
|
|
63
|
+
[problem(
|
|
64
|
+
line: context.line_count - count + 1,
|
|
65
|
+
column: 1,
|
|
66
|
+
message: "too many blank lines at the end of file (#{count} > #{max_end})",
|
|
67
|
+
fixable: true
|
|
68
|
+
)]
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def check_consecutive_empty_lines(context)
|
|
72
|
+
max = @config[:max]
|
|
73
|
+
problems = []
|
|
74
|
+
consecutive_count = 0
|
|
75
|
+
first_blank_line = nil
|
|
76
|
+
|
|
77
|
+
context.lines.each_with_index do |line, index|
|
|
78
|
+
line_number = index + 1
|
|
79
|
+
|
|
80
|
+
if blank_line?(line)
|
|
81
|
+
first_blank_line ||= line_number
|
|
82
|
+
consecutive_count += 1
|
|
83
|
+
else
|
|
84
|
+
if consecutive_count > max
|
|
85
|
+
problems << problem(
|
|
86
|
+
line: first_blank_line,
|
|
87
|
+
column: 1,
|
|
88
|
+
message: "too many blank lines (#{consecutive_count} > #{max})",
|
|
89
|
+
fixable: true
|
|
90
|
+
)
|
|
91
|
+
end
|
|
92
|
+
consecutive_count = 0
|
|
93
|
+
first_blank_line = nil
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
problems
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def blank_line?(line)
|
|
101
|
+
line.strip.empty?
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
Registry.register(EmptyLines)
|
|
106
|
+
end
|
|
107
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Yamlint
|
|
4
|
+
module Rules
|
|
5
|
+
class EmptyValues < LineRule
|
|
6
|
+
rule_id 'empty-values'
|
|
7
|
+
desc 'Forbid empty values in mappings.'
|
|
8
|
+
defaults({
|
|
9
|
+
'forbid-in-block-mappings': true,
|
|
10
|
+
'forbid-in-flow-mappings': true
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
def check_line(line, line_number, _context)
|
|
14
|
+
return if line.strip.empty?
|
|
15
|
+
return if line.strip.start_with?('#')
|
|
16
|
+
|
|
17
|
+
problems = []
|
|
18
|
+
|
|
19
|
+
if @config[:'forbid-in-block-mappings'] && line.match?(/:\s*$/) && !line.match?(/[|>]/)
|
|
20
|
+
problems << problem(
|
|
21
|
+
line: line_number,
|
|
22
|
+
column: line.index(':') + 1,
|
|
23
|
+
message: 'empty value in block mapping',
|
|
24
|
+
fixable: false
|
|
25
|
+
)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
if @config[:'forbid-in-flow-mappings'] && line.match?(/:\s*[,}]/)
|
|
29
|
+
problems << problem(
|
|
30
|
+
line: line_number,
|
|
31
|
+
column: line.index(':') + 1,
|
|
32
|
+
message: 'empty value in flow mapping',
|
|
33
|
+
fixable: false
|
|
34
|
+
)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
problems
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
Registry.register(EmptyValues)
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Yamlint
|
|
4
|
+
module Rules
|
|
5
|
+
class FloatValues < LineRule
|
|
6
|
+
rule_id 'float-values'
|
|
7
|
+
desc 'Check float value formatting.'
|
|
8
|
+
defaults({
|
|
9
|
+
'forbid-scientific-notation': false,
|
|
10
|
+
'forbid-nan': false,
|
|
11
|
+
'forbid-inf': false,
|
|
12
|
+
'require-numeral-before-decimal': false
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
def check_line(line, line_number, _context)
|
|
16
|
+
return if line.strip.empty?
|
|
17
|
+
return if line.strip.start_with?('#')
|
|
18
|
+
|
|
19
|
+
problems = []
|
|
20
|
+
|
|
21
|
+
if @config[:'forbid-scientific-notation'] && line.match?(/:\s*[\d.]+[eE][+-]?\d+/)
|
|
22
|
+
problems << problem(
|
|
23
|
+
line: line_number,
|
|
24
|
+
column: line.index(':') + 1,
|
|
25
|
+
message: 'scientific notation forbidden',
|
|
26
|
+
fixable: false
|
|
27
|
+
)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
if @config[:'forbid-nan'] && line.match?(/:\s*\.nan\s*(?:#.*)?$/i)
|
|
31
|
+
problems << problem(
|
|
32
|
+
line: line_number,
|
|
33
|
+
column: line.index(':') + 1,
|
|
34
|
+
message: '.nan value forbidden',
|
|
35
|
+
fixable: false
|
|
36
|
+
)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
if @config[:'forbid-inf'] && line.match?(/:\s*[+-]?\.inf\s*(?:#.*)?$/i)
|
|
40
|
+
problems << problem(
|
|
41
|
+
line: line_number,
|
|
42
|
+
column: line.index(':') + 1,
|
|
43
|
+
message: '.inf value forbidden',
|
|
44
|
+
fixable: false
|
|
45
|
+
)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
if @config[:'require-numeral-before-decimal'] && line.match?(/:\s*\.\d+/)
|
|
49
|
+
problems << problem(
|
|
50
|
+
line: line_number,
|
|
51
|
+
column: line.index(':') + 1,
|
|
52
|
+
message: 'numeral required before decimal point',
|
|
53
|
+
fixable: true
|
|
54
|
+
)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
problems
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def fixable?
|
|
61
|
+
@config[:'require-numeral-before-decimal']
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
Registry.register(FloatValues)
|
|
66
|
+
end
|
|
67
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'English'
|
|
4
|
+
module Yamlint
|
|
5
|
+
module Rules
|
|
6
|
+
class Hyphens < LineRule
|
|
7
|
+
rule_id 'hyphens'
|
|
8
|
+
desc 'Check space after hyphens.'
|
|
9
|
+
defaults({
|
|
10
|
+
'max-spaces-after': 1
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
def check_line(line, line_number, _context)
|
|
14
|
+
return if line.strip.empty?
|
|
15
|
+
return if line.strip.start_with?('#')
|
|
16
|
+
|
|
17
|
+
problems = []
|
|
18
|
+
max_after = @config[:'max-spaces-after']
|
|
19
|
+
|
|
20
|
+
line.scan(/^(\s*)-(\s+)/) do |_indent, spaces|
|
|
21
|
+
next if spaces.length <= max_after
|
|
22
|
+
|
|
23
|
+
pos = $LAST_MATCH_INFO.begin(2)
|
|
24
|
+
problems << problem(
|
|
25
|
+
line: line_number,
|
|
26
|
+
column: pos + 1,
|
|
27
|
+
message: "too many spaces after hyphen (#{spaces.length} > #{max_after})",
|
|
28
|
+
fixable: true
|
|
29
|
+
)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
problems
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def fixable?
|
|
36
|
+
true
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
Registry.register(Hyphens)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Yamlint
|
|
4
|
+
module Rules
|
|
5
|
+
class Indentation < LineRule
|
|
6
|
+
rule_id 'indentation'
|
|
7
|
+
desc 'Enforce consistent indentation.'
|
|
8
|
+
defaults({
|
|
9
|
+
spaces: 2,
|
|
10
|
+
'indent-sequences': true,
|
|
11
|
+
'check-multi-line-strings': false
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
def check_line(line, line_number, _context)
|
|
15
|
+
return if line.strip.empty?
|
|
16
|
+
return if line.start_with?('#')
|
|
17
|
+
|
|
18
|
+
expected_spaces = @config[:spaces]
|
|
19
|
+
leading_spaces = line[/\A */].length
|
|
20
|
+
|
|
21
|
+
return if leading_spaces.zero?
|
|
22
|
+
return if (leading_spaces % expected_spaces).zero?
|
|
23
|
+
|
|
24
|
+
problem(
|
|
25
|
+
line: line_number,
|
|
26
|
+
column: 1,
|
|
27
|
+
message: "wrong indentation: expected #{expected_spaces}n spaces",
|
|
28
|
+
fixable: true
|
|
29
|
+
)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def fixable?
|
|
33
|
+
true
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
Registry.register(Indentation)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'yaml'
|
|
4
|
+
|
|
5
|
+
module Yamlint
|
|
6
|
+
module Rules
|
|
7
|
+
class KeyDuplicates < Base
|
|
8
|
+
rule_id 'key-duplicates'
|
|
9
|
+
desc 'Forbid duplicated keys in mappings.'
|
|
10
|
+
defaults({})
|
|
11
|
+
|
|
12
|
+
def check(context)
|
|
13
|
+
handler = DuplicateKeyHandler.new
|
|
14
|
+
parser = Psych::Parser.new(handler)
|
|
15
|
+
handler.parser = parser
|
|
16
|
+
|
|
17
|
+
begin
|
|
18
|
+
parser.parse(context.content)
|
|
19
|
+
rescue Psych::SyntaxError
|
|
20
|
+
return []
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
handler.problems.map do |prob|
|
|
24
|
+
problem(
|
|
25
|
+
line: prob[:line],
|
|
26
|
+
column: prob[:column],
|
|
27
|
+
message: "duplicate key \"#{prob[:key]}\"",
|
|
28
|
+
fixable: false
|
|
29
|
+
)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
class DuplicateKeyHandler < Psych::Handler
|
|
34
|
+
attr_reader :problems
|
|
35
|
+
|
|
36
|
+
def initialize
|
|
37
|
+
super
|
|
38
|
+
@stack = []
|
|
39
|
+
@problems = []
|
|
40
|
+
@parser = nil
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
attr_writer :parser
|
|
44
|
+
|
|
45
|
+
def start_mapping(*)
|
|
46
|
+
parent = current_mapping_frame
|
|
47
|
+
@stack << {
|
|
48
|
+
type: :mapping,
|
|
49
|
+
keys: {},
|
|
50
|
+
expecting_key: true,
|
|
51
|
+
close_parent: capture_close_parent(parent)
|
|
52
|
+
}
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def end_mapping
|
|
56
|
+
frame = @stack.pop
|
|
57
|
+
close_parent_value(frame)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def start_sequence(*)
|
|
61
|
+
parent = current_mapping_frame
|
|
62
|
+
@stack << {
|
|
63
|
+
type: :sequence,
|
|
64
|
+
close_parent: capture_close_parent(parent)
|
|
65
|
+
}
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def end_sequence
|
|
69
|
+
frame = @stack.pop
|
|
70
|
+
close_parent_value(frame)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def scalar(value, _anchor, _tag, _plain, _quoted, _style)
|
|
74
|
+
frame = @stack.last
|
|
75
|
+
return unless frame && frame[:type] == :mapping
|
|
76
|
+
|
|
77
|
+
if frame[:expecting_key]
|
|
78
|
+
current_keys = frame[:keys]
|
|
79
|
+
mark = @parser&.mark
|
|
80
|
+
|
|
81
|
+
if current_keys.key?(value)
|
|
82
|
+
@problems << {
|
|
83
|
+
key: value,
|
|
84
|
+
line: mark ? mark.line + 1 : 1,
|
|
85
|
+
column: mark ? mark.column + 1 : 1
|
|
86
|
+
}
|
|
87
|
+
else
|
|
88
|
+
current_keys[value] = true
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
frame[:expecting_key] = false
|
|
92
|
+
else
|
|
93
|
+
frame[:expecting_key] = true
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
private
|
|
98
|
+
|
|
99
|
+
def current_mapping_frame
|
|
100
|
+
frame = @stack.last
|
|
101
|
+
return frame if frame && frame[:type] == :mapping
|
|
102
|
+
|
|
103
|
+
nil
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def capture_close_parent(parent)
|
|
107
|
+
return nil unless parent
|
|
108
|
+
|
|
109
|
+
if parent[:expecting_key]
|
|
110
|
+
{ parent:, expecting_key: false }
|
|
111
|
+
else
|
|
112
|
+
{ parent:, expecting_key: true }
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def close_parent_value(frame)
|
|
117
|
+
close = frame[:close_parent]
|
|
118
|
+
return unless close
|
|
119
|
+
|
|
120
|
+
close[:parent][:expecting_key] = close[:expecting_key]
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
Registry.register(KeyDuplicates)
|
|
126
|
+
end
|
|
127
|
+
end
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'yaml'
|
|
4
|
+
|
|
5
|
+
module Yamlint
|
|
6
|
+
module Rules
|
|
7
|
+
class KeyOrdering < Base
|
|
8
|
+
rule_id 'key-ordering'
|
|
9
|
+
desc 'Enforce alphabetical ordering of keys.'
|
|
10
|
+
defaults({})
|
|
11
|
+
|
|
12
|
+
def check(context)
|
|
13
|
+
handler = KeyOrderHandler.new
|
|
14
|
+
parser = Psych::Parser.new(handler)
|
|
15
|
+
handler.parser = parser
|
|
16
|
+
|
|
17
|
+
begin
|
|
18
|
+
parser.parse(context.content)
|
|
19
|
+
rescue Psych::SyntaxError
|
|
20
|
+
return []
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
handler.problems.map do |prob|
|
|
24
|
+
problem(
|
|
25
|
+
line: prob[:line],
|
|
26
|
+
column: prob[:column],
|
|
27
|
+
message: "wrong ordering of key \"#{prob[:key]}\" (should be before \"#{prob[:prev_key]}\")",
|
|
28
|
+
fixable: false
|
|
29
|
+
)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
class KeyOrderHandler < Psych::Handler
|
|
34
|
+
attr_reader :problems
|
|
35
|
+
attr_accessor :parser
|
|
36
|
+
|
|
37
|
+
def initialize
|
|
38
|
+
super
|
|
39
|
+
@stack = []
|
|
40
|
+
@problems = []
|
|
41
|
+
@parser = nil
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def start_mapping(*)
|
|
45
|
+
parent = current_mapping_frame
|
|
46
|
+
@stack << {
|
|
47
|
+
type: :mapping,
|
|
48
|
+
keys: [],
|
|
49
|
+
expecting_key: true,
|
|
50
|
+
close_parent: capture_close_parent(parent)
|
|
51
|
+
}
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def end_mapping
|
|
55
|
+
frame = @stack.pop
|
|
56
|
+
close_parent_value(frame)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def start_sequence(*)
|
|
60
|
+
parent = current_mapping_frame
|
|
61
|
+
@stack << {
|
|
62
|
+
type: :sequence,
|
|
63
|
+
close_parent: capture_close_parent(parent)
|
|
64
|
+
}
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def end_sequence
|
|
68
|
+
frame = @stack.pop
|
|
69
|
+
close_parent_value(frame)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def scalar(value, _anchor, _tag, _plain, _quoted, _style)
|
|
73
|
+
frame = @stack.last
|
|
74
|
+
return unless frame && frame[:type] == :mapping
|
|
75
|
+
|
|
76
|
+
if frame[:expecting_key]
|
|
77
|
+
current_keys = frame[:keys]
|
|
78
|
+
mark = @parser&.mark
|
|
79
|
+
|
|
80
|
+
if current_keys.any? && value < current_keys.last
|
|
81
|
+
@problems << {
|
|
82
|
+
key: value,
|
|
83
|
+
prev_key: current_keys.last,
|
|
84
|
+
line: mark ? mark.line + 1 : 1,
|
|
85
|
+
column: mark ? mark.column + 1 : 1
|
|
86
|
+
}
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
current_keys << value
|
|
90
|
+
frame[:expecting_key] = false
|
|
91
|
+
else
|
|
92
|
+
frame[:expecting_key] = true
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
private
|
|
97
|
+
|
|
98
|
+
def current_mapping_frame
|
|
99
|
+
frame = @stack.last
|
|
100
|
+
return frame if frame && frame[:type] == :mapping
|
|
101
|
+
|
|
102
|
+
nil
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def capture_close_parent(parent)
|
|
106
|
+
return nil unless parent
|
|
107
|
+
|
|
108
|
+
if parent[:expecting_key]
|
|
109
|
+
{ parent:, expecting_key: false }
|
|
110
|
+
else
|
|
111
|
+
{ parent:, expecting_key: true }
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def close_parent_value(frame)
|
|
116
|
+
close = frame[:close_parent]
|
|
117
|
+
return unless close
|
|
118
|
+
|
|
119
|
+
close[:parent][:expecting_key] = close[:expecting_key]
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
Registry.register(KeyOrdering)
|
|
125
|
+
end
|
|
126
|
+
end
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Yamlint
|
|
4
|
+
module Rules
|
|
5
|
+
class LineLength < LineRule
|
|
6
|
+
rule_id 'line-length'
|
|
7
|
+
desc 'Limit the line length.'
|
|
8
|
+
defaults({
|
|
9
|
+
max: 80,
|
|
10
|
+
'allow-non-breakable-words': true,
|
|
11
|
+
'allow-non-breakable-inline-mappings': true
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
def check_line(line, line_number, _context)
|
|
15
|
+
max_length = @config[:max]
|
|
16
|
+
actual_length = line.chomp.length
|
|
17
|
+
|
|
18
|
+
return if actual_length <= max_length
|
|
19
|
+
return if allow_non_breakable_words? && non_breakable_word?(line)
|
|
20
|
+
return if allow_non_breakable_inline_mappings? && inline_mapping?(line)
|
|
21
|
+
|
|
22
|
+
problem(
|
|
23
|
+
line: line_number,
|
|
24
|
+
column: max_length + 1,
|
|
25
|
+
message: "line too long (#{actual_length} > #{max_length} characters)",
|
|
26
|
+
fixable: false
|
|
27
|
+
)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
private
|
|
31
|
+
|
|
32
|
+
def allow_non_breakable_words?
|
|
33
|
+
@config[:'allow-non-breakable-words']
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def allow_non_breakable_inline_mappings?
|
|
37
|
+
@config[:'allow-non-breakable-inline-mappings']
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def non_breakable_word?(line)
|
|
41
|
+
stripped = line.strip
|
|
42
|
+
return false if stripped.empty?
|
|
43
|
+
|
|
44
|
+
# Check if the line contains a URL or a very long word
|
|
45
|
+
stripped.match?(%r{https?://\S+}) ||
|
|
46
|
+
stripped.split.any? { |word| word.length > @config[:max] }
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def inline_mapping?(line)
|
|
50
|
+
stripped = line.strip
|
|
51
|
+
stripped.match?(/^\{.*\}$/)
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
Registry.register(LineLength)
|
|
56
|
+
end
|
|
57
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Yamlint
|
|
4
|
+
module Rules
|
|
5
|
+
class NewLineAtEndOfFile < Base
|
|
6
|
+
rule_id 'new-line-at-end-of-file'
|
|
7
|
+
desc 'Require a new line character at the end of files.'
|
|
8
|
+
defaults({})
|
|
9
|
+
|
|
10
|
+
def check(context)
|
|
11
|
+
return [] if context.empty?
|
|
12
|
+
|
|
13
|
+
last_char = context.content[-1]
|
|
14
|
+
return [] if last_char == "\n"
|
|
15
|
+
|
|
16
|
+
[problem(
|
|
17
|
+
line: context.line_count,
|
|
18
|
+
column: (context.lines.last&.length || 0) + 1,
|
|
19
|
+
message: 'no new line character at the end of file',
|
|
20
|
+
fixable: true
|
|
21
|
+
)]
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def fixable?
|
|
25
|
+
true
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
Registry.register(NewLineAtEndOfFile)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Yamlint
|
|
4
|
+
module Rules
|
|
5
|
+
class NewLines < Base
|
|
6
|
+
rule_id 'new-lines'
|
|
7
|
+
desc 'Use consistent line endings throughout the file.'
|
|
8
|
+
defaults({ type: 'unix' })
|
|
9
|
+
|
|
10
|
+
def check(context)
|
|
11
|
+
return [] if context.empty?
|
|
12
|
+
|
|
13
|
+
expected_type = @config[:type]
|
|
14
|
+
problems = []
|
|
15
|
+
|
|
16
|
+
context.lines.each_with_index do |line, index|
|
|
17
|
+
line_number = index + 1
|
|
18
|
+
problem = check_line_ending(line, line_number, expected_type)
|
|
19
|
+
problems << problem if problem
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
problems
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def fixable?
|
|
26
|
+
true
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
private
|
|
30
|
+
|
|
31
|
+
def check_line_ending(line, line_number, expected_type)
|
|
32
|
+
case expected_type
|
|
33
|
+
when 'unix'
|
|
34
|
+
if line.end_with?("\r\n")
|
|
35
|
+
problem(
|
|
36
|
+
line: line_number,
|
|
37
|
+
column: line.length - 1,
|
|
38
|
+
message: 'wrong new line character: expected unix',
|
|
39
|
+
fixable: true
|
|
40
|
+
)
|
|
41
|
+
end
|
|
42
|
+
when 'dos'
|
|
43
|
+
if line.end_with?("\n") && !line.end_with?("\r\n")
|
|
44
|
+
problem(
|
|
45
|
+
line: line_number,
|
|
46
|
+
column: line.length,
|
|
47
|
+
message: 'wrong new line character: expected dos',
|
|
48
|
+
fixable: true
|
|
49
|
+
)
|
|
50
|
+
end
|
|
51
|
+
when 'platform'
|
|
52
|
+
nil
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
Registry.register(NewLines)
|
|
58
|
+
end
|
|
59
|
+
end
|