Module: Kamal::Lint::Loader
- Defined in:
- lib/kamal/lint/loader.rb
Class Method Summary collapse
-
.build_line_index(text) ⇒ Object
Build a mapping from dot-path (“env.secret”) to source line numbers.
- .deep_merge(base, override) ⇒ Object
- .load(config_file:, destination: nil, kamal_version: nil) ⇒ Object
- .safe_parse_yaml(text) ⇒ Object
- .walk_node(node, path, index) ⇒ Object
Class Method Details
.build_line_index(text) ⇒ Object
Build a mapping from dot-path (“env.secret”) to source line numbers. Walks the Psych AST.
138 139 140 141 142 143 144 145 146 147 |
# File 'lib/kamal/lint/loader.rb', line 138 def build_line_index(text) index = {} stream = Psych.parse_stream(text) stream.children.each do |doc| walk_node(doc.root, [], index) end index rescue Psych::SyntaxError index end |
.deep_merge(base, override) ⇒ Object
122 123 124 125 126 127 128 129 130 131 132 133 134 |
# File 'lib/kamal/lint/loader.rb', line 122 def deep_merge(base, override) return override unless base.is_a?(Hash) && override.is_a?(Hash) result = base.dup override.each do |k, v| result[k] = if result[k].is_a?(Hash) && v.is_a?(Hash) deep_merge(result[k], v) else v end end result end |
.load(config_file:, destination: nil, kamal_version: nil) ⇒ Object
51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 |
# File 'lib/kamal/lint/loader.rb', line 51 def load(config_file:, destination: nil, kamal_version: nil) raise ConfigNotFoundError, "Config file not found: #{config_file}" unless File.exist?(config_file) working_dir = Pathname.new(config_file).realpath.dirname.dirname.to_s # If config_file is at config/deploy.yml inside a project, working_dir = project root. # If the user pointed somewhere else, fall back to its parent dir's parent. base_text = File.read(config_file) base_parsed = safe_parse_yaml(base_text) source_lines = base_text.lines override_parsed = nil if destination override_path = File.join(File.dirname(config_file), "deploy.#{destination}.yml") if File.exist?(override_path) override_parsed = safe_parse_yaml(File.read(override_path)) source_lines = File.read(override_path).lines end end merged = override_parsed ? deep_merge(base_parsed, override_parsed) : base_parsed line_index = build_line_index(base_text) secrets_path = File.join(working_dir, ".kamal", "secrets") gitignore_path = File.join(working_dir, ".gitignore") secrets_keys = SecretsFile.read_keys(secrets_path) loaded = true load_error = nil begin # Run Kamal's own loader for parse-level validation. We don't use the # returned object — we keep working off the parsed Hash so we can # report line numbers — but we surface Kamal's own errors as findings. require "kamal" Dir.chdir(working_dir) do Kamal::Configuration.create_from( config_file: Pathname.new(config_file), destination: destination, version: "kamal-lint" ) end rescue => e loaded = false load_error = e end Context.new( config_file: config_file, destination: destination, working_dir: working_dir, parsed: merged || {}, base_parsed: base_parsed || {}, override_parsed: override_parsed, source_lines: source_lines, line_index: line_index, secrets: secrets_keys, secrets_path: secrets_path, gitignore_path: gitignore_path, kamal_version: kamal_version || KamalVersion.detect, kamal_loaded: loaded, kamal_load_error: load_error ) end |
.safe_parse_yaml(text) ⇒ Object
115 116 117 118 119 120 |
# File 'lib/kamal/lint/loader.rb', line 115 def safe_parse_yaml(text) result = YAML.safe_load(text, aliases: true, permitted_classes: [ Symbol ]) result.is_a?(Hash) ? result : {} rescue Psych::SyntaxError {} end |
.walk_node(node, path, index) ⇒ Object
149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 |
# File 'lib/kamal/lint/loader.rb', line 149 def walk_node(node, path, index) case node when Psych::Nodes::Mapping node.children.each_slice(2) do |key_node, value_node| next unless key_node && value_node key = key_node.value new_path = path + [ key ] index[new_path.join(".")] ||= key_node.start_line + 1 walk_node(value_node, new_path, index) end when Psych::Nodes::Sequence node.children.each_with_index do |child, idx| new_path = path + [ idx.to_s ] # Index the position itself so checks can find sequence elements # by ordinal (e.g. "env.secret.0"). index[new_path.join(".")] ||= child.start_line + 1 walk_node(child, new_path, index) end when Psych::Nodes::Scalar # Scalars at non-root locations need no further indexing; their # parent already wrote the line for them. end end |