Module: Legion::Settings::ProjectEnv

Extended by:
Logging::Helper
Defined in:
lib/legion/settings/project_env.rb

Overview

Per-project ‘.legionio.env` config file loader.

Walks up from Dir.pwd searching for a ‘.legionio.env` file. When found, parses `KEY=VALUE` lines with dot-notation keys and merges them into the loader at a priority between global settings and the request overlay.

File format:

# comment lines are ignored
llm.default_model=claude-sonnet-4-5-20241022
cache.driver=redis

Keys use dot notation to address nested settings paths. Values are always strings; callers should coerce as needed.

Resolution order (lowest → highest priority):

global settings < .legionio.env < request overlay (#9)

Constant Summary collapse

ENV_FILENAME =
'.legionio.env'

Class Method Summary collapse

Class Method Details

.find_project_env_file(start_dir: nil) ⇒ String?

Walk up from start_dir (defaults to Dir.pwd) looking for ‘.legionio.env`. Returns the first file found, or nil.

Parameters:

  • start_dir (String, nil) (defaults to: nil)

    directory to start the search from

Returns:

  • (String, nil)

    absolute path to the file, or nil



34
35
36
37
38
39
40
41
42
43
44
45
46
# File 'lib/legion/settings/project_env.rb', line 34

def find_project_env_file(start_dir: nil)
  dir = File.expand_path(start_dir || Dir.pwd)
  loop do
    candidate = File.join(dir, ENV_FILENAME)
    return candidate if File.file?(candidate) && File.readable?(candidate)

    parent = File.dirname(dir)
    break if parent == dir # filesystem root

    dir = parent
  end
  nil
end

.load_into(settings, start_dir: nil) ⇒ String?

Find and load the project env file into the given settings hash, merging overrides (env file values win over existing values).

Parameters:

  • settings (Hash)

    the settings hash to merge into (mutated in place)

  • start_dir (String, nil) (defaults to: nil)

    directory to start searching from

Returns:

  • (String, nil)

    path to the loaded file, or nil if none found



82
83
84
85
86
87
88
89
90
# File 'lib/legion/settings/project_env.rb', line 82

def load_into(settings, start_dir: nil)
  path = find_project_env_file(start_dir: start_dir)
  return nil unless path

  overrides = parse_env_file(path)
  deep_merge_into!(settings, overrides)
  log.debug("ProjectEnv: loaded #{path}")
  path
end

.parse_env_file(path) ⇒ Hash

Parse a ‘.legionio.env` file and return a nested hash of overrides.

Parameters:

  • path (String)

    absolute path to the file

Returns:

  • (Hash)

    nested hash with symbol keys



52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
# File 'lib/legion/settings/project_env.rb', line 52

def parse_env_file(path)
  result = {}
  File.readlines(path, chomp: true).each_with_index do |line, idx|
    next if line.strip.empty?
    next if line.strip.start_with?('#')

    parts = line.split('=', 2)
    unless parts.length == 2
      log.warn("#{path}:#{idx + 1}: skipping malformed line (no '=' found)")
      next
    end

    raw_key, value = parts
    key_parts = raw_key.strip.split('.')
    if key_parts.empty? || key_parts.any?(&:empty?)
      log.warn("#{path}:#{idx + 1}: skipping invalid key '#{raw_key.strip}'")
      next
    end

    set_nested(result, key_parts.map(&:to_sym), value.strip)
  end
  result
end