Class: Belt::CLI::FrontendEnvMap

Inherits:
Object
  • Object
show all
Defined in:
lib/belt/cli/frontend_env_map.rb

Overview

Resolves frontend env vars from a declarative map (env name → terraform output).

Map file (optional), first match wins:

frontend/env.yml
.belt/frontend_env.yml

Example:

VITE_API_URL: api_url
VITE_COGNITO_USER_POOL_ID: cognito_user_pool_id
REACT_APP_API_URL: api_url

No map → default { "VITE_API_URL" => "api_url" } (backwards compatible).

Constant Summary collapse

MAP_CANDIDATES =
[
  File.join('frontend', 'env.yml'),
  File.join('.belt', 'frontend_env.yml')
].freeze
DEFAULT_MAP =
{ 'VITE_API_URL' => 'api_url' }.freeze
DOTENV_PATH =
File.join('frontend', '.env')

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(env_name, env_dir: nil, map_path: nil) ⇒ FrontendEnvMap

Returns a new instance of FrontendEnvMap.



33
34
35
36
37
38
39
# File 'lib/belt/cli/frontend_env_map.rb', line 33

def initialize(env_name, env_dir: nil, map_path: nil)
  @env_name = env_name
  @env_dir = env_dir || File.join('infrastructure', env_name)
  @map_path = map_path || self.class.find_map_path
  @map = load_map
  @tf_cache = {}
end

Instance Attribute Details

#env_dirObject (readonly)

Returns the value of attribute env_dir.



31
32
33
# File 'lib/belt/cli/frontend_env_map.rb', line 31

def env_dir
  @env_dir
end

#env_nameObject (readonly)

Returns the value of attribute env_name.



31
32
33
# File 'lib/belt/cli/frontend_env_map.rb', line 31

def env_name
  @env_name
end

#mapObject (readonly)

Returns the value of attribute map.



31
32
33
# File 'lib/belt/cli/frontend_env_map.rb', line 31

def map
  @map
end

#map_pathObject (readonly)

Returns the value of attribute map_path.



31
32
33
# File 'lib/belt/cli/frontend_env_map.rb', line 31

def map_path
  @map_path
end

Class Method Details

.find_map_pathObject



41
42
43
# File 'lib/belt/cli/frontend_env_map.rb', line 41

def self.find_map_path
  MAP_CANDIDATES.find { |path| File.file?(path) }
end

Instance Method Details

#process_envObject

Hash of env_var => value suitable for process env (npm run build / vite). Skips keys whose terraform output is missing.



47
48
49
50
51
52
53
# File 'lib/belt/cli/frontend_env_map.rb', line 47

def process_env
  resolved = {}
  each_resolved do |env_key, value, _tf_output|
    resolved[env_key] = value if value
  end
  resolved
end

#using_default_map?Boolean

Returns:

  • (Boolean)


106
107
108
# File 'lib/belt/cli/frontend_env_map.rb', line 106

def using_default_map?
  @map_path.nil?
end

#write_dotenv!(path: DOTENV_PATH) ⇒ Object

Smart-merge mapped keys into frontend/.env.

  • Only overwrites keys present in the map
  • Preserves unknown keys, comments, and blank lines
  • Missing TF output → warn, do not clobber an existing value


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
# File 'lib/belt/cli/frontend_env_map.rb', line 59

def write_dotenv!(path: DOTENV_PATH)
  FileUtils.mkdir_p(File.dirname(path))

  existing_lines = File.file?(path) ? File.readlines(path, chomp: true) : []
  updates = {}
  missing = []

  each_resolved do |env_key, value, tf_output|
    if value
      updates[env_key] = value
    else
      missing << [env_key, tf_output]
    end
  end

  missing.each do |env_key, tf_output|
    puts "⚠️  terraform output '#{tf_output}' missing — leaving #{env_key} unchanged in #{path}"
  end

  written = {}
  new_lines = existing_lines.map do |line|
    key = dotenv_key(line)
    if key && updates.key?(key)
      written[key] = true
      format_dotenv_line(key, updates[key])
    else
      line
    end
  end

  updates.each do |key, value|
    next if written[key]

    new_lines << format_dotenv_line(key, value)
    written[key] = true
  end

  content = "#{new_lines.join("\n")}\n"
  File.write(path, content)

  {
    path: path,
    updated: written.keys.sort,
    missing: missing.map(&:first)
  }
end