New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

@thinkmill/devops-env-vars

Package Overview
Dependencies
Maintainers
12
Versions
14
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@thinkmill/devops-env-vars - npm Package Compare versions

Comparing version 2.0.0 to 3.0.0

.eslintrc

40

index.js

@@ -9,23 +9,2 @@ 'use strict';

// Configure the VPC's in different AWS regions
const AWS_VPCS = [
// Thinkmill Sydney (ap-southeast-2)
{ cidr: '10.117.0.0/16', env: 'live' },
{ cidr: '10.118.0.0/16', env: 'staging' },
{ cidr: '10.119.0.0/16', env: 'testing' },
// Also.. 10.97.0.0/16 Blueshyft XIT
// Thinkmill Ireland (eu-west-1)
{ cidr: '10.130.0.0/16', env: 'live' },
{ cidr: '10.131.0.0/16', env: 'staging' },
{ cidr: '10.132.0.0/16', env: 'testing' },
// blueshyft Sydney (ap-southeast-2)
{ cidr: '10.20.0.0/16', env: 'live' },
{ cidr: '10.21.0.0/16', env: 'staging' },
{ cidr: '10.22.0.0/16', env: 'testing' },
// Also.. 10.30.0.0/16 XIT
];
// The different environments we support

@@ -54,5 +33,9 @@ const SUPPORTED_ENVS = ['live', 'staging', 'testing', 'development'];

