Skip to content
This repository has been archived by the owner on Mar 4, 2022. It is now read-only.

Commit

Permalink
Merge d4948c7 into a775a3a
Browse files Browse the repository at this point in the history
  • Loading branch information
ryan-roemer committed Oct 3, 2016
2 parents a775a3a + d4948c7 commit 4425f34
Show file tree
Hide file tree
Showing 12 changed files with 496 additions and 49 deletions.
5 changes: 3 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@ language: node_js
node_js:
- "0.10"
- "0.12"
- "4.2"
- "5.7"
- "4"
- "5"
- "6"

# Use container-based Travis infrastructure.
sudo: false
Expand Down
5 changes: 5 additions & 0 deletions HISTORY.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
History
=======

## Unreleased

* Add `--env` environment variable flag.
[#45](https://github.com/FormidableLabs/builder/issues/45)

## 3.0.0

**BREAKING**:
Expand Down
27 changes: 22 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,8 @@ the rough goals and motivations behind the project.
- [builder run](#builder-run)
- [builder concurrent](#builder-concurrent)
- [builder envs](#builder-envs)
- [Custom Flags](#custom-flags)
- [Expanding the Archetype Path](#expanding-the-archetype-path)
- [Custom Flags](#custom-flags)
- [Expanding the Archetype Path](#expanding-the-archetype-path)
- [Tasks](#tasks)
- [npm Config](#npm-config)
- [`npm` Config Overview](#npm-config-overview)
Expand Down Expand Up @@ -278,6 +278,8 @@ Flags:
* `--setup`: Single task to run for the entirety of `<action>`
* `--quiet`: Silence logging
* `--log-level`: Level to log at (`info`, `warn`, `error`, `none`)
* `--env`: JSON object of keys to add to environment.
* `--env-path`: JSON file path of keys to add to environment.
* `--expand-archetype`: Expand `node_modules/<archetype>` with full path (default: `false`)
* `--builderrc`: Path to builder config file (default: `.builderrc`)

Expand All @@ -301,6 +303,8 @@ Flags:
* `--[no-]bail`: End all processes after the first failure (default: `true`)
* `--quiet`: Silence logging
* `--log-level`: Level to log at (`info`, `warn`, `error`, `none`)
* `--env`: JSON object of keys to add to environment.
* `--env-path`: JSON file path of keys to add to environment.
* `--expand-archetype`: Expand `node_modules/<archetype>` with full path (default: `false`)
* `--builderrc`: Path to builder config file (default: `.builderrc`)

Expand Down Expand Up @@ -343,13 +347,26 @@ Flags:
* `--envs-path`: Path to JSON env variable array file (default: `null`)
* `--quiet`: Silence logging
* `--log-level`: Level to log at (`info`, `warn`, `error`, `none`)
* `--env`: JSON object of keys to add to environment.
* `--env-path`: JSON file path of keys to add to environment.
* `--expand-archetype`: Expand `node_modules/<archetype>` with full path (default: `false`)
* `--builderrc`: Path to builder config file (default: `.builderrc`)

_Note_: The environments JSON array will overwrite **existing** values in the
environment.
environment. This includes environment variables provided to / from `builder`
from things such as `npm` `config` and the `--env`/`--env-path` flags.

###### Custom Flags
So, for example, if you invoke `builder` with:

```js
$ builder envs <task> '[{"FOO": "ENVS"}]' --env='{"FOO": "FLAG"}'
```

The environment variable `FOO` will have a value of `"ENVS"` with the single
environment object array item given to `builder envs` overriding the `--env`
flag value.

#### Custom Flags

Just like [`npm run <task> [-- <args>...]`](https://docs.npmjs.com/cli/run-script),
flags after a ` -- ` token in a builder task or from the command line are passed
Expand Down Expand Up @@ -391,7 +408,7 @@ The rough heuristic here is if we have custom arguments:
environment variables. (Builder uses `_BUILDER_ARGS_CUSTOM_FLAGS`).
2. If a non-`builder` command, then append without ` -- ` token.

###### Expanding the Archetype Path
#### Expanding the Archetype Path

Builder tasks often refer to configuration files in the archetype itself like:

Expand Down
1 change: 1 addition & 0 deletions appveyor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ environment:
- nodejs_version: 0.12
- nodejs_version: 4
- nodejs_version: 5
- nodejs_version: 6

# Install scripts. (runs after repo cloning)
install:
Expand Down
3 changes: 2 additions & 1 deletion bin/builder-core.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ module.exports = function (opts, callback) {
// Set up environment
var env = new Environment({
config: config,
env: opts.env
env: opts.env,
argv: opts.argv
});

// Set up logger state.
Expand Down
12 changes: 11 additions & 1 deletion lib/args.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,16 @@ var FLAGS = {
desc: "Level to log at (`info`, `warn`, `error`, `none`)",
types: [String],
default: "info"
},
env: {
desc: "JSON string of environment variables to add to process",
types: [String],
default: null
},
"env-path": {
desc: "JSON file path of environment variables to add to process",
types: [path],
default: null
}
},

Expand All @@ -98,7 +108,7 @@ var FLAGS = {
buffer: FLAG_BUFFER,
bail: FLAG_BAIL,
"envs-path": {
desc: "Path to JSON env variable array file (default: `null`)",
desc: "JSON File path to `envs` array",
types: [path],
default: null
}
Expand Down
38 changes: 38 additions & 0 deletions lib/environment.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@
*/
var _ = require("lodash");
var path = require("path");
var args = require("./args");
var clone = require("./utils/clone");
var jsonParse = require("./utils/json").parse;

// OS-specific helpers
var IS_WIN = process.platform.indexOf("win") === 0;
Expand All @@ -33,6 +35,10 @@ var Environment = module.exports = function (opts) {
opts = opts || {};
this.config = opts.config;

// Arguments
this.argv = opts.argv || process.argv;
var parsed = args.general(this.argv);

// Clone if real process env to avoid direct mutation.
this.env = opts.env || process.env;
if (this.env === process.env) {
Expand All @@ -53,6 +59,9 @@ var Environment = module.exports = function (opts) {

// Add in npm config environment variables.
this.updateConfigVars(this.config.pkgConfigs);

// Add command-line `--env|--env-path` environment overrides.
this.updateEnvFlag(parsed);
};

/**
Expand Down Expand Up @@ -125,3 +134,32 @@ Environment.prototype.updateConfigVars = function (pkgConfigs) {

return this.env;
};

/**
* Update environment `--env` / `--env-path` command line options.
*
* @param {Object} opts Options
* @param {String} opts.env Stringified JSON object
* @param {String} opts.envPath Path to JSON file
* @returns {Object} Mutated environment variable.
*/
Environment.prototype.updateEnvFlag = function (opts) {
// Nothing to parse.
if (!(opts.env || opts.envPath)) {
return this.env;
}

// Parse envs.
var envsObj = jsonParse({
str: opts.env,
path: opts.envPath
});

// Validation
if (!_.isPlainObject(envsObj)) {
throw new Error("Non-object JSON environment: " + JSON.stringify(envsObj));
}

// Mutate environment and return.
return _.merge(this.env, envsObj);
};
54 changes: 18 additions & 36 deletions lib/task.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
"use strict";

var fs = require("fs");
var path = require("path");
var _ = require("lodash");
var chalk = require("chalk");
var args = require("./args");
var Environment = require("./environment");
var log = require("./log");
var jsonParse = require("./utils/json").parse;

/**
* Task wrapper.
Expand Down Expand Up @@ -248,24 +248,6 @@ Task.prototype.concurrent = function (callback) {
{ env: env }, opts, callback);
};

Task.prototype._parseJson = function (objStr) {
try {
return JSON.parse(objStr);
} catch (err) {
log.error(this._action + ":json-obj", "Failed to load JSON object: " + objStr);
throw err;
}
};

Task.prototype._parseJsonFile = function (filePath) {
try {
return JSON.parse(fs.readFileSync(filePath));
} catch (err) {
log.error(this._action + ":json-file", "Failed to load JSON file: " + filePath);
throw err;
}
};

/**
* Run multiple environments.
*
Expand All @@ -280,35 +262,35 @@ Task.prototype.envs = function (callback) {
var flags = args.envs(this.argv);
var opts = this.getOpts(task, flags);

// Get task environment array.
var envsStr = this._commands[1];
// Parse envs.
var envsObj;
var err;
try {
if (envsStr) {
// Try string on command line first:
// $ builder envs <task> '[{ "FOO": "VAL1" }, { "FOO": "VAL2" }]'
opts._envs = this._parseJson(envsStr);
} else if (opts.envsPath) {
// Try JSON file path next:
// $ builder envs <task> --envs-path=my-environment-vars.json
opts._envs = this._parseJsonFile(opts.envsPath);
}
envsObj = jsonParse({
str: this._commands[1], // Get task environment array.
path: opts.envsPath
});
} catch (parseErr) {
return callback(parseErr);
err = parseErr;
}

// Validation
var err;
if (_.isEmpty(opts._envs)) {
if (!err && _.isEmpty(envsObj)) {
err = new Error("Empty/null JSON environments array.");
} else if (!_.isArray(opts._envs)) {
err = new Error("Non-array JSON environments object: " + JSON.stringify(opts._envs));
} else if (!err && !_.isArray(envsObj)) {
err = new Error("Non-array JSON environments object: " + JSON.stringify(envsObj));
}

if (err) {
log.error("envs:json-error", err);
log.error(this._action + ":json-error",
"Failed to load environments string / path with error: " + err);
return callback(err);
}

// Stash for use in runner options.
opts._envs = envsObj;

// Run.
this._runner.envs(task.cmd, { env: env }, opts, callback);
};

Expand Down
48 changes: 48 additions & 0 deletions lib/utils/json.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
"use strict";

var fs = require("fs");
var path = require("path");
var _ = require("lodash");

/**
* Parse environment string and/or JSON file path into a full JS object.
*
* For use with environment variable specification.
*
* **Note**: Does synchronous file read.
*
* Parsing logic:
*
* 1. Try `opts.str` string if set
* 2. If not, try `opts.path` file
*
* @param {Object} opts Options
* @param {String} opts.str Stringified JSON object
* @param {String} opts.path Path to JSON file
* @returns {Object} Data object.
*/
module.exports = {
parse: function (opts) {
var jsonStr = _.isUndefined(opts.str) ? null : opts.str;
var jsonPath = _.isUndefined(opts.path) ? null : opts.path;

// Validation: Programming error if thrown.
if (jsonStr === null && jsonPath === null) {
throw new Error("Must specify JSON string or path.");
}

// Try string first.
if (jsonStr) {
return JSON.parse(jsonStr);
}

// Try JSON file path next.
if (jsonPath) {
var data = fs.readFileSync(path.resolve(jsonPath));
return JSON.parse(data);
}

// Programming error.
throw new Error("Invalid parsing path.");
}
};
8 changes: 8 additions & 0 deletions test/server/fixtures/echo.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,16 @@
* }
* }
* ```
*
* Secondarily falls back on real environment variable `TEST_MESSAGE` if above
* is not set.
*/
var msg = process.env.npm_package_config__test_message;
if (typeof msg === "undefined") {
msg = process.env.TEST_MESSAGE;
}

var extra = process.argv[2] || "";
var out = typeof msg + " - " + (msg || "EMPTY") + (extra ? " - " + extra : "");

process.stdout.write(out + "\n");

0 comments on commit 4425f34

Please sign in to comment.