Class: Fastlane::Wpmreleasetoolkit::EnvManager

Inherits:
Object
  • Object
show all
Defined in:
lib/fastlane/plugin/wpmreleasetoolkit/env_manager/env_manager.rb

Overview

Manages loading of environment variables from a .env and accessing them in a user-friendly way.

Class Attribute Summary collapse

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(env_file_name:, env_file_folder: File.join(Dir.home, '.a8c-apps'), example_env_file_path: 'fastlane/example.env', mutate_env: true, print_error_lambda: ->(message) { FastlaneCore::UI.user_error!(message) }, print_warning_lambda: ->(message) { FastlaneCore::UI.important(message) }) ⇒ EnvManager

Set up by loading the .env file with the given name.

When mutate_env is true (the default), values from the .env file are layered into the process ENV using no-override semantics: keys already set in ENV (e.g. by CI) win. This lets fastlane actions that look up values via ENV.fetch(...) for their default_value: find them without the caller having to thread them through explicitly.

Pass mutate_env: false to keep ENV pristine — values are still accessible via get_required_env!, but only through this instance. Useful for tests that want isolation, or for callers that prefer to control ENV themselves.



29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
# File 'lib/fastlane/plugin/wpmreleasetoolkit/env_manager/env_manager.rb', line 29

def initialize(
  env_file_name:,
  env_file_folder: File.join(Dir.home, '.a8c-apps'),
  example_env_file_path: 'fastlane/example.env',
  mutate_env: true,
  print_error_lambda: ->(message) { FastlaneCore::UI.user_error!(message) },
  print_warning_lambda: ->(message) { FastlaneCore::UI.important(message) }
)
  @env_path = File.join(env_file_folder, env_file_name)
  @env_example_path = example_env_file_path
  @print_error_lambda = print_error_lambda
  @print_warning_lambda = print_warning_lambda

  unless File.exist?(@env_path) || running_on_ci?
    @print_warning_lambda.call("Warning: env file not found at #{@env_path}. Environment variables may not be loaded.")
  end

  unless mutate_env
    @loaded_env = File.exist?(@env_path) ? Dotenv.parse(@env_path) : {}
    @mutations = {}
    return
  end

  # `load` rather than `parse` so interpolated values (`B=${A}`) resolve
  # against the process `ENV`; `parse` resolves them against the file's own values.
  env_before = ENV.to_h
  @loaded_env = File.exist?(@env_path) ? Dotenv.load(@env_path) : {}
  # Records the value this instance wrote for each key it added. On restore,
  # we delete only if `ENV[key]` still matches — if a later caller overwrote
  # it, their value is left alone.
  @mutations = ENV.to_h.reject { |key, _| env_before.key?(key) }
end

Class Attribute Details

.default_print_error_lambdaObject



190
191
192
# File 'lib/fastlane/plugin/wpmreleasetoolkit/env_manager/env_manager.rb', line 190

def self.default_print_error_lambda
  @default&.print_error_lambda || @default_print_error_lambda || ->(message) { FastlaneCore::UI.user_error!(message) }
end

Instance Attribute Details

#env_example_pathObject (readonly)

Returns the value of attribute env_example_path.



11
12
13
# File 'lib/fastlane/plugin/wpmreleasetoolkit/env_manager/env_manager.rb', line 11

def env_example_path
  @env_example_path
end

#env_pathObject (readonly)

Returns the value of attribute env_path.



11
12
13
# File 'lib/fastlane/plugin/wpmreleasetoolkit/env_manager/env_manager.rb', line 11

def env_path
  @env_path
end

Returns the value of attribute print_error_lambda.



11
12
13
# File 'lib/fastlane/plugin/wpmreleasetoolkit/env_manager/env_manager.rb', line 11

def print_error_lambda
  @print_error_lambda
end

Class Method Details

.configured?Boolean

Returns true if a default instance has been configured via .set_up.

Returns:

  • (Boolean)


178
179
180
# File 'lib/fastlane/plugin/wpmreleasetoolkit/env_manager/env_manager.rb', line 178

def self.configured?
  !@default.nil?
end

.default!Object



182
183
184
185
186
187
188
# File 'lib/fastlane/plugin/wpmreleasetoolkit/env_manager/env_manager.rb', line 182

def self.default!
  return @default if configured?

  message = 'EnvManager is not configured. Call `EnvManager.set_up(...)` first.'
  default_print_error_lambda.call(message)
  raise message
end

.get_required_env!(key) ⇒ Object



162
163
164
# File 'lib/fastlane/plugin/wpmreleasetoolkit/env_manager/env_manager.rb', line 162

def self.get_required_env!(key)
  default!.get_required_env!(key)
end

.require_env_vars!(*keys) ⇒ Object



166
167
168
# File 'lib/fastlane/plugin/wpmreleasetoolkit/env_manager/env_manager.rb', line 166

def self.require_env_vars!(*keys)
  default!.require_env_vars!(*keys)
end

.reset!Object

