goatandsheep/react-native-dotenv

iOS release build: .env.production.local file does not overwrite variables defined in base .env file

zarv1k opened this issue ยท 27 comments

  • Asked question in discussions
  • Tried the troubleshooting Wiki
  • Followed the migration Wiki

In iOS release build, variables defined in base .env file aren't overwritten by variables in .env.production.local
Reproduces only for iOS release builds. On Android there is no any issue.

To Reproduce

.env file:

SOME_VAR1=var1

.env.production file:

SOME_VAR2=var2

.env.production.local file:

SOME_VAR1=overwrite-var1
SOME_VAR2=overwrite-var2

Expected behavior
console.log(SOME_VAR1); // overwrite-var1
console.log(SOME_VAR2); // overwrite-var2

Actual behavior Android (release)
console.log(SOME_VAR1); // overwrite-var1
console.log(SOME_VAR2); // overwrite-var2

Actual behavior iOS (release)
console.log(SOME_VAR1); // var1 - but it should be overwritten by value in .env.production.local
console.log(SOME_VAR2); // overwrite-var2

RN: 0.70.6
react-native-dotenv: 3.4.2, 3.4.6

babel.config.js:

plugins:[
//...
  [
      'module:react-native-dotenv',
      {
        moduleName: '@env',
        path: '.env',
        safe: false,
        allowUndefined: true,
      },
   ],
]

Hey, thank you for opening this issue! ๐Ÿ™‚ To boost priority on this issue and support open source please tip the team at https://issuehunt.io/r/goatandsheep/react-native-dotenv/issues/393

A bit more info:

  • I don't use cache, it happens in CI that always starts a build from scratch by cloning repo into clean folder, then install npm deps etc.
  • I don't use APP_ENV, but I tried it and it doesn't help

@goatandsheep I do use XCode schemes + build configurations like described in https://shockoe.com/ideas/development/how-to-setup-configurations-and-schemes-in-xcode/, but I don't set NODE_ENV anywhere - just didn't find where should I do that. But anyway, after some experiments I realized that .env.production.* files are used (so NODE_ENV=production in release configurations) like in issue description, but they aren't overwrite vars defined in base .env file.

Also I didn't give Full Disk Access for XCode, like described in https://github.com/goatandsheep/react-native-dotenv/wiki/Multi-env-troubleshooting#xcode, but I don't think it matters for this case.

Thinking about giving Full Disk Access for XCode, but I don't understand for what case should I do that? Moreover I use cli for building apps with (XCode is not even opened).

Just tried to change set up for all release build configurations in XCode: for every Release build configuration added NODE_ENV=production user defined setting.

Also gave Full Disk Access for XCode

