Module: Kdep::YamlDuplicateKeys
- Defined in:
- lib/kdep/yaml_duplicate_keys.rb
Overview
Finds keys declared twice in the same mapping, at any depth.
YAML resolves a repeated key by keeping the LAST one and discarding the
earlier block ENTIRELY, in silence. Nothing in the pipeline warns: helm
template, helm upgrade and kdep all receive the hash already collapsed by
the parser. In repleadfy/llm-api2#1 a redis values.yaml declared metrics:
twice; the first block -- the one redirecting the exporter registry to
bitnamilegacy, after Bitnami closed the free repo -- was dropped at parse
time, and the wrong image ran in production for 73 days, surviving only on
a node's image cache.
This walks the Psych AST rather than calling Psych.load, because load has
already applied last-one-wins and destroyed the evidence. The AST also
gives real line numbers on both sides, works at any depth, and knows that
key: value text inside a block scalar is data, not a key -- which a grep
over the raw file cannot.
Defined Under Namespace
Classes: Duplicate
Class Method Summary collapse
-
.in_dir(dir) ⇒ Object
Top-level only: everything kdep reads as input lives in the deploy dir root (app.yml, app.
.yml, secrets.yml, values.yaml), while nested dirs hold rendered output and templates. - .in_file(path) ⇒ Object
- .scan(node, path = []) ⇒ Object
Class Method Details
.in_dir(dir) ⇒ Object
Top-level only: everything kdep reads as input lives in the deploy dir
root (app.yml, app.
43 44 45 |
# File 'lib/kdep/yaml_duplicate_keys.rb', line 43 def self.in_dir(dir) Dir.glob(File.join(dir, "*.{yml,yaml}")).sort.flat_map { |f| in_file(f) } end |
.in_file(path) ⇒ Object
28 29 30 31 32 33 34 35 36 37 38 |
# File 'lib/kdep/yaml_duplicate_keys.rb', line 28 def self.in_file(path) ast = Psych.parse_stream(File.read(path)) scan(ast).map do |dup| Duplicate.new(File.basename(path), dup[:path], dup[:first], dup[:repeat]) end rescue Psych::SyntaxError # A file that doesn't parse is a different problem, reported elsewhere # with better context (Config#load, helm). Silently collapsing is the # failure this scanner exists for; don't turn it into a crash here. [] end |
.scan(node, path = []) ⇒ Object
47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
# File 'lib/kdep/yaml_duplicate_keys.rb', line 47 def self.scan(node, path = []) found = [] case node when Psych::Nodes::Mapping seen = {} node.children.each_slice(2) do |key, value| name = key.respond_to?(:value) ? key.value : key.to_s if seen.key?(name) found << { :path => (path + [name]).join("."), :first => seen[name], :repeat => key.start_line + 1, } end seen[name] = key.start_line + 1 found.concat(scan(value, path + [name])) end when Psych::Nodes::Sequence, Psych::Nodes::Document, Psych::Nodes::Stream node.children.each { |child| found.concat(scan(child, path)) } end found end |