/*
Figures out which APP_ENV to use, based on the value supplied, the supported envs and the servers IP address
// Figures out which APP_ENV to use, based on the value supplied, the supported envs and the servers IP address
function determineAppEnv (_processAppEnv) {
networksArray is expected in the format..
[{ cidr: '72.67.5.0/16', env: 'live' }, { cidr: '72.68.5.0/16', env: 'staging' }, { cidr: '72.69.5.0/16', env: 'testing' }]
*/
function determineAppEnv (_processAppEnv, networksArray = []) {
const debug = debugLib('@thinkmill/devops-env-vars:determineAppEnv');

@@ -71,9 +54,9 @@

const serverIp = getServerIp();
const possibleVpcs = AWS_VPCS.filter(vpc => new Netmask(vpc.cidr).contains(serverIp) && SUPPORTED_ENVS.includes(vpc.env));
if (possibleVpcs.length > 1) throw new Error(`Server IP matches > 1 potential VPC: ${possibleVpcs.map(vpc => (`${vpc.env} ${vpc.cidr}`)).join('; ')}`);
const candidateNetworks = networksArray.filter(network => new Netmask(network.cidr).contains(serverIp) && SUPPORTED_ENVS.includes(network.env));
if (candidateNetworks.length > 1) throw new Error(`Server IP matches > 1 potential network: ${candidateNetworks.map(network => (`${network.env} ${network.cidr}`)).join('; ')}`);
let envRtn = 'development';
if (possibleVpcs.length === 1) {
debug(`APP_ENV determined from server IP as ${chalk.cyan(possibleVpcs[0].env)} (${chalk.green(serverIp)} is within ${chalk.green(possibleVpcs[0].cidr)})`);
envRtn = possibleVpcs[0].env;
if (candidateNetworks.length === 1) {
debug(`APP_ENV determined from server IP as ${chalk.cyan(candidateNetworks[0].env)} (${chalk.green(serverIp)} is within ${chalk.green(candidateNetworks[0].cidr)})`);
envRtn = candidateNetworks[0].env;
}

@@ -174,3 +157,2 @@

supportedEnvs: SUPPORTED_ENVS,
awsVpcs: AWS_VPCS,
};
{
"name": "@thinkmill/devops-env-vars",
"version": "2.0.0",
"version": "3.0.0",
"description": "Helper functions that encapsulate our treatment of environment vars for KeystoneJS apps",
"main": "index.js",
"license": "MIT",
"scripts": {

@@ -25,4 +26,5 @@ "release-patch": "npm version patch && git push && git push --tags",

"devDependencies": {
"eslint": "^3.2.2"
"eslint": "^3.10.0",
"eslint-config-keystone": "^3.0.0"
}
}

@@ -10,12 +10,5 @@ Devops: Environment Variables

The code block below demonstrates how this library should be used in a modern KeystoneJS app.
It's based on the [`config.js` in the `admyt-platform` codebase](https://github.com/Thinkmill/admyt-platform/blob/develop/config.js).
The code block below demonstrates how this library can be used in a fictional KeystoneJS app, the `chumble-platform`.
```javascript
'use strict';
// This doesn't actually stop the config from being loaded onto clients, it's just a warning for developers
if (typeof window !== 'undefined') throw new Error(`You definitely shouldn't require ./config on the client`);
```js
const envLib = require('@thinkmill/devops-env-vars');

@@ -26,4 +19,10 @@ const path = require('path');

// This doesn't actually stop the config from being loaded onto clients, it's just a warning for developers
if (typeof window !== 'undefined') throw new Error(`You definitely shouldn't require ./config on the client`);
// Determine the current APP_ENV
const APP_ENV = envLib.determineAppEnv(process.env.APP_ENV);
const APP_ENV = envLib.determineAppEnv(
process.env.APP_ENV,
[{ cidr: '72.67.5.0/16', env: 'live' }, { cidr: '72.68.5.0/16', env: 'staging' }, { cidr: '72.69.5.0/16', env: 'testing' }],
);

@@ -43,12 +42,12 @@ // Convert the APP_ENV to some handy flags

// If not supplied, Keystone will default to localhost (ie. in dev)
MONGO_URI: { required: flags.IN_PRODUCTION, default: 'mongodb://localhost/admyt-platform' },
MONGO_URI: { required: flags.IN_PRODUCTION, default: 'mongodb://localhost/chumble-platform' },
// Used to encrypt user cookies; not important in dev
JWT_TOKEN_SECRET: { required: flags.IN_PRODUCTION, default: 'gottalovejwts' },
JWT_TOKEN_SECRET: { required: flags.IN_PRODUCTION, default: 'dev-secret-goes-here' },
// When not live, allow to be defaulted to a test key
MANDRILL_API_KEY: { required: flags.IN_PRODUCTION, default: 'testkeygoeshere' },
MANDRILL_API_KEY: { required: flags.IN_PRODUCTION, default: 'test-key-goes-here' },
// Cloudinary creds; used by Types.CloudinaryImage
CLOUDINARY_URL: { required: flags.IN_PRODUCTION, default: 'cloudinary://862989489411169:Wp74nFvzkSPGkQHgtCBH7wN4Yik@thinkmill' },
CLOUDINARY_URL: { required: flags.IN_PRODUCTION, default: 'cloudinary://012345678902345:9FDRoGKGpYZVASNDwyTdJRKOIku@thinkmill' },

@@ -69,8 +68,2 @@ // S3 credentials; used by Types.S3File

// For the eCentric payment gateway
ECENTRIC_MERCHANT_ID: { required: flags.IN_PRODUCTION },
// Recreate the entire DB
RESET_DATABASE: { required: false, default: false, type: Boolean },
// What port should the webserver bind to

@@ -81,13 +74,30 @@ PORT: { required: flags.IN_PRODUCTION, default: 3000, type: Number },

// Set any other static or derived vars (that don't need to be overridden by .env or process vars)
config.OTHER_IMPORTANT_VARS = 'blah blah'
config.FORCE_SSL = flags.IN_PRODUCTION;
// Support details
config.FROM_EMAIL = 'support@chumble.com.au';
config.FROM_NAME = 'Chumble Support';
config.SUPPORT_PHONE_NUMBER = '1800 422 554';
// ..
// Where should we address the plumbus API
config.PLUMBUS_API_URL = ({
live: 'https://api.plumbus.net.au',
staging: 'https://api-staging.plumbus.net.au',
testing: 'https://api-testing.plumbus.net.au',
development: 'http://localhost:7634', // Use a local stub server in dev
})[APP_ENV];
// Lock and export the config vars
// Are we disabling developer authentication to developer endpoints?
config.ALLOW_UNAUTHENTICATED_ACCESS_TO_DEVELOPER_ENDPOINTS = IN_DEVELOPMENT;
// Can calls to the /ploobis/create endpoint specify their own fleeb?
config.ALLOW_FLEEB_TO_BE_SPECIFIED_ON_CREATE = true;
// Can ploobis be reset even after email generation has commenced
config.ALLOW_PLOOBIS_RESET_AFTER_EMAIL_GENERATION = !IN_LIVE;
// Freeze and export the config vars
module.exports = Object.freeze(config);
```
Lets step though the code above in detail.
Lets step though the code above in detail..

@@ -99,7 +109,6 @@

Since some of the config variables are also often needed client side, there's a temptation to simply require `config.js` there too.
This is a terrible, terrible idea; it usually exposes security-sensitive values to the end user.
The `config.js` file should simply never leave the server.
This is a terrible idea for, hopefully, obvious reasons; it almost certainly exposes security-sensitive values to the end user.
We put this warning in place as a last ditch effort to prevent accidental inclusion.
```javascript
```js
// This doesn't actually stop the config from being loaded onto clients, it's just a warning for developers

@@ -113,12 +122,13 @@ if (typeof window !== 'undefined') throw new Error(`You definitely shouldn't require ./config on the client`);

First, we call `determineAppEnv()`, which determines the current `APP_ENV` by inspecting the servers IP address the `APP_ENV` value supplied by `process.env` (if present):
We call `determineAppEnv()` to determines the current `APP_ENV`.
```javascript
```js
// Determine the current APP_ENV
const APP_ENV = envLib.determineAppEnv(process.env.APP_ENV);
const APP_ENV = envLib.determineAppEnv(
process.env.APP_ENV,
[{ cidr: '72.67.5.0/16', env: 'live' }, { cidr: '72.68.5.0/16', env: 'staging' }, { cidr: '72.69.5.0/16', env: 'testing' }],
);
```
It inspects the servers IP address the `APP_ENV` value supplied by `process.env` (if present).
This determination is based on the IP address ranges we use for VPCs in our deployed regions,
(documented in the Thinkmill Wiki)[https://github.com/Thinkmill/wiki/blob/master/infrastructure/ip-addresses.md].
The valid `APP_ENV` are:

@@ -141,5 +151,3 @@

This may not hold for all apps, especially older apps created before our `APP_ENV` usage was codified.
`envLib.buildAppFlags(APP_ENV)`

@@ -150,3 +158,3 @@ --------------------------------------------------------------------------------

```javascript
```js
// Convert the APP_ENV to some handy flags

@@ -163,3 +171,3 @@ const flags = envLib.buildAppFlags(APP_ENV);

```javascript
```js
console.log(flags);

@@ -173,5 +181,5 @@ // { IN_LIVE: false, IN_STAGING: true, IN_TESTING: false, IN_DEVELOPMENT: false, IN_PRODUCTION: true }

Next, standard practice is to seek out a `.env` file in the directory above the application root, named for the current `APP_ENV`:
Standard practice is to seek out a `.env` file in the directory above the application root, named for the current `APP_ENV`:
```javascript
```js
// Attempt to read the local .env file for this APP_ENV

@@ -181,5 +189,6 @@ if (!flags.IN_DEVELOPMENT) dotenv.config({ path: path.resolve(`../${APP_ENV}.env`) });

This file should contain any credentials, settings, etc. that are required for the environment but too sensitive to store in the codebase.
Mandrill API keys, merchant account credentials, live Mongo connection URIs, etc. might be required for a live system but generally aren't needed in development.
As such, the code above skips this step when `IN_DEVELOPMENT` is true.
This file should contain any variables required for the environment but security sensitive, so not store in the repo.
Eg. Mandrill API keys, merchant account credentials, Mongo DB URIs, etc.
Often these can be defaulted in development environments.
The code above skips this step when `IN_DEVELOPMENT` is true.

@@ -189,3 +198,5 @@ If the `.env` file isn't found a warning will be printed to `stderr` but the app will continue to load.

**The `dotenv` package loads these variables directly into the `process.env` scope.**
**IMPORTANT:**
The `dotenv` package loads these variables directly into the `process.env` scope.
This is the default behaviour of `dotenv` and actually pretty useful if you have variables used by packages that don't accept values any other way.

@@ -200,3 +211,3 @@ In it's standard usage, no other part of this process alters the `process.env` scope; we mostly work out of the `config` object, created next.

```javascript
```js
// Extract the vars defined from process.env and apply validation and defaults

@@ -220,5 +231,7 @@ const config = envLib.mergeConfig(APP_ENV, flags, process.env, {

If supplied, the value given by the environment will be interpreted as this type.
If an appropriate value can't be unambiguously determined (ie. a value of "coffee" suppled for a `Boolean` value) an error will be thrown.
If an appropriate value can't be unambiguously determined (eg. a value of "coffee" suppled for a `Boolean` value) an error will be thrown.
As noted above, **the `mergeConfig()` function does not modify the `process.env` scope**.
**IMPORTANT:**
As noted above, the `mergeConfig()` function does not modify the `process.env` scope.
Variables that are defaulted based on the validation rules supplied will only exist in the object returned by `mergeConfig()`.

@@ -234,57 +247,18 @@

They're usually either constants or values that are derived from the other environment variables.
Some examples, adapted from various codebases, are included below.
### Static Values
### Examples
Values that are constant for now but may change in future. Eg..
Support contact details:
SodaKING product pricing:
```javascript
config.CANISTER_EXCHANGE_PRICE_PER_UNIT_IN_CENTS = 1895;
config.CANISTER_SELL_PRICE_PER_UNIT_IN_CENTS = 4495;
```js
// Support details
config.FROM_EMAIL = 'support@chumble.com.au';
config.FROM_NAME = 'Chumble Support';
config.SUPPORT_PHONE_NUMBER = '1800 422 554';
```
Blueshyft support contact details:
An the URL of an external system based on the current `APP_ENV`:
```javascript
config.FROM_EMAIL = 'support@blueshyft.com.au';
config.FROM_NAME = 'Blueshyft Support';
config.SUPPORT_PHONE_NUMBER = '1800 817 483';
```
### Addressing External Systems
Many (all?) Thinkmill apps rely on external systems that differ between environments (`APP_ENV`).
This is especially true in for blueshyft, where requests often require the cooperation of shared
internal services (such as the core, transaction engine, etc) and external services (such as remote partner APIs).
Since both these approaches add values directly to the config object (without using `mergeConfig()`),
values set in this way can't be overridden/set without code changes.
#### blueshyft Apps
For the blueshyft network of apps, the
[`@thinkmill/blueshyft-network` package](https://www.npmjs.com/package/@thinkmill/blueshyft-network)
was developed to centralise the addressing of apps across environments.
Usage of the package looks like this:
```javascript
const network = require('@thinkmill/blueshyft-network');
// Pull in any vars we want for the network config (for this APP_ENV) and merge them into our config
config = Object.assign(config, network.getVars(APP_ENV, [
'CORE_API_URL',
'PCA_TRANSACTIONS_API_URL',
'TLS_ECOSYSTEM',
]));
```
See the [package docs](https://www.npmjs.com/package/@thinkmill/blueshyft-network) for details.
#### Non-blueshyft Apps
In non-blueshyft systems, an implementation pattern has evolved to define a set of values while maintaining readability.
```javascript
```js
// Where should we address the plumbus API
config.PLUMBUS_API_URL = ({

@@ -294,21 +268,17 @@ live: 'https://api.plumbus.net.au',

testing: 'https://api-testing.plumbus.net.au',
development: 'http://localhost:7634', // Plumbus stub server
development: 'http://localhost:7634', // Use a local stub server in dev
})[APP_ENV];
```
It can be useful to control specific functionality with feature flags:
### Feature Flags
It's often useful to control specific code branches with individual flags.
These examples taken from the `blueshyft-transactions-api` codebase:
```javascript
```js
// Are we disabling developer authentication to developer endpoints?
config.ALLOW_UNAUTHENTICATED_ACCESS_TO_DEVELOPER_ENDPOINTS = IN_DEVELOPMENT;
// Can calls to the /sweeps/create end point specify the sweepday used or do we exclusively rely on getNextSweepday()
config.ALLOW_SWEEPDAY_TO_BE_SPECIFIED_ON_CREATE = true;
// Can calls to the /ploobis/create endpoint specify their own fleeb?
config.ALLOW_FLEEB_TO_BE_SPECIFIED_ON_CREATE = true;
// Can sweeps be 'reset' after email generation has started
config.ALLOW_RESET_AFTER_EMAIL_GENERATION = !IN_LIVE;
// Can ploobis be reset even after email generation has commenced
config.ALLOW_PLOOBIS_RESET_AFTER_EMAIL_GENERATION = !IN_LIVE;
```

@@ -320,10 +290,9 @@

The final lines in our example export the `config` object we've created for use by the app after
[freezing](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze) it.
This prevents any other part of the application from accidenally making changes to this object.
In this example we [freeze](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze)
the config object before exporting it for use in our app.
This goes some way towards preventing other parts of the application from unintentionally setting config values.
```javascript
// Lock and export the config vars
```js
// Freeze and export the config vars
module.exports = Object.freeze(config);
```
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc