Populate process ENV from EnvManager by default#723
Conversation
PR #578 switched `EnvManager` from `Dotenv.load` to `Dotenv.parse` to gain instance isolation and clean `reset!` semantics. The trade was net negative: fastlane actions read their `default_value:` from `ENV.fetch(...)`, so without ENV mutation the loaded values are invisible to actions like `app_store_connect_api_key`, `match`, or `upload_to_testflight` — defeating the affordance `EnvManager` exists to provide. Layer parsed values into process `ENV` by default, with no-override semantics so pre-existing `ENV` entries (e.g. set by CI) still win. Track which keys we added so `reset!` can roll back precisely, without disturbing pre-existing entries. Pass `mutate_env: false` to opt out and keep the parse-only / instance-isolation semantics for callers (mostly tests) that want them. --- Generated with the help of Claude Code, https://claude.com/claude-code Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR changes EnvManager’s default behavior to once again populate the process ENV from the loaded .env file (while preserving “no-override” semantics where pre-existing ENV values win). It adds an opt-out (mutate_env: false) for callers that want the parse-only / per-instance isolation behavior introduced previously, and updates reset behavior to roll back any default-instance ENV mutations.
Changes:
- Default
EnvManagerinitialization now layers parsed.envvalues into processENV(no-override), withmutate_env: falseto opt out. - Track and roll back
ENVmutations viarestore_env!, called fromEnvManager.reset!. - Update specs to cover default mutation behavior, opt-out isolation, and rollback semantics; add a breaking-change changelog entry.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
lib/fastlane/plugin/wpmreleasetoolkit/env_manager/env_manager.rb |
Adds mutate_env defaulting to true, implements restore_env!, and makes reset! roll back default-instance mutations. |
spec/env_manager_spec.rb |
Updates and extends tests to validate default ENV mutation, opt-out behavior, and rollback/idempotency. |
CHANGELOG.md |
Documents the breaking behavior change and the mutate_env: false opt-out. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| def restore_env! | ||
| @mutated_keys.each { |key| ENV.delete(key) } | ||
| @mutated_keys = [] |
There was a problem hiding this comment.
Good catch — fixed in 97a2002.
@mutated_keys is now @mutations (a key => value hash recording what this instance wrote), and restore_env! only deletes when ENV[key] still equals the value we set. Any later overwrite by another caller is left in place. Added a regression test in spec/env_manager_spec.rb.
Posted by Claude (Opus 4.7) on behalf of @mokagio with approval.
| ### Breaking Changes | ||
|
|
||
| _None_ | ||
| - `EnvManager`: populate the process `ENV` from the loaded `.env` file by default (no-override semantics — pre-existing `ENV` values win), so fastlane actions that read their `default_value:` from `ENV` find loaded secrets without callers having to thread them through explicitly. Pass `mutate_env: false` to opt out and keep the parse-only / instance-isolation semantics introduced in [#578]. `EnvManager.reset!` now also rolls back the keys it added. [#XXX] |
`restore_env!` previously deleted every key this instance had set, even if a later caller had overwritten the value via `ENV[key] = ...`. That contradicts the intent of "remove only what this instance added" and could silently drop a caller's update. Track the value written for each mutated key and delete only when `ENV[key]` still matches. Addresses #723 (comment). --- Generated with the help of Claude Code, https://claude.com/claude-code Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
What does it do?
Flips
EnvManager's default back to populating the processENVfrom the loaded.envfile (no-override semantics — pre-existingENVwins).Adds a
mutate_env: falseopt-out for callers who want to preserve the parse-only / instance-isolation semantics introduced in #578.Why
#578 switched
EnvManagerfromDotenv.loadtoDotenv.parseto make instances independent of processENVand to givereset!something to undo.Both real wins, but the trade was net negative.
Fastlane actions look up their
default_value:viaENV.fetch(...).With ENV pristine, calls like
app_store_connect_api_key,match, andupload_to_testflightcan't see anything loaded from the.envfile — defeating the affordanceEnvManagerexists to provide.matticspace-mobile and the Gravatar SDK had been quietly depending on
Dotenv.load's side effect.The everyfirst-app TestFlight wiring rediscovered the regression first-hand.
This PR keeps the structural wins of #578 (parse + per-instance dict) but layers values into
ENVby default.reset!tracks the keys it added and removes only those, so pre-existingENVentries are never disturbed.Migration
This is technically a breaking behavior change for anyone who upgraded to a release that included #578 and depended on
ENVstaying pristine.Mitigation: pass
mutate_env: falsetoset_up(ornew) to restore the previous behavior.Worth a major version bump — happy to defer that decision to reviewers.
Checklist before requesting a review
bundle exec rubocopto test for code style violations and recommendations.specs/*_spec.rb) if applicable.bundle exec rspecto run the whole test suite and ensure all your tests pass.CHANGELOG.mdfile to describe your changes under the appropriate existing###subsection of the existing## Trunksection.MIGRATION.mdfile — pending a decision on whether this ships in a major or minor bump.