Skip to content
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

Using environment variables in app.config.ts doesn't work with eas update #2222

Open
andrejpavlovic opened this issue Feb 8, 2024 · 13 comments
Labels
needs review Issue is ready to be reviewed by a maintainer

Comments

@andrejpavlovic
Copy link

andrejpavlovic commented Feb 8, 2024

Build/Submit details page URL

No response

Summary

How do I get eas update to properly load app.config.js file that references environment variables, if those aren't available when eas update is executed?

Let's say I have an .env file with this environment variable:

MY_VALUE=test

I use this value in app.config.js for something and ensure it is not empty:

export default ({ config })=> {
  if (!process.env.MY_VALUE) {
    throw new Error(`MY_VALUE environment variable not defined`)
  }

  // ... proceed to to use MY_VALUE in the config

  return config
}

✔️ Running expo command such as yarn expo config works fine, since the environment variable is automatically loaded from .env file.

❌ However when running eas config or eas update, the error above is thrown because MY_VALUE environment value is not available.

Now, I understand that eas is not supposed to be reading .env values, but the issue here is that it's reading the expo config which uses environment variables.

So I thought adding MY_VALUE to eas.json as env value would fix the issue, but it only works for eas config. eas update doesn't read environment values even from eas.json.

If eas update doesn't read environment values from .env or from eas.json then why is it loading app.config.js which more than likely relies on environment values in order to configure expo plugins, etc.?

My workaround for now was to just load .env manually in app.config.js:

import * as dotenv from 'dotenv'

// eas doesn't load values from `.env`, so make sure they are loaded here
dotenv.config({
  path: [path.resolve(__dirname, '.env.local'), path.resolve(__dirname, '.env')],
})

export default ({ config })=> {
  if (!process.env.MY_VALUE) {
    throw new Error(`MY_VALUE environment variable not defined`)
  }

  // ... proceed to to use MY_VALUE in the config

  return config
}

Managed or bare?

managed

Environment

expo-env-info 1.2.0 environment info:
System:
OS: Windows 10 10.0.19045
Binaries:
Node: 18.18.0 - C:\Program Files\nodejs\node.EXE
Yarn: 1.22.19 - C:\Program Files (x86)\Yarn\bin\yarn.CMD
npm: 10.2.5 - C:\Program Files\nodejs\npm.CMD
Watchman: 20210110.135312.0 - C:\ProgramData\chocolatey\bin\watchman.EXE
IDEs:
Android Studio: AI-231.9392.1.2311.11330709
npmPackages:
expo: ~50.0.6 => 50.0.6
react: 18.2.0 => 18.2.0
react-dom: ^18.2.0 => 18.2.0
react-native: 0.73.4 => 0.73.4
react-native-web: ~0.19.10 => 0.19.10
Expo Workflow: bare

✔ Check Expo config for common issues
✔ Check package.json for common issues
✔ Check dependencies for packages that should not be installed directly
✔ Check for common project setup issues
✔ Check npm/ yarn versions
✔ Check for issues with metro config
✔ Check Expo config (app.json/ app.config.js) schema
✔ Check that packages match versions required by installed Expo SDK
✔ Check that native modules do not use incompatible support packages
✔ Check for legacy global CLI installed locally
✔ Check that native modules use compatible support package versions for installed Expo SDK

Error output

No response

Reproducible demo or steps to reproduce from a blank project

Description posted above.

@andrejpavlovic andrejpavlovic added the needs review Issue is ready to be reviewed by a maintainer label Feb 8, 2024
@libcthorne
Copy link

I just encountered the same issue. Thanks for the workaround for now!

@doyoonkim12345
Copy link

did you create the prebuild? i encoutered same issue, i solved by prebuild creation instead of set NODE_ENV.

@andrejpavlovic
Copy link
Author

andrejpavlovic commented Feb 13, 2024

did you create the prebuild? i encoutered same issue, i solved by prebuild creation instead of set NODE_ENV.

I don't think prebuild is relevant here, since prebuild is an expo command, and the question is really about how to access environment variables when using eas update

@islamashraful
Copy link

I was facing a similar issue, so hope I can share some insights.

I am using an expo app with different app variants [staging, production].

This is my eas config

"staging": {
    "distribution": "store",
    "env": {
      "APP_VARIANT": "staging"
    },
    "channel": "staging",
    "autoIncrement": true
  },
  "production": {
    "channel": "production",
    "autoIncrement": true
  }

And my app.config.ts is something like this

