-
Notifications
You must be signed in to change notification settings - Fork 9
Populate process ENV from EnvManager by default #723
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: trunk
Are you sure you want to change the base?
Changes from all commits
e2eb304
97a2002
b33ee53
e875207
3c28110
4dbda49
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -15,10 +15,22 @@ class << self | |
| end | ||
|
|
||
| # 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. | ||
| 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) } | ||
| ) | ||
|
|
@@ -31,10 +43,34 @@ def initialize( | |
| @print_warning_lambda.call("Warning: env file not found at #{@env_path}. Environment variables may not be loaded.") | ||
| end | ||
|
|
||
| # Parse rather than load so we don't mutate the global ENV. Each instance | ||
| # gets its own view, and values from the process environment (e.g. set by | ||
| # CI) still take precedence — see `env_value`. | ||
| # Always parse rather than load: it lets us track exactly which keys we | ||
| # add to `ENV` so `reset!` can undo only those, without disturbing keys | ||
| # that pre-existed in the process environment. | ||
| @loaded_env = File.exist?(@env_path) ? Dotenv.parse(@env_path) : {} | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💭 One concern, maybe a bit of an edge case: if I understand correctly, it seems I wonder if a possible solution could be to use |
||
| # 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 = {} | ||
|
|
||
| return unless mutate_env | ||
|
|
||
| @loaded_env.each do |key, value| | ||
| # No-override: anything already in `ENV` (e.g. set by CI) wins. | ||
| next if ENV.key?(key) | ||
|
|
||
| ENV[key] = value | ||
| @mutations[key] = value | ||
| end | ||
| end | ||
|
|
||
| # 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. | ||
| def restore_env! | ||
| @mutations.each { |key, value| ENV.delete(key) if ENV[key] == value } | ||
| @mutations = {} | ||
| end | ||
|
|
||
| # Use this instead of getting values from `ENV` directly. It will throw an error if the requested value is missing or empty. | ||
|
|
@@ -135,8 +171,10 @@ def self.require_env_vars!(*keys) | |
| default!.require_env_vars!(*keys) | ||
| end | ||
|
|
||
| # Clears the default instance, useful for test teardown. | ||
| # Clears the default instance, useful for test teardown. Also rolls back | ||
| # any `ENV` mutations the default instance performed via `mutate_env`. | ||
| def self.reset! | ||
| @default&.restore_env! | ||
| @default = nil | ||
| end | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This mentions
set_up/new, then saysreset!removes keys this instance added. Correct me if I'm wrong, butreset!only restores the default instance created byset_upand callers usingnewneedrestore_env!instead?