From 6a9011e2fee06e73d5710f10b0aff6317a6f098b Mon Sep 17 00:00:00 2001 From: alan bount Date: Thu, 30 Jul 2015 13:33:49 -0400 Subject: [PATCH] Fix to fail gracefully if current version missing Use Case: Given developer has a branch with a new Migration version. When the developer changes branch back to master/integration. Then the Migration should not throw an Error, crashing Meteor. Change _findIndexByVersion() to return false if not found. Change test for index into _migrateTo(), start=graceful fail, end=error. Add a new test to verify functionality. --- migrations_server.js | 34 ++++++++++++++++++++++------------ migrations_tests.js | 17 +++++++++++++++-- package.js | 2 +- 3 files changed, 38 insertions(+), 15 deletions(-) diff --git a/migrations_server.js b/migrations_server.js index 152169a..69c5f06 100644 --- a/migrations_server.js +++ b/migrations_server.js @@ -84,7 +84,7 @@ Migrations.migrateTo = function(command) { // remember to run meteor with --once otherwise it will restart if (subcommand === 'exit') - process.exit(0); + process.exit(0); } // just returns the current version @@ -118,7 +118,18 @@ Migrations._migrateTo = function(version, rerun) { } var startIdx = this._findIndexByVersion(currentVersion); + if (typeof startIdx === 'boolean' && !startIdx) { + // fail gracefully, if the current version is not found + console.log('Not migrating, current version not found.'); + return; + } + var endIdx = this._findIndexByVersion(version); + if (typeof endIdx === 'boolean' && !endIdx) { + // throw error, if the destination version is not found + console.log('Not migrating, target version not found. (throwing error)'); + throw new Meteor.Error('Can\'t find migration version ' + version); + } // console.log('startIdx:' + startIdx + ' endIdx:' + endIdx); console.log('Migrating from version ' + this._list[startIdx].version @@ -127,17 +138,17 @@ Migrations._migrateTo = function(version, rerun) { // run the actual migration function migrate(direction, idx) { var migration = self._list[idx]; - + if (typeof migration[direction] !== 'function') { - throw new Meteor.Error('Cannot migrate ' + direction + ' on version ' + throw new Meteor.Error('Cannot migrate ' + direction + ' on version ' + migration.version); } - function maybeName() { + function maybeName() { return migration.name ? ' (' + migration.name + ')' : ''; } - console.log('Running ' + direction + '() on version ' + console.log('Running ' + direction + '() on version ' + migration.version + maybeName()); migration[direction].call(); } @@ -166,7 +177,7 @@ Migrations._migrateTo = function(version, rerun) { } // gets the current control record, optionally creating it if non-existant -Migrations._getControl = function() { +Migrations._getControl = function() { var control = this._collection.findOne({_id: 'control'}); return control || this._setControl({version: 0, locked: false}); @@ -178,24 +189,23 @@ Migrations._setControl = function(control) { check(control.version, Number); check(control.locked, Boolean); - this._collection.update({_id: 'control'}, + this._collection.update({_id: 'control'}, {$set: {version: control.version, locked: control.locked}}, {upsert: true}); return control; } -// returns the migration index in _list or throws if not found +// returns the migration index in _list or false Migrations._findIndexByVersion = function(version) { for (var i = 0;i < this._list.length;i++) { if (this._list[i].version === version) - return i; + return i; } - - throw new Meteor.Error('Can\'t find migration version ' + version); + return false; } //reset (mainly intended for tests) Migrations._reset = function() { this._list = [{version: 0, up: function(){}}]; this._collection.remove({}); -} \ No newline at end of file +} diff --git a/migrations_tests.js b/migrations_tests.js index fbd01c5..02b4120 100644 --- a/migrations_tests.js +++ b/migrations_tests.js @@ -96,7 +96,7 @@ Tinytest.add('Tests migrating down to version 0', function(test) { Migrations._reset(); test.equal(Migrations.getVersion(), 0); - + Migrations.add({ up: function () {run.push('u1');}, down: function () {run.push('d1');}, @@ -163,4 +163,17 @@ Tinytest.add('Checks that rerun works correctly', function(test) { Migrations.migrateTo('1,rerun'); test.equal(run, ['u1', 'u1']); test.equal(Migrations.getVersion(), 1); -}); \ No newline at end of file +}); + +Tinytest.add('Checks that it fails gracefully if a current migration is missing', function(test) { + Migrations._reset(); + + // set the "current" version to a non-existent record, 999 + Migrations._setControl({version: 999, locked: false}); + test.equal(Migrations.getVersion(), 999); + + // shouldnt do anything (no errors) + Migrations.migrateTo('latest'); + test.equal(Migrations.getVersion(), 999); +}); + diff --git a/package.js b/package.js index 6584124..ef7eedd 100644 --- a/package.js +++ b/package.js @@ -1,6 +1,6 @@ Package.describe({ summary: "Allows you to define and run db migrations.", - version: "0.7.4", + version: "0.7.5", name: "percolate:migrations", git: "https://github.com/percolatestudio/meteor-migrations.git" });