Skip to content

Instantly share code, notes, and snippets.

@BennyG93
Created May 2, 2020 18:13
Show Gist options
  • Save BennyG93/d81346a4cd1091f1563af3b25f3421af to your computer and use it in GitHub Desktop.
Save BennyG93/d81346a4cd1091f1563af3b25f3421af to your computer and use it in GitHub Desktop.

Managing Environments

While doing my normal googling to figure out how other people were managing their config in their react native projects, I came across two seemingly popular methods that I unfortunately just did not click with. So I ended up creating my own workflow for managing and changing between environments.

React-native-config

Firstly I discovered react-native-config, it seemed great and boasted a 12-factor approach, however it just seemed a bit complex for what I was looking for (called me lazy) I didn't want configure something in 2 different way for both iOS and Android.

React-native-dotenv

Secondly appeared react-native-dotenv which seemed much easier to set up and get going, so thats exactly what I did... and thats where it went wrong. I was trying to test changing the API endpoint used in the build, and thats when I discovered that after you make a change to the desired variable, you must also make a change to the file in your codebase which imports this variable for the affect to take place. This was something I was simply not prepared to live with!

JSON + SOPS

So I thought about what I wanted to achieve in my quest for config management, cross platform, simplicity, and ability to configure any number of environments... and an added bonus of security if needed.

Solution Overview

JSON would be used to describe an environment using key value notations, while SOPS would be used to encrypt and decrypt sensitive config values which can be safely pushed to git. Example json config:

{
  "API_KEY": "KgGLWnjW5qhW63YaTbe9"
  "API_URL": "http://api.dev.cartelwars.co",
}

Create a centralised config module within the app which imports the JSON config file, and export the env vars which can be within the app at any point. The reason we create this config module, instead of importing the config file directly, is so the we have 1 single entry point for the variables where we can manage setting defaults.

Example config.js:

import ENV from './environment.json';

API_KEY = ENV.API_KEY
API_URL = ENV.API_URL || localhost:3000 // setting default value

export default {
  API_KEY,
  API_URL
};

Create a directory structure to separate your different environment files, and store your secret values in a SOPS encrypted file. Take the following example directory structure.

my_project/
├── Environments/
│   ├── dev/
|   |   ├── secrets.json
|   |   └── values.json
│   ├── stage/
│   └── prod/
└── src/
    ├── app.js
    ├── config.js
    └── environment.json

Glue it all together with a script which will firstly decrypt the secrets.yaml into plain text, then join the contents of secrets.yaml and values.yaml together into a single JSON payload and insert it into the src/environment.json file where the config.js module can import the values for a build. I created a new directory and file from the root of the project my_project/scripts/set-env.sh. I opted to use Bash for the language as its simple and portable.

Example command to set environment vars (it takes one argument of the env name):

./scripts/set-env.sh dev

Example set-env.sh

SCRIPT_PATH=$0
ENV=$1
CONFIG_PATH="$(dirname $(dirname $0) )/src/environment.json"
ENVIRONMENTS_PATH="$(dirname $(dirname $0) )/environments/${ENV}"

# Use SOPS to decrypt the secrets in place
sops -d -i ${ENVIRONMENTS_PATH}/secrets.json

# Use jq to combine all the environment json files into one json file in the config path
jq -s 'reduce .[] as $item ({}; . * $item)' ${ENVIRONMENTS_PATH}/* > ${CONFIG_PATH}

# Use SOPS to re-encrypt the secrets file again
sops -e -i ${ENVIRONMENTS_PATH}/secrets.json

And thats it. Now whenever you want to your environment vars, you just need to create the var files and run the script, from there your app does the rest.

High level workflow

The high level work flow is as follows:

  1. Create a new environment sub directory containing a values.json and secrets.json with your non-sensitive and sensitive environment vars.
  2. Run the set-env.sh script to load your new environment vars to environment.json
  3. Start your app in your preferred way, probably with yarn yarn start
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment