From 43c4ca22c584d9c10e6a0509ee5613ddd402bc4b Mon Sep 17 00:00:00 2001 From: stafford Date: Mon, 21 Nov 2016 13:37:44 +1100 Subject: [PATCH 01/13] log error on undefined stack --- api.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api.js b/api.js index e66a1cdc..7fff31d5 100644 --- a/api.js +++ b/api.js @@ -136,7 +136,7 @@ function dbmigrate(plugins, isModule, options, callback) { function registerEvents() { process.on('uncaughtException', function(err) { - log.error(err.stack); + log.error(err.stack || err); process.exit(1); }); From 3e06b1c3f5d14bbcb0beaa1cecb8aa512ec9e68a Mon Sep 17 00:00:00 2001 From: Tobias Gurtzick Date: Wed, 30 Nov 2016 15:55:43 +0100 Subject: [PATCH 02/13] refactor(api): cleanup information and added commentary --- api.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/api.js b/api.js index 7fff31d5..ed3e079d 100644 --- a/api.js +++ b/api.js @@ -165,6 +165,11 @@ dbmigrate.prototype = { return true; }, + /** + * Registers and initializes hooks. + * + * @returns Promise + */ registerAPIHook: function(callback) { var plugins = this.internals.plugins; @@ -550,7 +555,7 @@ function setDefaultArgv(internals, isModule) { 'ignore-completed-migrations': false }) .usage( - 'Usage: db-migrate [up|down|reset|sync|create|db|seed|transition] ' + + 'Usage: db-migrate [up|down|reset|sync|create|db|transition] ' + '[[dbname/]migrationName|all] [options]' ) From b5e7c80a833c4e8eb0ef24838d810d393015a2c9 Mon Sep 17 00:00:00 2001 From: Tobias Gurtzick Date: Wed, 30 Nov 2016 16:53:48 +0100 Subject: [PATCH 03/13] feat(config): add rc style configs fixes #308 fixes #406 --- .gitignore | 1 + .npmignore | 1 + api.js | 10 ++++++++-- package.json | 2 ++ 4 files changed, 12 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 374cb87e..a9a6cc35 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,7 @@ database.json *.sublime-project *.sublime-workspace archive +.db-migraterc # Vim swap files .*.sw[a-z] diff --git a/.npmignore b/.npmignore index 0e6e098f..726ac2d3 100644 --- a/.npmignore +++ b/.npmignore @@ -8,3 +8,4 @@ database.json *.sublime-project *.sublime-workspace archive +.db-migraterc diff --git a/api.js b/api.js index ed3e079d..9f485631 100644 --- a/api.js +++ b/api.js @@ -540,6 +540,9 @@ dbmigrate.prototype = { function setDefaultArgv(internals, isModule) { + var rc = require('rc'); + var deepExtend = require('deep-extend'); + internals.argv = optimist .default({ verbose: false, @@ -558,7 +561,6 @@ function setDefaultArgv(internals, isModule) { 'Usage: db-migrate [up|down|reset|sync|create|db|transition] ' + '[[dbname/]migrationName|all] [options]' ) - .describe('env', 'The environment to run the migrations under (dev, test, prod).') .alias('e', 'env') @@ -636,6 +638,8 @@ function setDefaultArgv(internals, isModule) { var plugins = internals.plugins; var plugin = plugins.hook('init:cli:config:hook'); + var _config = internals.argv.argv.config; + if(plugin) { plugin.forEach(function(plugin) { @@ -648,7 +652,9 @@ function setDefaultArgv(internals, isModule) { }); } - internals.argv = internals.argv.argv; + internals.argv = deepExtend(internals.argv.argv, rc('db-migrate', {})); + internals.argv.rcconfig = internals.argv.config; + internals.argv.config = _config; if (internals.argv.version) { console.log(internals.dbm.version); diff --git a/package.json b/package.json index 2e20d50d..f2af0f62 100644 --- a/package.json +++ b/package.json @@ -47,6 +47,7 @@ "balanced-match": "^0.4.2", "bluebird": "^3.1.1", "db-migrate-shared": "^1.1.2", + "deep-extend": "^0.4.1", "dotenv": "^2.0.0", "final-fs": "^1.6.0", "inflection": "^1.10.0", @@ -56,6 +57,7 @@ "parse-database-url": "~0.3.0", "pkginfo": "^0.4.0", "prompt": "^1.0.0", + "rc": "^1.1.6", "resolve": "^1.1.6", "semver": "^5.3.0", "tunnel-ssh": "^4.0.0" From d1c81c6f6fdd5055dab79d0d2f6ac67ea3cf0367 Mon Sep 17 00:00:00 2001 From: Tobias Gurtzick Date: Wed, 30 Nov 2016 18:59:27 +0100 Subject: [PATCH 04/13] refactor(api): refactor tests and make api.js more modular --- api.js | 258 ++------------------------ lib/commands/down.js | 34 ++++ lib/commands/helper/assert.js | 17 ++ lib/commands/helper/migration-hook.js | 6 + lib/commands/on-complete.js | 34 ++++ lib/commands/set-default-argv.js | 151 +++++++++++++++ lib/commands/sync.js | 40 ++++ lib/commands/up.js | 40 ++++ test/integration/api_test.js | 81 ++++---- 9 files changed, 379 insertions(+), 282 deletions(-) create mode 100644 lib/commands/down.js create mode 100644 lib/commands/helper/assert.js create mode 100644 lib/commands/helper/migration-hook.js create mode 100644 lib/commands/on-complete.js create mode 100644 lib/commands/set-default-argv.js create mode 100644 lib/commands/sync.js create mode 100644 lib/commands/up.js diff --git a/api.js b/api.js index 9f485631..b5c44213 100644 --- a/api.js +++ b/api.js @@ -8,6 +8,7 @@ var log = require('db-migrate-shared').log; var pkginfo = require('pkginfo')(module, 'version'); // jshint ignore:line var dotenv = require('dotenv'); var Promise = Promise; +var onComplete = require('./lib/commands/on-complete.js'); function registerPluginLoader(plugins) { @@ -72,6 +73,8 @@ var APIHooks = { function dbmigrate(plugins, isModule, options, callback) { + var setDefaultArgv = require('./lib/commands/set-default-argv.js'); + this.internals = { onComplete: onComplete, @@ -244,6 +247,8 @@ dbmigrate.prototype = { */ up: function(specification, opts, callback) { + var executeUp = require('./lib/commands/up.js'); + if (arguments.length > 0) { if (typeof(specification) === 'string') { @@ -281,6 +286,8 @@ dbmigrate.prototype = { */ down: function(specification, opts, callback) { + var executeDown = require('./lib/commands/down.js'); + if (arguments.length > 0) { if (typeof(specification) === 'number') { @@ -538,155 +545,6 @@ dbmigrate.prototype = { }; -function setDefaultArgv(internals, isModule) { - - var rc = require('rc'); - var deepExtend = require('deep-extend'); - - internals.argv = optimist - .default({ - verbose: false, - table: 'migrations', - 'seeds-table': 'seeds', - 'force-exit': false, - 'sql-file': false, - 'non-transactional': false, - config: internals.configFile || internals.cwd + '/database.json', - 'migrations-dir': internals.cwd + '/migrations', - 'vcseeder-dir': internals.cwd + '/VCSeeder', - 'staticseeder-dir': internals.cwd + '/Seeder', - 'ignore-completed-migrations': false - }) - .usage( - 'Usage: db-migrate [up|down|reset|sync|create|db|transition] ' + - '[[dbname/]migrationName|all] [options]' - ) - .describe('env', - 'The environment to run the migrations under (dev, test, prod).') - .alias('e', 'env') - .string('e') - - .describe('migrations-dir', 'The directory containing your migration files.') - .alias('m', 'migrations-dir') - .string('m') - - .describe('count', 'Max number of migrations to run.') - .alias('c', 'count') - .string('c') - - .describe('dry-run', 'Prints the SQL but doesn\'t run it.') - .boolean('dry-run') - - .describe('force-exit', 'Forcibly exit the migration process on completion.') - .boolean('force-exit') - - .describe('verbose', 'Verbose mode.') - .alias('v', 'verbose') - .boolean('v') - - .alias('h', 'help') - .alias('h', '?') - .boolean('h') - - .describe('version', 'Print version info.') - .alias('i', 'version') - .boolean('version') - - .describe('config', 'Location of the database.json file.') - .string('config') - - .describe('sql-file', - 'Automatically create two sql files for up and down statements in ' + - '/sqls and generate the javascript code that loads them.' - ) - .boolean('sql-file') - - .describe('coffee-file', 'Create a coffeescript migration file') - .boolean('coffee-file') - .describe('ignore-on-init', - 'Create files that will run only if ignore-on-init in the env is set ' + - 'to false (currently works onlt with SQL)' - ).boolean('ignore-on-init') - - .describe('migration-table', - 'Set the name of the migration table, which stores the migration history.' - ) - .alias('table', 'migration-table') - .alias('t', 'table') - .string('t') - - .describe('seeds-table', - 'Set the name of the seeds table, which stores the seed history.') - .string('seeds-table') - - .describe('vcseeder-dir', - 'Set the path to the Version Controlled Seeder directory.') - .string('vcseeder-dir') - - .describe('staticseeder-dir', 'Set the path to the Seeder directory.') - .string('staticseeder-dir') - - .describe('non-transactional', 'Explicitly disable transactions') - .boolean('non-transactional') - - .describe('ignore-completed-migrations', 'Start at the first migration') - .boolean('ignore-completed-migrations') - - .describe('log-level', 'Set the log-level, for example sql|warn') - .string('log-level'); - - - var plugins = internals.plugins; - var plugin = plugins.hook('init:cli:config:hook'); - var _config = internals.argv.argv.config; - - if(plugin) { - - plugin.forEach(function(plugin) { - - var configs = plugin['init:cli:config:hook'](); - if(!configs) return; - - //hook not yet used, we look into migrating away from optimist first - return; - }); - } - - internals.argv = deepExtend(internals.argv.argv, rc('db-migrate', {})); - internals.argv.rcconfig = internals.argv.config; - internals.argv.config = _config; - - if (internals.argv.version) { - console.log(internals.dbm.version); - process.exit(0); - } - - if (!isModule && (internals.argv.help || internals.argv._.length === 0)) { - optimist.showHelp(); - process.exit(1); - } - - if (internals.argv['log-level']) { - - log.setLogLevel(internals.argv['log-level']); - } - - internals.ignoreCompleted = internals.argv['ignore-completed-migrations']; - internals.migrationTable = internals.argv.table; - internals.seedTable = internals.argv['seeds-table']; - internals.matching = ''; - internals.verbose = internals.argv.verbose; - global.verbose = internals.verbose; - internals.notransactions = internals.argv['non-transactional']; - internals.dryRun = internals.argv['dry-run']; - global.dryRun = internals.dryRun; - - if (internals.dryRun) { - log.info('dry run'); - } - -} - function createMigrationDir(dir, callback) { fs.stat(dir, function(err) { if (err) { @@ -891,42 +749,6 @@ function migrationHook(internals) { return Migration.registerHook(internals.plugins, internals); } -function executeUp(internals, config, callback) { - - migrationHook(internals) - .then(function() { - - var Migrator = require('./lib/migrator.js'); - var index = require('./connect'); - - if (!internals.argv.count) { - internals.argv.count = Number.MAX_VALUE; - } - index.connect({ - config: config.getCurrent().settings, - internals: internals - }, Migrator, function(err, migrator) { - assert.ifError(err); - - if (internals.locTitle) - migrator.migrationsDir = path.resolve(internals.argv['migrations-dir'], - internals.locTitle); - else - migrator.migrationsDir = path.resolve(internals.argv['migrations-dir']); - - internals.migrationsDir = migrator.migrationsDir; - - migrator.driver.createMigrationsTable(function(err) { - assert.ifError(err); - log.verbose('migration table created'); - - migrator.up(internals.argv, internals.onComplete.bind(this, - migrator, internals, callback)); - }); - }); - }); -} - function executeSync(internals, config, callback) { migrationHook(internals) @@ -963,36 +785,6 @@ function executeSync(internals, config, callback) { }); } -function executeDown(internals, config, callback) { - - migrationHook(internals) - .then(function() { - - var Migrator = require('./lib/migrator.js'); - var index = require('./connect'); - - if (!internals.argv.count) { - log.info('Defaulting to running 1 down migration.'); - internals.argv.count = 1; - } - - index.connect({ - config: config.getCurrent().settings, - internals: internals - }, Migrator, function(err, migrator) { - assert.ifError(err); - - migrator.migrationsDir = path.resolve(internals.argv['migrations-dir']); - - migrator.driver.createMigrationsTable(function(err) { - assert.ifError(err); - migrator.down(internals.argv, internals.onComplete.bind(this, - migrator, internals, callback)); - }); - }); - }); -} - function executeDB(internals, config, callback) { var index = require('./connect'); @@ -1119,38 +911,6 @@ function executeUndoSeed(internals, config, callback) { }); } -function onComplete(migrator, internals, callback, originalErr) { - - if (typeof(callback) !== 'function') { - originalErr = originalErr || callback; - } - - migrator.driver.close(function(err) { - if ((err || originalErr) && typeof(callback) === 'function') { - - callback({ - err: err, - originalErr: originalErr - }); - return; - } else { - - assert.ifError(originalErr); - assert.ifError(err); - log.info('Done'); - } - - if (internals.argv['force-exit']) { - log.verbose('Forcing exit'); - process.exit(0); - } - - if (typeof(callback) === 'function') { - callback(); - } - }); -} - function transition(internals) { require('./lib/transitions/transitioner.js')(internals); @@ -1216,8 +976,12 @@ function run(internals, config) { } if (action === 'up') { + + var executeUp = require('./lib/commands/up.js'); executeUp(internals, config); } else { + + var executeDown = require('./lib/commands/down.js'); executeDown(internals, config); } break; diff --git a/lib/commands/down.js b/lib/commands/down.js new file mode 100644 index 00000000..dbe3f657 --- /dev/null +++ b/lib/commands/down.js @@ -0,0 +1,34 @@ +var path = require('path'); +var log = require('db-migrate-shared').log; +var assert = require('./helper/assert.js'); +var migrationHook = require('./helper/migration-hook.js'); + +module.exports = function(internals, config, callback) { + + migrationHook(internals) + .then(function() { + + var Migrator = require('../migrator.js'); + var index = require('../../connect'); + + if (!internals.argv.count) { + log.info('Defaulting to running 1 down migration.'); + internals.argv.count = 1; + } + + index.connect({ + config: config.getCurrent().settings, + internals: internals + }, Migrator, function(err, migrator) { + if(!assert(err)) return; + + migrator.migrationsDir = path.resolve(internals.argv['migrations-dir']); + + migrator.driver.createMigrationsTable(function(err) { + if(!assert(err)) return; + migrator.down(internals.argv, internals.onComplete.bind(this, + migrator, internals, callback)); + }); + }); + }); +}; diff --git a/lib/commands/helper/assert.js b/lib/commands/helper/assert.js new file mode 100644 index 00000000..ce648c67 --- /dev/null +++ b/lib/commands/helper/assert.js @@ -0,0 +1,17 @@ + +module.exports = function(err, callback) { + if (err) { + + if (typeof(callback) === 'function') { + + callback(err); + return false; + } else { + + assert.ifError(err); + return false; + } + } + + return true; +}; diff --git a/lib/commands/helper/migration-hook.js b/lib/commands/helper/migration-hook.js new file mode 100644 index 00000000..f9ed96ff --- /dev/null +++ b/lib/commands/helper/migration-hook.js @@ -0,0 +1,6 @@ + +module.exports = function(internals) { + + var Migration = require('../../migration.js'); + return Migration.registerHook(internals.plugins, internals); +}; diff --git a/lib/commands/on-complete.js b/lib/commands/on-complete.js new file mode 100644 index 00000000..b064231a --- /dev/null +++ b/lib/commands/on-complete.js @@ -0,0 +1,34 @@ +var assert = require('assert'); +var log = require('db-migrate-shared').log; + +module.exports = function(migrator, internals, callback, originalErr) { + if (typeof(callback) !== 'function') { + originalErr = originalErr || callback; + } + + migrator.driver.close(function(err) { + + if ((err || originalErr) && typeof(callback) === 'function') { + + callback({ + err: err, + originalErr: originalErr + }); + return; + } else { + + assert.ifError(originalErr); + assert.ifError(err); + log.info('Done'); + } + + if (internals.argv['force-exit']) { + log.verbose('Forcing exit'); + return process.exit(0); + } + + if (typeof(callback) === 'function') { + callback(); + } + }); +}; diff --git a/lib/commands/set-default-argv.js b/lib/commands/set-default-argv.js new file mode 100644 index 00000000..bf45b635 --- /dev/null +++ b/lib/commands/set-default-argv.js @@ -0,0 +1,151 @@ +var optimist = require('optimist'); +var log = require('db-migrate-shared').log; + +module.exports = function(internals, isModule) { + + var rc = require('rc'); + var deepExtend = require('deep-extend'); + + internals.argv = optimist + .default({ + verbose: false, + table: 'migrations', + 'seeds-table': 'seeds', + 'force-exit': false, + 'sql-file': false, + 'non-transactional': false, + config: internals.configFile || internals.cwd + '/database.json', + 'migrations-dir': internals.cwd + '/migrations', + 'vcseeder-dir': internals.cwd + '/VCSeeder', + 'staticseeder-dir': internals.cwd + '/Seeder', + 'ignore-completed-migrations': false + }) + .usage( + 'Usage: db-migrate [up|down|reset|sync|create|db|transition] ' + + '[[dbname/]migrationName|all] [options]' + ) + .describe('env', + 'The environment to run the migrations under (dev, test, prod).') + .alias('e', 'env') + .string('e') + + .describe('migrations-dir', 'The directory containing your migration files.') + .alias('m', 'migrations-dir') + .string('m') + + .describe('count', 'Max number of migrations to run.') + .alias('c', 'count') + .string('c') + + .describe('dry-run', 'Prints the SQL but doesn\'t run it.') + .boolean('dry-run') + + .describe('force-exit', 'Forcibly exit the migration process on completion.') + .boolean('force-exit') + + .describe('verbose', 'Verbose mode.') + .alias('v', 'verbose') + .boolean('v') + + .alias('h', 'help') + .alias('h', '?') + .boolean('h') + + .describe('version', 'Print version info.') + .alias('i', 'version') + .boolean('version') + + .describe('config', 'Location of the database.json file.') + .string('config') + + .describe('sql-file', + 'Automatically create two sql files for up and down statements in ' + + '/sqls and generate the javascript code that loads them.' + ) + .boolean('sql-file') + + .describe('coffee-file', 'Create a coffeescript migration file') + .boolean('coffee-file') + .describe('ignore-on-init', + 'Create files that will run only if ignore-on-init in the env is set ' + + 'to false (currently works onlt with SQL)' + ).boolean('ignore-on-init') + + .describe('migration-table', + 'Set the name of the migration table, which stores the migration history.' + ) + .alias('table', 'migration-table') + .alias('t', 'table') + .string('t') + + .describe('seeds-table', + 'Set the name of the seeds table, which stores the seed history.') + .string('seeds-table') + + .describe('vcseeder-dir', + 'Set the path to the Version Controlled Seeder directory.') + .string('vcseeder-dir') + + .describe('staticseeder-dir', 'Set the path to the Seeder directory.') + .string('staticseeder-dir') + + .describe('non-transactional', 'Explicitly disable transactions') + .boolean('non-transactional') + + .describe('ignore-completed-migrations', 'Start at the first migration') + .boolean('ignore-completed-migrations') + + .describe('log-level', 'Set the log-level, for example sql|warn') + .string('log-level'); + + + var plugins = internals.plugins; + var plugin = plugins.hook('init:cli:config:hook'); + var _config = internals.argv.argv.config; + + if(plugin) { + + plugin.forEach(function(plugin) { + + var configs = plugin['init:cli:config:hook'](); + if(!configs) return; + + //hook not yet used, we look into migrating away from optimist first + return; + }); + } + + internals.argv = deepExtend(internals.argv.argv, rc('db-migrate', {})); + internals.argv.rcconfig = internals.argv.config; + internals.argv.config = _config; + + if (internals.argv.version) { + console.log(internals.dbm.version); + process.exit(0); + } + + if (!isModule && (internals.argv.help || internals.argv._.length === 0)) { + optimist.showHelp(); + process.exit(1); + } + + if (internals.argv['log-level']) { + + log.setLogLevel(internals.argv['log-level']); + } + + internals.ignoreCompleted = internals.argv['ignore-completed-migrations']; + internals.migrationTable = internals.argv.table; + internals.seedTable = internals.argv['seeds-table']; + internals.matching = ''; + internals.verbose = internals.argv.verbose; + global.verbose = internals.verbose; + internals.notransactions = internals.argv['non-transactional']; + internals.dryRun = internals.argv['dry-run']; + global.dryRun = internals.dryRun; + + if (internals.dryRun) { + log.info('dry run'); + } + +}; diff --git a/lib/commands/sync.js b/lib/commands/sync.js new file mode 100644 index 00000000..81740b0d --- /dev/null +++ b/lib/commands/sync.js @@ -0,0 +1,40 @@ +var path = require('path'); +var log = require('db-migrate-shared').log; +var assert = require('./helper/assert.js'); +var migrationHook = require('./helper/migration-hook.js'); + +module.exports = function(internals, config, callback) { + + migrationHook(internals) + .then(function() { + + var Migrator = require('../migrator.js'); + var index = require('../../connect'); + + if (!internals.argv.count) { + internals.argv.count = Number.MAX_VALUE; + } + index.connect({ + config: config.getCurrent().settings, + internals: internals + }, Migrator, function(err, migrator) { + if(!assert(err)) return; + + if (internals.locTitle) + migrator.migrationsDir = path.resolve(internals.argv['migrations-dir'], + internals.locTitle); + else + migrator.migrationsDir = path.resolve(internals.argv['migrations-dir']); + + internals.migrationsDir = migrator.migrationsDir; + + migrator.driver.createMigrationsTable(function(err) { + if(!assert(err)) return; + log.verbose('migration table created'); + + migrator.sync(internals.argv, internals.onComplete.bind(this, + migrator, internals, callback)); + }); + }); + }); +}; diff --git a/lib/commands/up.js b/lib/commands/up.js new file mode 100644 index 00000000..8854d5f6 --- /dev/null +++ b/lib/commands/up.js @@ -0,0 +1,40 @@ +var path = require('path'); +var log = require('db-migrate-shared').log; +var assert = require('./helper/assert.js'); +var migrationHook = require('./helper/migration-hook.js'); + +module.exports = function(internals, config, callback) { + + migrationHook(internals) + .then(function() { + + var Migrator = require('../migrator.js'); + var index = require('../../connect'); + + if (!internals.argv.count) { + internals.argv.count = Number.MAX_VALUE; + } + index.connect({ + config: config.getCurrent().settings, + internals: internals + }, Migrator, function(err, migrator) { + if(!assert(err, callback)) return; + + if (internals.locTitle) + migrator.migrationsDir = path.resolve(internals.argv['migrations-dir'], + internals.locTitle); + else + migrator.migrationsDir = path.resolve(internals.argv['migrations-dir']); + + internals.migrationsDir = migrator.migrationsDir; + + migrator.driver.createMigrationsTable(function(err) { + if(!assert(err, callback)) return; + log.verbose('migration table created'); + + migrator.up(internals.argv, internals.onComplete.bind(this, + migrator, internals, callback)); + }); + }); + }); +}; diff --git a/test/integration/api_test.js b/test/integration/api_test.js index bb7f6caa..1314e995 100644 --- a/test/integration/api_test.js +++ b/test/integration/api_test.js @@ -1,9 +1,8 @@ var Code = require('code'); var Lab = require('lab'); var lab = exports.lab = Lab.script(); -var DBMigrate = require('../../'); -var path = require('path'); -var cp = require('child_process'); +var sinon = require('sinon'); +var proxyquire = require('proxyquire').noPreserveCache(); lab.experiment('api', function() { @@ -17,22 +16,23 @@ lab.experiment('api', function() { // register cleanup method and start preparing the test onCleanup(teardown); - createMigration(function() { + overwriteExit(); - var dbmigrate = DBMigrate.getInstance(true, config); + var dbmigrate = stubApiInstance(true, { + './lib/commands/up.js': upStub + }, config); - dbmigrate.setConfigParam('force-exit', true); - dbmigrate.silence(true); + dbmigrate.setConfigParam('force-exit', true); + dbmigrate.silence(true); - /** - * We have set force-exit above, this should end up in db-migrate - * executing process.exit on the final callback. - * Process.exit has been overwritten and will finally call validate. - * - * The test validation takes place in validate() - */ - dbmigrate.up(); - }); + /** + * We have set force-exit above, this should end up in db-migrate + * executing process.exit on the final callback. + * Process.exit has been overwritten and will finally call validate. + * + * The test validation takes place in validate() + */ + dbmigrate.up(); /** * Final validation after process.exit should have been called. @@ -43,38 +43,39 @@ lab.experiment('api', function() { done(); } + function upStub(internals) { + + internals.onComplete({ + driver: { + close: sinon.stub().callsArg(0) + } + }, internals); + } + /** * Create a migration with the programatic API and overwrite process.exit. */ - function createMigration(callback) { + function overwriteExit() { - var api = DBMigrate.getInstance(true, config); - api.silence(true); + process.exit = function(err) { - api.create( 'test', function() { - process.exit = function(err) { + var ret = called; + called = true; - var ret = called; - called = true; + process.exit = process_exit; - process.exit = process_exit; + if(err) + process.exit.apply(arguments); - if(err) - process.exit.apply(arguments); - - Code.expect(ret).to.be.false(); - validate(); - }; - - callback(); - } ); + Code.expect(ret).to.be.false(); + validate(); + }; } function teardown(next) { process.exit = process_exit; process.argv = argv; - cp.exec('rm -r ' + path.join(__dirname, 'migrations'), this.callback); return next(); } }); @@ -97,7 +98,7 @@ lab.experiment('api', function() { } }; - var api = DBMigrate.getInstance(true, options); + var api = stubApiInstance(true, {}, options); var actual = api.config; var expected = options.config; @@ -108,3 +109,13 @@ lab.experiment('api', function() { done(); }); }); + +function stubApiInstance(isModule, stubs, options, callback) { + + delete require.cache[require.resolve('../../api.js')]; + delete require.cache[require.resolve('optimist')]; + var mod = proxyquire('../../api.js', stubs), + plugins = {}; + + return new mod(plugins, isModule, options, callback); +}; From a2522a2c8a6a9517cd582dce994913f32f59f1b4 Mon Sep 17 00:00:00 2001 From: Tobias Gurtzick Date: Wed, 30 Nov 2016 19:02:13 +0100 Subject: [PATCH 05/13] fix(api): add missing reference to sync --- api.js | 40 ++++------------------------------------ 1 file changed, 4 insertions(+), 36 deletions(-) diff --git a/api.js b/api.js index b5c44213..ee321d75 100644 --- a/api.js +++ b/api.js @@ -322,6 +322,8 @@ dbmigrate.prototype = { */ sync: function(specification, opts, callback) { + var executeSync = require('./lib/commands/sync.js'); + if (arguments.length > 0) { if (typeof(specification) === 'string') { @@ -749,42 +751,6 @@ function migrationHook(internals) { return Migration.registerHook(internals.plugins, internals); } -function executeSync(internals, config, callback) { - - migrationHook(internals) - .then(function() { - - var Migrator = require('./lib/migrator.js'); - var index = require('./connect'); - - if (!internals.argv.count) { - internals.argv.count = Number.MAX_VALUE; - } - index.connect({ - config: config.getCurrent().settings, - internals: internals - }, Migrator, function(err, migrator) { - assert.ifError(err); - - if (internals.locTitle) - migrator.migrationsDir = path.resolve(internals.argv['migrations-dir'], - internals.locTitle); - else - migrator.migrationsDir = path.resolve(internals.argv['migrations-dir']); - - internals.migrationsDir = migrator.migrationsDir; - - migrator.driver.createMigrationsTable(function(err) { - assert.ifError(err); - log.verbose('migration table created'); - - migrator.sync(internals.argv, internals.onComplete.bind(this, - migrator, internals, callback)); - }); - }); - }); -} - function executeDB(internals, config, callback) { var index = require('./connect'); @@ -937,6 +903,8 @@ function run(internals, config) { break; case 'sync': + var executeSync = require('./lib/commands/sync.js'); + if (internals.argv._.length === 0) { log.error('Missing sync destination!'); From 11fef2912e8bd8f0962f86d63711e2ae465101a2 Mon Sep 17 00:00:00 2001 From: Tobias Gurtzick Date: Wed, 30 Nov 2016 19:07:46 +0100 Subject: [PATCH 06/13] refactor(api): remove last output of postponed seeders --- api.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api.js b/api.js index ee321d75..e17930dc 100644 --- a/api.js +++ b/api.js @@ -995,7 +995,7 @@ function run(internals, config) { } else { - log.error('Invalid Action: Must be [up|down|create|reset|sync|seed|' + + log.error('Invalid Action: Must be [up|down|create|reset|sync|' + 'db|transition].'); optimist.showHelp(); process.exit(1); From 3f0bd0796f4b481c11f7fd0a7f9e0425c2e06317 Mon Sep 17 00:00:00 2001 From: Tobias Gurtzick Date: Wed, 30 Nov 2016 19:21:00 +0100 Subject: [PATCH 07/13] add test dummies for api --- test/integration/api_test.js | 44 +++++++++++++++++++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/test/integration/api_test.js b/test/integration/api_test.js index 1314e995..729f43db 100644 --- a/test/integration/api_test.js +++ b/test/integration/api_test.js @@ -4,7 +4,7 @@ var lab = exports.lab = Lab.script(); var sinon = require('sinon'); var proxyquire = require('proxyquire').noPreserveCache(); -lab.experiment('api', function() { +lab.experiment('api', { parallel: true }, function() { lab.test('force process exit after migrations have been run', { parallel : true}, function(done, onCleanup) { @@ -108,6 +108,48 @@ lab.experiment('api', function() { Code.expect(actual).to.equal(expected); done(); }); + + lab.test.skip('should do something', { parallel: true }, function(done) { + + var api = stubApiInstance(true, { + './lib/commands/up.js': upStub + }); + + api.up(); + + function upStub() { + + done(); + } + }); + + lab.test.skip('should do something', { parallel: true }, function(done) { + + var api = stubApiInstance(true, { + './lib/commands/down.js': downStub + }); + + api.down(); + + function downStub() { + + done(); + } + }); + + lab.test.skip('should do something', { parallel: true }, function(done) { + + var api = stubApiInstance(true, { + './lib/commands/sync.js': syncStub + }); + + api.sync(); + + function syncStub() { + + done(); + } + }); }); function stubApiInstance(isModule, stubs, options, callback) { From 85bde6de71a609449ffb8937eba6917b34582fa7 Mon Sep 17 00:00:00 2001 From: Tobias Gurtzick Date: Wed, 30 Nov 2016 23:14:20 +0100 Subject: [PATCH 08/13] add first programable api tests --- api.js | 7 +- test/integration/api_test.js | 126 +++++++++++++++++++++++++++-------- 2 files changed, 104 insertions(+), 29 deletions(-) diff --git a/api.js b/api.js index e17930dc..373b6a0f 100644 --- a/api.js +++ b/api.js @@ -95,8 +95,11 @@ function dbmigrate(plugins, isModule, options, callback) { dotenv.load({ silent: true }); - registerEvents(); + /* $lab:coverage:off$ */ + if(options && !options.throwUncatched) + registerEvents(); + /* $lab:coverage:on$ */ if (typeof(options) === 'object') { @@ -352,6 +355,8 @@ dbmigrate.prototype = { */ reset: function(scope, callback) { + var executeDown = require('./lib/commands/down.js'); + if (typeof(scope) === 'string') { this.internals.migrationMode = scope; diff --git a/test/integration/api_test.js b/test/integration/api_test.js index 729f43db..2a60bffb 100644 --- a/test/integration/api_test.js +++ b/test/integration/api_test.js @@ -3,6 +3,7 @@ var Lab = require('lab'); var lab = exports.lab = Lab.script(); var sinon = require('sinon'); var proxyquire = require('proxyquire').noPreserveCache(); +var Promise = require('bluebird'); lab.experiment('api', { parallel: true }, function() { @@ -109,48 +110,112 @@ lab.experiment('api', { parallel: true }, function() { done(); }); - lab.test.skip('should do something', { parallel: true }, function(done) { - - var api = stubApiInstance(true, { - './lib/commands/up.js': upStub - }); + lab.test('should handle all up parameter variations properly', + { parallel: true }, function(done) { + + Promise.resolve([ + [], // promise + [sinon.spy()], + ['nameatargetmigration', sinon.spy()], // targeted migration + ['nameatargetmigration'], // promise targeted migration + [1, sinon.spy()], // targeted migration + [1], // promise targeted migration + ['nameatargetmigration', 'testscope', sinon.spy()], // scoped target + ['nameatargetmigration', 'testscope'], // promise scope target + [1, 'testscope', sinon.spy()], // scoped target + [1, 'testscope'] // promise scope target + ]) + .each(defaultExecParams('up')) + .each(spyCallback) + .asCallback(done); + }); - api.up(); + lab.test('should handle all down parameter variations properly', + { parallel: true }, function(done) { + + Promise.resolve([ + [], // promise + [sinon.spy()], + [1, sinon.spy()], // targeted migration + [1], // promise targeted migration + [1, 'testscope', sinon.spy()], // scoped target + [1, 'testscope'] // promise scope target + ]) + .each(defaultExecParams('down')) + .each(spyCallback) + .asCallback(done); + }); - function upStub() { + lab.test('should handle all reset parameter variations properly', + { parallel: true }, function(done) { + + Promise.resolve([ + [], // promise + [sinon.spy()], + ['testscope', sinon.spy()], // scoped target + ['testscope'] // promise scope target + ]) + .each(defaultExecParams('reset')) + .each(spyCallback) + .asCallback(done); + }); - done(); - } + lab.test('should handle all sync parameter variations properly', + { parallel: true }, function(done) { + + Promise.resolve([ + [], + ['nameatargetmigration', sinon.spy()], // targeted migration + ['nameatargetmigration'], // promise targeted migration + ['nameatargetmigration', 'testscope', sinon.spy()], // scoped target + ['nameatargetmigration', 'testscope'], // promise scope target + ]) + .each(defaultExecParams('sync')) + .each(spyCallback) + .asCallback(done); }); +}); - lab.test.skip('should do something', { parallel: true }, function(done) { +function defaultExecParams(method) { - var api = stubApiInstance(true, { - './lib/commands/down.js': downStub - }); + return function(args, index) { - api.down(); + var stubs = {}; + stubs['./lib/commands/' + method + '.js'] = stub; - function downStub() { + var api = stubApiInstance(true, stubs); - done(); - } - }); + return [ api[method].apply(api, args), args ]; - lab.test.skip('should do something', { parallel: true }, function(done) { + function stub(internals, config, callback) { - var api = stubApiInstance(true, { - './lib/commands/sync.js': syncStub - }); + if(typeof(args[0]) === 'string') { - api.sync(); + Code.expect(internals.argv.destination).to.equal(args[0]); + } else if(typeof(args[0]) === 'number') { - function syncStub() { + Code.expect(internals.argv.count).to.equal(args[0]); + } - done(); + if(typeof(args[1]) === 'string') { + + Code.expect(internals.migrationMode).to.equal(args[1]); + Code.expect(internals.matching).to.equal(args[1]); + } + + callback(); } - }); -}); + }; +} + +function spyCallback(api, args) { + + if(typeof(args[args.length - 1]) === 'function') { + + var spy = args[args.length - 1]; + Code.expect(spy.called).to.be.true(); + } +} function stubApiInstance(isModule, stubs, options, callback) { @@ -158,6 +223,11 @@ function stubApiInstance(isModule, stubs, options, callback) { delete require.cache[require.resolve('optimist')]; var mod = proxyquire('../../api.js', stubs), plugins = {}; + options = options || {}; + + options = Object.assign(options, { + throwUncatched: true + }); return new mod(plugins, isModule, options, callback); -}; +} From ecae49bab668e4dcf580dbe39660b7b5c58949c1 Mon Sep 17 00:00:00 2001 From: Tobias Gurtzick Date: Wed, 30 Nov 2016 23:23:44 +0100 Subject: [PATCH 09/13] directly use promises in tests --- test/integration/api_test.js | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/test/integration/api_test.js b/test/integration/api_test.js index 2a60bffb..73c851fc 100644 --- a/test/integration/api_test.js +++ b/test/integration/api_test.js @@ -111,9 +111,9 @@ lab.experiment('api', { parallel: true }, function() { }); lab.test('should handle all up parameter variations properly', - { parallel: true }, function(done) { + { parallel: true }, function() { - Promise.resolve([ + return Promise.resolve([ [], // promise [sinon.spy()], ['nameatargetmigration', sinon.spy()], // targeted migration @@ -126,14 +126,13 @@ lab.experiment('api', { parallel: true }, function() { [1, 'testscope'] // promise scope target ]) .each(defaultExecParams('up')) - .each(spyCallback) - .asCallback(done); + .each(spyCallback); }); lab.test('should handle all down parameter variations properly', - { parallel: true }, function(done) { + { parallel: true }, function() { - Promise.resolve([ + return Promise.resolve([ [], // promise [sinon.spy()], [1, sinon.spy()], // targeted migration @@ -142,28 +141,26 @@ lab.experiment('api', { parallel: true }, function() { [1, 'testscope'] // promise scope target ]) .each(defaultExecParams('down')) - .each(spyCallback) - .asCallback(done); + .each(spyCallback); }); lab.test('should handle all reset parameter variations properly', - { parallel: true }, function(done) { + { parallel: true }, function() { - Promise.resolve([ + return Promise.resolve([ [], // promise [sinon.spy()], ['testscope', sinon.spy()], // scoped target ['testscope'] // promise scope target ]) .each(defaultExecParams('reset')) - .each(spyCallback) - .asCallback(done); + .each(spyCallback); }); lab.test('should handle all sync parameter variations properly', - { parallel: true }, function(done) { + { parallel: true }, function() { - Promise.resolve([ + return Promise.resolve([ [], ['nameatargetmigration', sinon.spy()], // targeted migration ['nameatargetmigration'], // promise targeted migration @@ -171,8 +168,7 @@ lab.experiment('api', { parallel: true }, function() { ['nameatargetmigration', 'testscope'], // promise scope target ]) .each(defaultExecParams('sync')) - .each(spyCallback) - .asCallback(done); + .each(spyCallback); }); }); From 369bf8528c78ec74993dd2780d7bd203d84cad7d Mon Sep 17 00:00:00 2001 From: ZReichert Date: Thu, 15 Jun 2017 13:10:30 -0400 Subject: [PATCH 10/13] Double quoting interpolated file name for coffee --- lib/migration.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/migration.js b/lib/migration.js index 443dccf6..f326f6e6 100644 --- a/lib/migration.js +++ b/lib/migration.js @@ -279,16 +279,16 @@ Migration.prototype.coffeeSqlFileLoaderTemplate = function() { '', '', 'exports.up = (db, callback) ->', - ' filePath = path.join \'#{__dirname}/sqls/' + this.name.replace( - '.coffee', '') + '-up.sql\'', + ' filePath = path.join "#{__dirname}/sqls/' + this.name.replace( + '.coffee', '') + '-up.sql"', ' fs.readFile filePath, {encoding: \'utf-8\'}, (err,data) ->', ' return callback err if err', '', ' db.runSql data, callback', '', 'exports.down = (db, callback) ->', - ' filePath = path.join \'#{__dirname}/sqls/' + this.name.replace( - '.coffee', '') + '-down.sql\'', + ' filePath = path.join "#{__dirname}/sqls/' + this.name.replace( + '.coffee', '') + '-down.sql"', ' fs.readFile filePath, {encoding: \'utf-8\'}, (err,data) ->', ' return callback err if err', From f7c28c133607a0d5c1ae767a7fd4f01f3c78d4c4 Mon Sep 17 00:00:00 2001 From: Gonzalo Manrique Date: Sun, 9 Jul 2017 17:36:19 -0300 Subject: [PATCH 11/13] fix(create): use same timestamp in every created file --- api.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/api.js b/api.js index 373b6a0f..54e9fac0 100644 --- a/api.js +++ b/api.js @@ -648,7 +648,7 @@ function executeCreateMigration(internals, config, callback) { templateType = Migration.TemplateType.DEFAULT_COFFEE; } var migration = new Migration(internals.argv.title + ( - shouldCreateCoffeeFile( internals, config ) ? '.coffee' : '.js'), path, new Date(), + shouldCreateCoffeeFile( internals, config ) ? '.coffee' : '.js'), path, internals.runTimestamp, templateType); index.createMigration(migration, function(err, migration) { if (_assert(err, callback)) { @@ -709,7 +709,7 @@ function createSqlFiles(internals, config, callback) { var templateTypeDefaultSQL = Migration.TemplateType.DEFAULT_SQL; var migrationUpSQL = new Migration(internals.argv.title + '-up.sql', - sqlDir, new Date(), templateTypeDefaultSQL); + sqlDir, internals.runTimestamp, templateTypeDefaultSQL); index.createMigration(migrationUpSQL, function(err, migration) { if (_assert(err, callback)) { @@ -717,7 +717,7 @@ function createSqlFiles(internals, config, callback) { migration.path)); var migrationDownSQL = new Migration(internals.argv.title + - '-down.sql', sqlDir, new Date(), templateTypeDefaultSQL); + '-down.sql', sqlDir, internals.runTimestamp, templateTypeDefaultSQL); index.createMigration(migrationDownSQL, function(err, migration) { if (_assert(err, callback)) { @@ -891,6 +891,8 @@ function run(internals, config) { var action = internals.argv._.shift(), folder = action.split(':'); + internals.runTimestamp = new Date(); + action = folder[0]; switch (action) { From ec24db40943c92e40f67ecc3cb0e39446cd85537 Mon Sep 17 00:00:00 2001 From: Tobias Gurtzick Date: Wed, 14 Dec 2016 17:35:12 +0100 Subject: [PATCH 12/13] fix(args): dont parse when called as module fixes #449 --- lib/commands/set-default-argv.js | 194 ++++++++++++++++--------------- 1 file changed, 103 insertions(+), 91 deletions(-) diff --git a/lib/commands/set-default-argv.js b/lib/commands/set-default-argv.js index bf45b635..3af0804f 100644 --- a/lib/commands/set-default-argv.js +++ b/lib/commands/set-default-argv.js @@ -5,99 +5,111 @@ module.exports = function(internals, isModule) { var rc = require('rc'); var deepExtend = require('deep-extend'); + var defaultConfig = { + verbose: false, + table: 'migrations', + 'seeds-table': 'seeds', + 'force-exit': false, + 'sql-file': false, + 'non-transactional': false, + config: internals.configFile || internals.cwd + '/database.json', + 'migrations-dir': internals.cwd + '/migrations', + 'vcseeder-dir': internals.cwd + '/VCSeeder', + 'staticseeder-dir': internals.cwd + '/Seeder', + 'ignore-completed-migrations': false + }; + + if(!isModule) { + + internals.argv = optimist + .default(defaultConfig) + .usage( + 'Usage: db-migrate [up|down|reset|sync|create|db|transition] ' + + '[[dbname/]migrationName|all] [options]' + ) + .describe('env', + 'The environment to run the migrations under (dev, test, prod).') + .alias('e', 'env') + .string('e') + + .describe('migrations-dir', 'The directory containing your migration files.') + .alias('m', 'migrations-dir') + .string('m') + + .describe('count', 'Max number of migrations to run.') + .alias('c', 'count') + .string('c') + + .describe('dry-run', 'Prints the SQL but doesn\'t run it.') + .boolean('dry-run') + + .describe('force-exit', 'Forcibly exit the migration process on completion.') + .boolean('force-exit') + + .describe('verbose', 'Verbose mode.') + .alias('v', 'verbose') + .boolean('v') + + .alias('h', 'help') + .alias('h', '?') + .boolean('h') + + .describe('version', 'Print version info.') + .alias('i', 'version') + .boolean('version') + + .describe('config', 'Location of the database.json file.') + .string('config') + + .describe('sql-file', + 'Automatically create two sql files for up and down statements in ' + + '/sqls and generate the javascript code that loads them.' + ) + .boolean('sql-file') + + .describe('coffee-file', 'Create a coffeescript migration file') + .boolean('coffee-file') + .describe('ignore-on-init', + 'Create files that will run only if ignore-on-init in the env is set ' + + 'to false (currently works onlt with SQL)' + ).boolean('ignore-on-init') + + .describe('migration-table', + 'Set the name of the migration table, which stores the migration history.' + ) + .alias('table', 'migration-table') + .alias('t', 'table') + .string('t') + + .describe('seeds-table', + 'Set the name of the seeds table, which stores the seed history.') + .string('seeds-table') + + .describe('vcseeder-dir', + 'Set the path to the Version Controlled Seeder directory.') + .string('vcseeder-dir') + + .describe('staticseeder-dir', 'Set the path to the Seeder directory.') + .string('staticseeder-dir') + + .describe('non-transactional', 'Explicitly disable transactions') + .boolean('non-transactional') + + .describe('ignore-completed-migrations', 'Start at the first migration') + .boolean('ignore-completed-migrations') + + .describe('log-level', 'Set the log-level, for example sql|warn') + .string('log-level'); + } + else { - internals.argv = optimist - .default({ - verbose: false, - table: 'migrations', - 'seeds-table': 'seeds', - 'force-exit': false, - 'sql-file': false, - 'non-transactional': false, - config: internals.configFile || internals.cwd + '/database.json', - 'migrations-dir': internals.cwd + '/migrations', - 'vcseeder-dir': internals.cwd + '/VCSeeder', - 'staticseeder-dir': internals.cwd + '/Seeder', - 'ignore-completed-migrations': false - }) - .usage( - 'Usage: db-migrate [up|down|reset|sync|create|db|transition] ' + - '[[dbname/]migrationName|all] [options]' - ) - .describe('env', - 'The environment to run the migrations under (dev, test, prod).') - .alias('e', 'env') - .string('e') - - .describe('migrations-dir', 'The directory containing your migration files.') - .alias('m', 'migrations-dir') - .string('m') - - .describe('count', 'Max number of migrations to run.') - .alias('c', 'count') - .string('c') - - .describe('dry-run', 'Prints the SQL but doesn\'t run it.') - .boolean('dry-run') - - .describe('force-exit', 'Forcibly exit the migration process on completion.') - .boolean('force-exit') - - .describe('verbose', 'Verbose mode.') - .alias('v', 'verbose') - .boolean('v') - - .alias('h', 'help') - .alias('h', '?') - .boolean('h') - - .describe('version', 'Print version info.') - .alias('i', 'version') - .boolean('version') - - .describe('config', 'Location of the database.json file.') - .string('config') - - .describe('sql-file', - 'Automatically create two sql files for up and down statements in ' + - '/sqls and generate the javascript code that loads them.' - ) - .boolean('sql-file') - - .describe('coffee-file', 'Create a coffeescript migration file') - .boolean('coffee-file') - .describe('ignore-on-init', - 'Create files that will run only if ignore-on-init in the env is set ' + - 'to false (currently works onlt with SQL)' - ).boolean('ignore-on-init') - - .describe('migration-table', - 'Set the name of the migration table, which stores the migration history.' - ) - .alias('table', 'migration-table') - .alias('t', 'table') - .string('t') - - .describe('seeds-table', - 'Set the name of the seeds table, which stores the seed history.') - .string('seeds-table') - - .describe('vcseeder-dir', - 'Set the path to the Version Controlled Seeder directory.') - .string('vcseeder-dir') - - .describe('staticseeder-dir', 'Set the path to the Seeder directory.') - .string('staticseeder-dir') - - .describe('non-transactional', 'Explicitly disable transactions') - .boolean('non-transactional') - - .describe('ignore-completed-migrations', 'Start at the first migration') - .boolean('ignore-completed-migrations') - - .describe('log-level', 'Set the log-level, for example sql|warn') - .string('log-level'); + internals.argv = { + get argv() { + return defaultConfig; + } + }; + } var plugins = internals.plugins; var plugin = plugins.hook('init:cli:config:hook'); From b0837d55151115fbf6697941034abd341fb7c305 Mon Sep 17 00:00:00 2001 From: Tobias Gurtzick Date: Tue, 11 Jul 2017 13:09:16 +0200 Subject: [PATCH 13/13] fix deep config replacements fixes #473 --- lib/config.js | 37 ++++++++++++++++++++++++------------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/lib/config.js b/lib/config.js index 2c9124fc..e6ddc6f9 100644 --- a/lib/config.js +++ b/lib/config.js @@ -83,13 +83,35 @@ exports.loadFile = function(fileName, currentEnv, plugins) { return exports.loadObject(config, currentEnv); }; +function walkConfig(level) { + + for (var configEntry in level) { + if (level[configEntry] && level[configEntry].ENV){ + + if(!process.env[level[configEntry].ENV]) { + + log.verbose('Environment variable ' + level[configEntry].ENV + + ' is empty!'); + } + + level[configEntry] = process.env[level[configEntry].ENV]; + } + else if(level[configEntry] && typeof(level[configEntry]) === 'object') { + + level[configEntry] = walkConfig(level[configEntry]); + } + } + + return level; +} + exports.loadObject = function(config, currentEnv) { var out = new Config(); for (var env in config) { if (config[env].ENV) { if(!process.env[config[env].ENV]) - log.verbose('Environment variable ' + config[env].ENV + 'is empty!'); + log.verbose('Environment variable ' + config[env].ENV + ' is empty!'); else out[env] = parseDatabaseUrl(process.env[config[env].ENV]); } else if (typeof(config[env]) === 'string') { @@ -97,18 +119,7 @@ exports.loadObject = function(config, currentEnv) { } else { //Check config entry's for ENV objects //which will tell us to grab configuration from the environment - for (var configEntry in config[env]) { - if (config[env][configEntry] && config[env][configEntry].ENV){ - - if(!process.env[config[env][configEntry].ENV]) { - - log.verbose('Environment variable ' + config[env][configEntry].ENV + - 'is empty!'); - } - - config[env][configEntry] = process.env[config[env][configEntry].ENV]; - } - } + config[env] = walkConfig(config[env]); out[env] = config[env]; } }