abstract-migrate
Version:
Storage-agnostic migration runner
529 lines (413 loc) • 17 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.framework = exports.needsToRun = exports.run = exports.loadFromFs = exports.DIRECTION_ROLLBACK = exports.DIRECTION_DOWN = exports.DIRECTION_UP = undefined;
var _typeof2 = require('babel-runtime/helpers/typeof');
var _typeof3 = _interopRequireDefault(_typeof2);
var _defineProperty2 = require('babel-runtime/helpers/defineProperty');
var _defineProperty3 = _interopRequireDefault(_defineProperty2);
var _regenerator = require('babel-runtime/regenerator');
var _regenerator2 = _interopRequireDefault(_regenerator);
var _promise = require('babel-runtime/core-js/promise');
var _promise2 = _interopRequireDefault(_promise);
var _asyncToGenerator2 = require('babel-runtime/helpers/asyncToGenerator');
var _asyncToGenerator3 = _interopRequireDefault(_asyncToGenerator2);
var _intro, _nothing;
var loadFromFs = exports.loadFromFs = function () {
var _ref = (0, _asyncToGenerator3.default)(_regenerator2.default.mark(function _callee(migrationPath) {
return _regenerator2.default.wrap(function _callee$(_context) {
while (1) {
switch (_context.prev = _context.next) {
case 0:
return _context.abrupt('return', new _promise2.default(function (resolve, reject) {
_fs2.default.readdir(migrationPath, function (err, files) {
if (err) {
reject(err);
} else {
resolve(files.filter(function (file) {
return file.match(/^\d+-[\w\d_-]+\.js$/);
}).map(function (file) {
return file.slice(0, -3);
}).sort());
}
});
}));
case 1:
case 'end':
return _context.stop();
}
}
}, _callee, this);
}));
return function loadFromFs(_x) {
return _ref.apply(this, arguments);
};
}();
var run = exports.run = function () {
var _ref2 = (0, _asyncToGenerator3.default)(_regenerator2.default.mark(function _callee2(filename, direction, timestamp) {
var migration;
return _regenerator2.default.wrap(function _callee2$(_context2) {
while (1) {
switch (_context2.prev = _context2.next) {
case 0:
migration = require(_path2.default.join(_config2.default.migrationPath, filename));
if (!(direction === DIRECTION_UP)) {
_context2.next = 6;
break;
}
_context2.next = 4;
return (0, _promise3.promiseback)(migration.up);
case 4:
_context2.next = 12;
break;
case 6:
if (!(direction === DIRECTION_DOWN || direction === DIRECTION_ROLLBACK)) {
_context2.next = 11;
break;
}
_context2.next = 9;
return (0, _promise3.promiseback)(migration.down);
case 9:
_context2.next = 12;
break;
case 11:
throw new Error('Unknown migration direction');
case 12:
return _context2.abrupt('return', { name: filename, timestamp: timestamp });
case 13:
case 'end':
return _context2.stop();
}
}
}, _callee2, this);
}));
return function run(_x2, _x3, _x4) {
return _ref2.apply(this, arguments);
};
}();
var needsToRun = exports.needsToRun = function () {
var _ref12 = (0, _asyncToGenerator3.default)(_regenerator2.default.mark(function _callee3(direction, options) {
var ranMigrations, files;
return _regenerator2.default.wrap(function _callee3$(_context3) {
while (1) {
switch (_context3.prev = _context3.next) {
case 0:
_context3.next = 2;
return _storage2.default.load();
case 2:
ranMigrations = _context3.sent;
// Sort the ran migrations by their name -- newest migration first
ranMigrations.sort(function (_ref13, _ref14) {
var left = _ref13.name;
var right = _ref14.name;
return right.localeCompare(left);
});
_context3.next = 6;
return loadFromFs(_config2.default.migrationPath);
case 6:
files = _context3.sent;
_context3.t0 = direction;
_context3.next = _context3.t0 === DIRECTION_UP ? 10 : _context3.t0 === DIRECTION_DOWN ? 11 : _context3.t0 === DIRECTION_ROLLBACK ? 12 : 13;
break;
case 10:
return _context3.abrupt('return', filterUp(ranMigrations, files, options));
case 11:
return _context3.abrupt('return', filterDown(ranMigrations, files, options));
case 12:
return _context3.abrupt('return', filterRollback(ranMigrations, files, options));
case 13:
throw new Error('Unknown migration direct \'' + direction);
case 14:
case 'end':
return _context3.stop();
}
}
}, _callee3, this);
}));
return function needsToRun(_x5, _x6) {
return _ref12.apply(this, arguments);
};
}();
var framework = exports.framework = function () {
var _ref15 = (0, _asyncToGenerator3.default)(_regenerator2.default.mark(function _callee6(direction, name, options) {
var _this = this;
var t, UP, needsLock, lockAcquired, _ret2;
return _regenerator2.default.wrap(function _callee6$(_context6) {
while (1) {
switch (_context6.prev = _context6.next) {
case 0:
t = mapWords(direction);
UP = direction === DIRECTION_UP;
needsLock = !options.dryRun;
console.log(_chalk2.default.gray(t('intro')));
if (!needsLock) {
_context6.next = 11;
break;
}
_context6.next = 7;
return _storage2.default.acquireLock();
case 7:
lockAcquired = _context6.sent;
if (lockAcquired) {
_context6.next = 11;
break;
}
console.log(_chalk2.default.yellow.bold('Could not lock') + _chalk2.default.yellow(' Lock could not be acquired, quitting'));
return _context6.abrupt('return');
case 11:
_context6.prev = 11;
return _context6.delegateYield(_regenerator2.default.mark(function _callee5() {
var nameValue, migrationsToRun, now, ranMigrations;
return _regenerator2.default.wrap(function _callee5$(_context5) {
while (1) {
switch (_context5.prev = _context5.next) {
case 0:
nameValue = (0, _arguments.nameOrNumber)(name);
_context5.next = 3;
return needsToRun(direction, {
ignorePast: UP ? options.ignorePast : undefined,
only: !!options.only,
until: typeof nameValue !== 'number' ? nameValue : null,
count: typeof nameValue === 'number' ? nameValue : null
});
case 3:
migrationsToRun = _context5.sent;
if (migrationsToRun.length) {
_context5.next = 7;
break;
}
console.log(t('nothing'));
return _context5.abrupt('return', {
v: void 0
});
case 7:
if (!options.dryRun) {
_context5.next = 11;
break;
}
console.log(_chalk2.default.yellow.bold('Dry run') + _chalk2.default.yellow(' No migrations will be executed'));
migrationsToRun.forEach(function (file) {
console.log('[✓] ' + _chalk2.default.cyan(file));
});
return _context5.abrupt('return', {
v: void 0
});
case 11:
// Record time once so rollback functions properly
now = Date.now();
// Run the migrations serially
_context5.next = 14;
return (0, _promise3.mapSeries)(migrationsToRun, function () {
var _ref16 = (0, _asyncToGenerator3.default)(_regenerator2.default.mark(function _callee4(file) {
var migration;
return _regenerator2.default.wrap(function _callee4$(_context4) {
while (1) {
switch (_context4.prev = _context4.next) {
case 0:
_context4.prev = 0;
_context4.next = 3;
return run(file, direction, now);
case 3:
migration = _context4.sent;
console.log('[✓] ' + _chalk2.default.cyan(file));
return _context4.abrupt('return', migration);
case 8:
_context4.prev = 8;
_context4.t0 = _context4['catch'](0);
console.log('[✘] ' + _chalk2.default.red(file));
console.log(_chalk2.default.yellow.bold('Migration failed!') + _chalk2.default.yellow(' Your database may be in an invalid state.'));
throw _context4.t0;
case 13:
case 'end':
return _context4.stop();
}
}
}, _callee4, _this, [[0, 8]]);
}));
return function (_x10) {
return _ref16.apply(this, arguments);
};
}());
case 14:
ranMigrations = _context5.sent;
if (!UP) {
_context5.next = 20;
break;
}
_context5.next = 18;
return _storage2.default.add(ranMigrations.slice().reverse());
case 18:
_context5.next = 22;
break;
case 20:
_context5.next = 22;
return _storage2.default.remove(ranMigrations);
case 22:
case 'end':
return _context5.stop();
}
}
}, _callee5, _this);
})(), 't0', 13);
case 13:
_ret2 = _context6.t0;
if (!((typeof _ret2 === 'undefined' ? 'undefined' : (0, _typeof3.default)(_ret2)) === "object")) {
_context6.next = 16;
break;
}
return _context6.abrupt('return', _ret2.v);
case 16:
_context6.prev = 16;
if (!needsLock) {
_context6.next = 20;
break;
}
_context6.next = 20;
return _storage2.default.releaseLock();
case 20:
return _context6.finish(16);
case 21:
case 'end':
return _context6.stop();
}
}
}, _callee6, this, [[11,, 16, 21]]);
}));
return function framework(_x7, _x8, _x9) {
return _ref15.apply(this, arguments);
};
}();
exports.filterUp = filterUp;
exports.filterDown = filterDown;
exports.filterRollback = filterRollback;
var _fs = require('fs');
var _fs2 = _interopRequireDefault(_fs);
var _path = require('path');
var _path2 = _interopRequireDefault(_path);
var _chalk = require('chalk');
var _chalk2 = _interopRequireDefault(_chalk);
var _config = require('../config');
var _config2 = _interopRequireDefault(_config);
var _storage = require('./storage');
var _storage2 = _interopRequireDefault(_storage);
var _arguments = require('./arguments');
var _promise3 = require('./promise');
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var DIRECTION_UP = exports.DIRECTION_UP = 'direction/up';
var DIRECTION_DOWN = exports.DIRECTION_DOWN = 'direction/down';
var DIRECTION_ROLLBACK = exports.DIRECTION_ROLLBACK = 'direction/rollback';
function filterUp(ranMigrations, files, _ref3) {
var _ref3$ignorePast = _ref3.ignorePast,
ignorePast = _ref3$ignorePast === undefined ? false : _ref3$ignorePast,
until = _ref3.until,
count = _ref3.count,
only = _ref3.only;
var migrationsToRun = void 0;
if (!ranMigrations.length) {
// We have no ran migrations, run them all!
migrationsToRun = files;
} else if (ignorePast) {
(function () {
// Only grab migrations since the most recently ran
var timestamp = ranMigrations[0].name.match(/^(\d+)-/)[1];
migrationsToRun = files.filter(function (filename) {
return filename.match(/^(\d+)-/)[1] > timestamp;
});
})();
} else {
// Grab all of the unran migrations
migrationsToRun = files.filter(function (file) {
return !ranMigrations.find(function (migration) {
return file === migration.name;
});
});
}
// If we're requesting a specific migration, check for validity and return
if (only) {
if (!until) {
throw new Error('A migration name must be provided with the --only flag');
}
return migrationsToRun.filter(function (file) {
return file === until;
});
}
if (until) {
return migrationsToRun.filter(function (file) {
return file <= until;
});
}
if (count) {
return migrationsToRun.slice(0, count);
}
return migrationsToRun;
}
function filterDown(ranMigrations, files, _ref4) {
var until = _ref4.until,
count = _ref4.count,
only = _ref4.only;
if (!ranMigrations) {
return [];
}
var migrationsToRun = void 0;
if (only) {
if (!until) {
throw new Error('A migration name must be provided with the --only flag');
}
migrationsToRun = ranMigrations.filter(function (_ref5) {
var name = _ref5.name;
return name === until;
});
} else if (count) {
migrationsToRun = ranMigrations.slice(0, count);
} else if (until) {
migrationsToRun = ranMigrations.filter(function (_ref6) {
var name = _ref6.name;
return name >= until;
});
} else {
throw new Error('A downward migration requires either a migration name or count');
}
migrationsToRun = migrationsToRun.map(function (_ref7) {
var name = _ref7.name;
return name;
});
migrationsToRun.forEach(function (migration) {
if (files.indexOf(migration) === -1) {
throw new Error('Migration \'' + migration + '\' cannot be downgraded because it does not have a migration file');
}
});
return migrationsToRun;
}
function filterRollback(ranMigrations, files) {
if (!ranMigrations || !ranMigrations.length) {
return [];
}
var ranMigrationsByDate = ranMigrations.slice().sort(function (_ref8, _ref9) {
var left = _ref8.timestamp;
var right = _ref9.timestamp;
return right - left;
});
var migrationsToRun = ranMigrationsByDate.filter(function (_ref10) {
var timestamp = _ref10.timestamp;
return timestamp === ranMigrationsByDate[0].timestamp;
}).map(function (_ref11) {
var name = _ref11.name;
return name;
}).sort().reverse();
migrationsToRun.forEach(function (migration) {
if (files.indexOf(migration) === -1) {
throw new Error('Migration \'' + migration + '\' cannot be downgraded because it does not have a migration file');
}
});
return migrationsToRun;
}
var wordMap = {
intro: (_intro = {}, (0, _defineProperty3.default)(_intro, DIRECTION_UP, 'Running migrations'), (0, _defineProperty3.default)(_intro, DIRECTION_DOWN, 'Running down migrations'), (0, _defineProperty3.default)(_intro, DIRECTION_ROLLBACK, 'Rolling back migrations'), _intro),
nothing: (_nothing = {}, (0, _defineProperty3.default)(_nothing, DIRECTION_UP, 'There are no pending migrations'), (0, _defineProperty3.default)(_nothing, DIRECTION_DOWN, 'There are no migrations to run down'), (0, _defineProperty3.default)(_nothing, DIRECTION_ROLLBACK, 'There are no migrations to roll back'), _nothing)
};
var mapWords = function mapWords(direction) {
return function (topic) {
return wordMap[topic][direction];
};
};