Clears the default instance, useful for test teardown. Also rolls back any ENV mutations the default instance performed via mutate_env.



172
173
174
175
# File 'lib/fastlane/plugin/wpmreleasetoolkit/env_manager/env_manager.rb', line 172

def self.reset!
  @default&.restore_env!
  @default = nil
end

.set_up(**args) ⇒ Object

Class-level convenience methods that delegate to a default instance. This preserves the existing API: EnvManager.set_up(...) then EnvManager.get_required_env!(...).



153
154
155
156
157
158
159
160
# File 'lib/fastlane/plugin/wpmreleasetoolkit/env_manager/env_manager.rb', line 153

def self.set_up(**args)
  if configured?
    default_print_error_lambda.call('EnvManager is already configured. Call `EnvManager.reset!` before calling `EnvManager.set_up(...)` again.')
    return @default
  end

  @default = new(**args)
end

Instance Method Details

#branch_nameObject



130
131
132
# File 'lib/fastlane/plugin/wpmreleasetoolkit/env_manager/env_manager.rb', line 130

def branch_name
  ENV.fetch('BUILDKITE_BRANCH', nil)
end

#build_numberObject

CI environment helpers — read common metadata from the CI provider.

Notice that given Buildkite is the only CI provider we use, they are Buildkite-dependent.

If this were to be adopted more broadly, we'd need a two-tier approach:

  1. Detect which CI is in use
  2. Use its specific env vars
  3. Maybe fallback to best guess or outright error if no vendor detected


126
127
128
# File 'lib/fastlane/plugin/wpmreleasetoolkit/env_manager/env_manager.rb', line 126

def build_number
  ENV.fetch('BUILDKITE_BUILD_NUMBER', '0')
end

#commit_hashObject



134
135
136
# File 'lib/fastlane/plugin/wpmreleasetoolkit/env_manager/env_manager.rb', line 134

def commit_hash
  ENV.fetch('BUILDKITE_COMMIT', nil)
end

#get_required_env!(key) ⇒ Object

Use this instead of getting values from ENV directly. It will throw an error if the requested value is missing or empty.



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
# File 'lib/fastlane/plugin/wpmreleasetoolkit/env_manager/env_manager.rb', line 73

def get_required_env!(key)
  unless env_var_set?(key)
    message = "Environment variable '#{key}' is not set."

    error_message =
      if running_on_ci?
        message
      elsif File.exist?(@env_path)
        "#{message} Consider adding it to #{@env_path}."
      else
        env_file_dir = File.dirname(@env_path)
        env_file_name = File.basename(@env_path)

        <<~MSG
          #{env_file_name} not found in #{env_file_dir} while looking for env var #{key}.

          Please copy #{@env_example_path} to #{@env_path} and fill in the value for #{key}.

          mkdir -p #{Shellwords.shellescape(env_file_dir)} && cp #{Shellwords.shellescape(@env_example_path)} #{Shellwords.shellescape(@env_path)}
        MSG
      end

    @print_error_lambda.call(error_message)
    raise KeyError, error_message
  end

  value = env_value(key)

  if value.to_s.empty?
    empty_message = "Env var for key #{key} is set but empty. Please set a value for #{key}."
    @print_error_lambda.call(empty_message)
    raise ArgumentError, empty_message
  end

  value
end

#pr_number_or_branch_nameObject

Returns a human-readable label: "PR #123" for PR builds, or the branch name otherwise.



146
147
148
# File 'lib/fastlane/plugin/wpmreleasetoolkit/env_manager/env_manager.rb', line 146

def pr_number_or_branch_name
  pull_request_number&.then { |num| "PR ##{num}" } || branch_name
end

#pull_request_numberObject

Returns the PR number as an Integer, or nil if not running on a PR build. Buildkite sets BUILDKITE_PULL_REQUEST to 'false' (not nil) when not on a PR.



140
141
142
143
# File 'lib/fastlane/plugin/wpmreleasetoolkit/env_manager/env_manager.rb', line 140

def pull_request_number
  pr_num = ENV.fetch('BUILDKITE_PULL_REQUEST', 'false')
  pr_num == 'false' ? nil : Integer(pr_num)
end

#require_env_vars!(*keys) ⇒ Object

Use this to ensure all env vars a lane requires are set.

The best place to call this is at the start of a lane, to fail early.



113
114
115
# File 'lib/fastlane/plugin/wpmreleasetoolkit/env_manager/env_manager.rb', line 113

def require_env_vars!(*keys)
  keys.flatten.each { |key| get_required_env!(key) }
end

#restore_env!Object

Remove from ENV any keys this instance added via mutate_env: true, but only if the value is still the one we wrote. Keys that a later caller has overwritten are left untouched. Idempotent — calling more than once is safe. Used by reset! and available for callers that want to roll back manually.



67
68
69
70
# File 'lib/fastlane/plugin/wpmreleasetoolkit/env_manager/env_manager.rb', line 67

def restore_env!
  @mutations.each { |key, value| ENV.delete(key) if ENV[key] == value }
  @mutations = {}
end