Skip to content

Commit

Permalink
Merge pull request #1955 from jagoda/feat-composer-interpolation
Browse files Browse the repository at this point in the history
Use environment variables in CLI configuration json file
  • Loading branch information
Eran Hammer committed Oct 8, 2014
2 parents c0f587f + dbfd366 commit bf9ccb2
Show file tree
Hide file tree
Showing 5 changed files with 139 additions and 0 deletions.
3 changes: 3 additions & 0 deletions docs/Reference.md
Expand Up @@ -3077,6 +3077,9 @@ installed in the path. The following arguments are available to the **hapi** CL
Note that `--require` will require from `node_modules`, an absolute path, a relative path, or from the `node_modules`
set by `-p` if available.

When using the CLI to compose a pack of servers, all values in the configuration json file can be set to an
environment variable by prefixing the variable name with`'$env.'`.

In order to help with A/B testing there is [confidence](https://github.com/hapijs/confidence). Confidence is a
configuration document format, an API, and a foundation for A/B testing. The configuration format is designed to
work with any existing JSON-based configuration, serving values based on object path ('/a/b/c' translates to a.b.c).
Expand Down
17 changes: 17 additions & 0 deletions lib/cli.js
Expand Up @@ -69,13 +69,30 @@ internals.loadExtras = function () {
};


internals.interpolateManifest = function (manifest) {
if (typeof manifest === 'object') {
Object.keys(manifest).forEach(function (key) {
var value = manifest[key];

if (typeof value === 'string' && value.indexOf('$env.') === 0) {
manifest[key] = process.env[value.slice(5)];
}
else {
internals.interpolateManifest(value);
}
});
}
};


internals.getManifest = function () {

var manifest = null;
var manifestPath = !Hoek.isAbsolutePath(internals.argv.c) ? Path.join(process.cwd(), internals.argv.c) : internals.argv.c;

try {
manifest = JSON.parse(Fs.readFileSync(manifestPath));
internals.interpolateManifest(manifest);
}
catch (err) {
console.log('Failed loading configuration file: ' + internals.argv.c + ' (' + err.message + ')');
Expand Down
94 changes: 94 additions & 0 deletions test/cli.js
Expand Up @@ -371,4 +371,98 @@ describe('Hapi command line', function () {
expect(data.toString()).to.not.exist;
});
});

describe('using environment variables', function () {

var changes = [];

var setEnv = function (key, value) {
var previous = process.env[key];

if (typeof value === 'undefined') {
delete process.env[key];
}
else {
process.env[key] = value;
}

return setEnv.bind(null, key, previous);
};

before(function (done) {
changes.push(setEnv('host', 'localhost'));
changes.push(setEnv('plugin_option', 'plugin-option'));
changes.push(setEnv('port', 0));
changes.push(setEnv('special_value', 'special-value'));
done();
});

after(function (done) {
var restore = changes.pop();

while (restore) {
restore();
restore = changes.pop();
}
done();
});

it('interpolates environment variable values', function (done) {

var manifest = {
pack: {
cache: {
engine: 'catbox-memory'
},
app: {
my: '$env.special_value'
}
},
servers: [
{
port: '$env.port',
options: {
labels: ['api', 'nasty', 'test']
}
},
{
host: '$env.host',
port: '$env.port',
options: {
labels: ['api', 'nice']
}
}
],
plugins: {
'./--options': {
key : '$env.plugin_option'
}
}
};

var configPath = internals.uniqueFilename(Os.tmpDir());
var hapiPath = Path.join(__dirname, '..', 'bin', 'hapi');
var modulePath = Path.join(__dirname, 'pack');

Fs.writeFileSync(configPath, JSON.stringify(manifest));

var hapi = ChildProcess.spawn('node', [hapiPath, '-c', configPath, '-p', modulePath]);

hapi.stdout.setEncoding('utf8');
hapi.stdout.on('data', function (data) {

expect(data).to.equal('app.my: special-value, options.key: plugin-option\n');
hapi.kill();
Fs.unlinkSync(configPath);

done();
});

hapi.stderr.setEncoding('utf8');
hapi.stderr.on('data', function (data) {

expect(data).to.not.exist;
});
});
});
});
18 changes: 18 additions & 0 deletions test/pack/--options/lib/index.js
@@ -0,0 +1,18 @@
// Declare internals

var internals = {};


// Plugin registration

exports.register = function (plugin, options, next) {

console.log('app.my: %s, options.key: %s', plugin.app.my, options.key);

return next();
};


exports.register.attributes = {
pkg: require('../package.json')
};
7 changes: 7 additions & 0 deletions test/pack/--options/package.json
@@ -0,0 +1,7 @@
{
"name": "--options",
"description": "Test plugin module",
"version": "0.0.1",
"private": true,
"main": "lib/index"
}

0 comments on commit bf9ccb2

Please sign in to comment.