Nothing helps, unfortunately :(

I know you're actively on this but I'll have to think longer on this. and also will need to improve some of the docs on caching and switching environments.

I don't think it's some cache, but who knows....

I wipe out the repo folder before build in CI, clone RN app repo again, yarn install, xcodebuild clean..., xcodebuild archive..., so metro starts on every build by "Start Packager" script in XCode build phase that looks like the following:

export RCT_METRO_PORT="${RCT_METRO_PORT:=8081}"
echo "export RCT_METRO_PORT=${RCT_METRO_PORT}" > "${SRCROOT}/../node_modules/react-native/scripts/.packager.env"
if [ -z "${RCT_NO_LAUNCH_PACKAGER+xxx}" ] ; then
  if nc -w 5 -z localhost ${RCT_METRO_PORT} ; then
    if ! curl -s "http://localhost:${RCT_METRO_PORT}/status" | grep -q "packager-status:running" ; then
      echo "Port ${RCT_METRO_PORT} already in use, packager is either not running or not running correctly"
      exit 2
    fi
  else
    open "$SRCROOT/../node_modules/react-native/scripts/launchPackager.command" || echo "Can't start packager automatically"
  fi
fi

It doesn't work if Metro is started with the build and runs on another shell:

APP_ENV=staging react-native run-ios
APP_ENV=staging react-native run-android

It works if Metro is started alone

APP_ENV=staging react-native start
johnf commented

I have a strong suspicion about what is happening here but I don't know why.

I added a bunch of console.log into react-native-dotenv. I discovered that everything in .env gets loaded into the environment. i.e. it exists in process.env when the babel transforms run.

So anything in the files is ignored because it exists in the environment.

I'm trying to work out what on the code side is pulling this into the environment.

I'm fairly sure it's code as my code push build, which calls metro directly, isn't affected.

johnf commented

I've worked this out for my particular use case.
I'm using sentry. sentry-cli gets called as part of the xCode build process. Sentry supports .env files to load in things like SENTRY_DSN.
You can disable it by setting SENTRY_LOAD_DOTENV=0

Hi @johnf ! Yes that's exactly how this is supposed to work. Sorry if there's been confusion and that you had to do all that digging.

johnf commented

@goatandsheep Do you think it's worth adding an entry to the README specifically about sentry? I'm happy to create a PR

I've worked this out for my particular use case.

I'm using sentry. sentry-cli gets called as part of the xCode build process. Sentry supports .env files to load in things like SENTRY_DSN.

You can disable it by setting SENTRY_LOAD_DOTENV=0

I'm also using sentry-cli, so I'll try to set SENTRY_LOAD_DOTENV=0 and inform here

Hi @johnf I suppose that should be fine. Usually for specific use cases, I prefer to move it to the Wiki as the README is getting quite long. Do you think that this is prohibitive to people knowing how to use the library?

I have used Sentry before, but haven't setup the DSN before so looked on this page https://docs.sentry.io/product/cli/configuration/
I don't think this is a good recommendation so I don't think I should approve merging this to the README. My recommendation is to not push your keys to git and instead use the dashboards. This library is supposed to get the keys from the environment which is what the DSN does
image
Have you tried this out?

johnf commented

@goatandsheep What I want to add (wiki could be OK) is a warning about sentry.

I am setting SENTRY_DSN in GitHub Action environment variables, but this happened to me.

My .env looks like this (simplified example)

APP_VERSION="git"

It's committed to git and will be loaded in on developer machines. In the burger menu Version: git is displayed as the version of the app.

As part of the CI process, I grab the git tag being processed and write the following to .env.local

APP_VERSION="1.4.1"

This works without any issues for my android builds. But for my ios builds, I was getting Version: git in my production app.

What was happening was this:

  • Sentry overrides the code build process in Xcode to build the JS bundle and upload the source files
  • It runs sentry-cli react-native xcode ../node_modules/react-native/scripts/react-native-xcode.sh
  • As part of this process, it has its own internal dotenv (using the go library, I believe)
  • This loads .env but not .env.local and sets the environment up for react-native-xcode.sh

This means when react-native-dotenv runs you have process.env.APP_ENV set to git. Most importantly, Sentry ignores .env.local.

This affects anyone who

  • Uses sentry in their react-native app
  • Has a variable in .env
  • Then changes it in .env.local

The env.local override will be ignored by react-native-dotenv because Sentry already loaded it into the environment.

From my perspective, this is completely unexpected behaviour from Sentry. I would never have expected that it supports .env files, and you must read the documentation thoroughly and deeply to discover it.

So perhaps we can add a one-liner to the readme like

NOTE: If environment variables are not loading correctly and use are using Sentry refer to WIKIPAGE

The wiki page could then dive into something like my explanation above and how to use SENTRY_DOTENV_LOAD=0

Oh interesting. This happens for local Sentry not just on the server. That is really bizarre. Yes I'm happy to put this on the wiki

johnf commented

Yeah it happens where ever you're running sentry-cli to upload the artifacts.

I'll throw some text together and send it across for you to add a little later

Btw instead of setting to .env.local why don't you set the value in your environment variables in shell in the CI script. Shell variables take precedence.

stale commented

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

I can confirm that env variables are only overwritten on android and not on ios. The behavior is different. If a var exists in .env AND in .env.production, when building for production the .env.production won't be used on ios where it will on Android

stale commented

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

wiulma commented

same issue here. do you have any suggestions about how to fix it or any workaround? thanks.

I am not sure if it will help you but I am just checking my old git commit to what files I changed to make it work

I modified or created only these files:

  1. .env
  2. .env.development
  3. babel.config.js include 'module:react-native-dotenv',

Now when I run the the app in dev mode react-native run-ios then it simply picks the variables from .env.development but when I create a release build it takes the variables from .env files.