export default ({ config }: ConfigContext): ExpoConfig => {
  const isStaging = process.env.APP_VARIANT === 'staging';

  const productionConfig: ExpoConfig = {
    ...config,
    name: 'App',
    ios: {
      bundleIdentifier: 'com.xxxx.influencer',
    },
    android: {
      package: 'com.xxxx.influencer',
    },
    extra: {
      eas: {
        projectId: '8c24d46b-xxxx-45f1-xxxx-xxx571c7xxxx',
      },
      GQL_URL: 'https://gql.xxxx.com/graphql/',
    },
    updates: {
      url: 'https://u.expo.dev/xxx-xxxx-45f1-9379-dce571cxxx',
    },
    runtimeVersion: {
      policy: 'appVersion',
    },
  };

  if (isStaging) {
    return {
      ...productionConfig,
      name: '(Stg)App',
      ios: {
        ...productionConfig.ios,
        bundleIdentifier: 'com.xxxx.influencer.staging',
      },
      android: {
        ...productionConfig.android,
        package: 'com.xxxx.influencer.staging',
      },
      extra: {
        ...productionConfig.extra,
        GQL_URL: 'https://stg-gql.xxxx.com/graphql/',
      },
    };
  }

  return productionConfig;
};

For building the app (for staging) I use these commands

eas build --profile staging --platform android
eas build --profile staging --platform ios

Till this point, everything works fine. The build can properly set all environment variables, as I have specified the profile with env on my eas.json.

Also, on the app.config.ts, it can pick up the correct env[staging].

The problem happens with the eas update!

I was trying to push an update on my staging variant with the following command

eas update --branch staging --message "Message..."

Although I was sending the update to the staging branch, the new update wasn't able to pick the APP_VARIANT correctly.

Probably, it was undefined on app.config.ts, and my staging app was behaving like the production app by using my production GQL_URL: 'https://gql.xxxx.com/graphql/', endpoint.

Finally, I was able to fix that by setting the APP_VARIANT with the eas update command like this

APP_VARIANT=staging eas update --branch staging --message "Message..."

On the CI, it will be something like this

APP_VARIANT=staging npx eas-cli update --branch staging --message="`git log -1 --pretty=%B`" --non-interactive

@yonihod
Copy link

yonihod commented Mar 6, 2024

I was facing a similar issue, so hope I can share some insights.

I am using an expo app with different app variants [staging, production].

This is my eas config

"staging": {
    "distribution": "store",
    "env": {
      "APP_VARIANT": "staging"
    },
    "channel": "staging",
    "autoIncrement": true
  },
  "production": {
    "channel": "production",
    "autoIncrement": true
  }

And my app.config.ts is something like this

export default ({ config }: ConfigContext): ExpoConfig => {
  const isStaging = process.env.APP_VARIANT === 'staging';

  const productionConfig: ExpoConfig = {
    ...config,
    name: 'App',
    ios: {
      bundleIdentifier: 'com.xxxx.influencer',
    },
    android: {
      package: 'com.xxxx.influencer',
    },
    extra: {
      eas: {
        projectId: '8c24d46b-xxxx-45f1-xxxx-xxx571c7xxxx',
      },
      GQL_URL: 'https://gql.xxxx.com/graphql/',
    },
    updates: {
      url: 'https://u.expo.dev/xxx-xxxx-45f1-9379-dce571cxxx',
    },
    runtimeVersion: {
      policy: 'appVersion',
    },
  };

  if (isStaging) {
    return {
      ...productionConfig,
      name: '(Stg)App',
      ios: {
        ...productionConfig.ios,
        bundleIdentifier: 'com.xxxx.influencer.staging',
      },
      android: {
        ...productionConfig.android,
        package: 'com.xxxx.influencer.staging',
      },
      extra: {
        ...productionConfig.extra,
        GQL_URL: 'https://stg-gql.xxxx.com/graphql/',
      },
    };
  }

  return productionConfig;
};

For building the app (for staging) I use these commands

eas build --profile staging --platform android
eas build --profile staging --platform ios

Till this point, everything works fine. The build can properly set all environment variables, as I have specified the profile with env on my eas.json.

Also, on the app.config.ts, it can pick up the correct env[staging].

The problem happens with the eas update!

I was trying to push an update on my staging variant with the following command

eas update --branch staging --message "Message..."

Although I was sending the update to the staging branch, the new update wasn't able to pick the APP_VARIANT correctly.

Probably, it was undefined on app.config.ts, and my staging app was behaving like the production app by using my production GQL_URL: 'https://gql.xxxx.com/graphql/', endpoint.

Finally, I was able to fix that by setting the APP_VARIANT with the eas update command like this

APP_VARIANT=staging eas update --branch staging --message "Message..."

On the CI, it will be something like this

APP_VARIANT=staging npx eas-cli update --branch staging --message="`git log -1 --pretty=%B`" --non-interactive

I am having an issue with the build not the eas update, I am trying to set a new projectId based on configurations but it keeps failing when running the eas build worker, i thought you might have an input here

@ChromeQ
Copy link

ChromeQ commented Mar 19, 2024

Thanks for your workaround @andrejpavlovic - however I found an issue which is preventing me from continuing, I load the dotenv config using the ES6 method: import 'dotenv/config'; as described here: https://github.com/motdotla/dotenv?tab=readme-ov-file#how-do-i-use-dotenv-with-import (I'm using dotenv-vault but not sure that is relevant as I can see the vars being loaded in logs)

And for some reason the config is being called 3 times, once with the correct process.env var set and twice more with it undefined:

// app.config.ts
export default ({ config }: ConfigContext): ExpoConfig => {
  const url = `https://u.expo.dev/${process.env.EXPO_PROJECT_ID}`;
  console.log('URL', url);
  ...
};
// output
[[email protected]][INFO] Loading env from encrypted .env.vault
URL https://u.expo.dev/022xxxxx-eb2e-xxx-b078-24xxxxxfb2d
URL https://u.expo.dev/undefined
URL https://u.expo.dev/undefined

It looks like you are using a dynamic configuration! Learn more: https://docs.expo.dev/workflow/configuration/#dynamic-configuration-with-appconfigjs)
Add the following EAS Update key-values to the project app.config.js:
Learn more: https://expo.fyi/eas-update-config

EAS is only being called once with this command: npx eas-cli@latest update --auto --non-interactive

Interestingly, when using your method of import * as dotenv from 'dotenv'; and calling dotenv.config() outside the scope of the config function (I don't require the config options with my dotenv-vault) then it seems to call it many more times but it does actually set the env var correctly every time:

// output
[[email protected]][INFO] Loading env from encrypted .env.vault
URL https://u.expo.dev/022xxxxx-eb2e-xxx-b078-24xxxxxfb2d
[[email protected]][INFO] Loading env from encrypted .env.vault
URL https://u.expo.dev/022xxxxx-eb2e-xxx-b078-24xxxxxfb2d
[[email protected]][INFO] Loading env from encrypted .env.vault
URL https://u.expo.dev/022xxxxx-eb2e-xxx-b078-24xxxxxfb2d
[[email protected]][INFO] Loading env from encrypted .env.vault
URL https://u.expo.dev/022xxxxx-eb2e-xxx-b078-24xxxxxfb2d
[[email protected]][INFO] Loading env from encrypted .env.vault
URL https://u.expo.dev/022xxxxx-eb2e-xxx-b078-24xxxxxfb2d
[[email protected]][INFO] Loading env from encrypted .env.vault
URL https://u.expo.dev/022xxxxx-eb2e-xxx-b078-24xxxxxfb2d
- Exporting...
[expo-cli] [[email protected]][INFO] Loading env from encrypted .env.vault
- Exporting...
[expo-cli] URL https://u.expo.dev/022xxxxx-eb2e-xxx-b078-24xxxxxfb2d
- Exporting...
[expo-cli] [[email protected]][INFO] Loading env from encrypted .env.vault
- Exporting...
[expo-cli] URL https://u.expo.dev/022xxxxx-eb2e-xxx-b078-24xxxxxfb2d
- Exporting...
[expo-cli] [[email protected]][INFO] Loading env from encrypted .env.vault
- Exporting...
[expo-cli] URL https://u.expo.dev/022xxxxx-eb2e-xxx-b078-24xxxxxfb2d
[expo-cli] Starting Metro Bundler

It would be nice for this to work as expected by EAS.

@ChromeQ
Copy link

ChromeQ commented Mar 19, 2024

I have solved this issue for me anyway by utilising dotenvx which only injects the variables once and they persist no matter how many times eas calls the config function - and it keeps the config file cleaner.

This was discovered as dotenv-vault didn't play nicely with my .env.local file on development.

Summary:
Don't change your app.config.js/ts - don't import dotenv
Install dotenvx (and maybe remove dotenv) and use it to run eas:

npx dotenvx run -- npx eas-cli@latest update --auto --non-interactive

// Or simpler when using the (expo-gitub-action)[https://github.com/expo/expo-github-action/tree/v8/]
npx dotenvx run -- eas update --auto --non-interactive

If you need additional env vars passed in then set them before dotenvx:

APP_PROFILE=preview npx dotenvx run -- eas update --auto --non-interactive

// Not like this
npx dotenvx run -- APP_PROFILE=preview eas update --auto --non-interactive

Hope this helps someone until Expo/EAS fix this as it is a bit annoying and 🤯

@AdamGerthel
Copy link

AdamGerthel commented Mar 22, 2024

Using dotenv cli to load the env variables is the only viable solution to this problem. The documentation is really confusing on this topic, especially since "update" is an EAS command, you'd expect it to use variables from eas.json at least, but no - it doesn't load variables from neither eas.json nor env files.

See #1265 (comment)

Here's how I've done it (with dotenv-cli as a project dependency):

yarn dotenv -e .env -e .env.<environment> -- npx eas-cli@latest update --channel <channel>

@ChromeQ
Copy link

ChromeQ commented Mar 22, 2024

@AdamGerthel if you use npx then you don't need to install it as a project dependency, it will download it and use it on demand.
eas.json is not very useful in my opinion as there are no dynamic values in json

@DeepSwami
Copy link

Thanks for your workaround @andrejpavlovic - however I found an issue which is preventing me from continuing, I load the dotenv config using the ES6 method: import 'dotenv/config'; as described here: https://github.com/motdotla/dotenv?tab=readme-ov-file#how-do-i-use-dotenv-with-import (I'm using dotenv-vault but not sure that is relevant as I can see the vars being loaded in logs)

And for some reason the config is being called 3 times, once with the correct process.env var set and twice more with it undefined:

// app.config.ts
export default ({ config }: ConfigContext): ExpoConfig => {
  const url = `https://u.expo.dev/${process.env.EXPO_PROJECT_ID}`;
  console.log('URL', url);
  ...
};
// output
[[email protected]][INFO] Loading env from encrypted .env.vault
URL https://u.expo.dev/022xxxxx-eb2e-xxx-b078-24xxxxxfb2d
URL https://u.expo.dev/undefined
URL https://u.expo.dev/undefined

It looks like you are using a dynamic configuration! Learn more: https://docs.expo.dev/workflow/configuration/#dynamic-configuration-with-appconfigjs)
Add the following EAS Update key-values to the project app.config.js:
Learn more: https://expo.fyi/eas-update-config

EAS is only being called once with this command: npx eas-cli@latest update --auto --non-interactive

Interestingly, when using your method of import * as dotenv from 'dotenv'; and calling dotenv.config() outside the scope of the config function (I don't require the config options with my dotenv-vault) then it seems to call it many more times but it does actually set the env var correctly every time:

// output
[[email protected]][INFO] Loading env from encrypted .env.vault
URL https://u.expo.dev/022xxxxx-eb2e-xxx-b078-24xxxxxfb2d
[[email protected]][INFO] Loading env from encrypted .env.vault
URL https://u.expo.dev/022xxxxx-eb2e-xxx-b078-24xxxxxfb2d
[[email protected]][INFO] Loading env from encrypted .env.vault
URL https://u.expo.dev/022xxxxx-eb2e-xxx-b078-24xxxxxfb2d
[[email protected]][INFO] Loading env from encrypted .env.vault
URL https://u.expo.dev/022xxxxx-eb2e-xxx-b078-24xxxxxfb2d
[[email protected]][INFO] Loading env from encrypted .env.vault
URL https://u.expo.dev/022xxxxx-eb2e-xxx-b078-24xxxxxfb2d
[[email protected]][INFO] Loading env from encrypted .env.vault
URL https://u.expo.dev/022xxxxx-eb2e-xxx-b078-24xxxxxfb2d
- Exporting...
[expo-cli] [[email protected]][INFO] Loading env from encrypted .env.vault
- Exporting...
[expo-cli] URL https://u.expo.dev/022xxxxx-eb2e-xxx-b078-24xxxxxfb2d
- Exporting...
[expo-cli] [[email protected]][INFO] Loading env from encrypted .env.vault
- Exporting...
[expo-cli] URL https://u.expo.dev/022xxxxx-eb2e-xxx-b078-24xxxxxfb2d
- Exporting...
[expo-cli] [[email protected]][INFO] Loading env from encrypted .env.vault
- Exporting...
[expo-cli] URL https://u.expo.dev/022xxxxx-eb2e-xxx-b078-24xxxxxfb2d
[expo-cli] Starting Metro Bundler

It would be nice for this to work as expected by EAS.

import * as dotenv from 'dotenv'; dotenv.config();

The above approach solved my issue. the last call was not persisting env variables.

Thank you so much for this workaround😊

@sprutner
Copy link

@ChromeQ Thanks!!!! That helped me after a lot of frustration!

@KirschX
Copy link

KirschX commented Jun 27, 2024

npx dotenvx run -- eas update --auto --non-interactive

This is the easiest and cleanest solution for me. Thank you so much!

@Guiles92
Copy link

Guiles92 commented Aug 23, 2024

I encountered the same issue. Using a TypeScript app.config file and an env file named .env.local, I got errors when trying to build or run npx expo-doctor, saying it couldn't find an env variable. The fix: add import * as dotenv from 'dotenv'; dotenv.config(); to the app.config.ts file. There's a catch though - either rename the file to just .env, or specify the file path in the config function like this: dotenv.config({ path: '.env.local' });.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
needs review Issue is ready to be reviewed by a maintainer
Projects
None yet
Development

No branches or pull requests