Migration runner - first iteration (#7501)

refs #7489
- add independent migratio runner
- add init script
- this is not connected to Ghost yet, but next PR will
This commit is contained in:
Katharina Irrgang 2016-10-10 14:27:31 +02:00 committed by Hannah Wolfe
parent 637d177cac
commit c4fa34224f
9 changed files with 257 additions and 0 deletions

View File

@ -0,0 +1,14 @@
var Promise = require('bluebird'),
commands = require('../../schema').commands,
logging = require('../../../logging'),
schema = require('../../schema').tables,
schemaTables = Object.keys(schema);
module.exports = function createTables(options) {
var database = options.database;
return Promise.mapSeries(schemaTables, function createTable(table) {
logging.info('Creating table: ' + table);
return commands.createTable(table, database);
});
};

View File

@ -0,0 +1,26 @@
var program = require('commander'),
Sephiroth = require('../'),
config = require('../../../config'),
logging = require('../../../logging'),
sephiroth = new Sephiroth({database: config.get('database')});
/**
* @TODO:
* - make migration folder configurable
* - dirty requires
*/
program
.command('init')
.description('populate tables')
.option('--init', 'populate tables')
.action(function () {
return sephiroth.commands.init()
.then(function () {
logging.info('Finished database init!');
}).catch(function (err) {
logging.error(err);
}).finally(process.exit);
});
program.parse(process.argv);

View File

@ -0,0 +1,15 @@
function Sephiroth(options) {
options = options || {};
this.commands = require('./lib/commands');
this.utils = require('./lib/utils');
this.database = require('./lib/database');
if (!options.database) {
this.utils.throwError({code: this.utils.errors.databaseConfigMissing});
}
this.database.connect(options.database);
}
module.exports = Sephiroth;

View File

@ -0,0 +1 @@
exports.init = require('./init');

View File

@ -0,0 +1,49 @@
var Promise = require('bluebird'),
path = require('path'),
utils = require('../utils'),
logging = require('../../../../logging');
/**
* @TODO:
* - better error handling
* - prettier code please
* - dirty requires
*/
module.exports = function init(options) {
options = options || {};
var migrationFolder = options.migrationFolder || path.join(__dirname, '../../../migrations'),
dbInitTasks = utils.readTasks(migrationFolder + '/init');
return utils.createTransaction(function executeTasks(transaction) {
return Promise.each(dbInitTasks, function executeInitTask(task) {
return utils.preTask({
database: transaction,
task: task.name,
type: 'init'
}).then(function () {
logging.info('Running: ' + task.name);
return task.execute({
database: transaction
});
}).then(function () {
return utils.postTask({
database: transaction,
task: task.name,
type: 'init'
});
}).catch(function (err) {
if (err.code === utils.errors.taskFound) {
logging.warn('Skipping:' + task.name);
return Promise.resolve();
}
throw err;
});
});
}).catch(function (err) {
logging.warn('rolling back...');
return Promise.reject(err);
});
};

View File

@ -0,0 +1,19 @@
var knex = require('knex');
/**
* we only support knex
*/
exports.connect = function connect(options) {
var client = options.client;
if (client === 'sqlite3') {
options.useNullAsDefault = options.useNullAsDefault || false;
}
if (client === 'mysql') {
options.connection.timezone = 'UTC';
options.connection.charset = 'utf8mb4';
}
exports.knex = knex(options);
};

View File

@ -0,0 +1,2 @@
exports.commands = require('./commands');
exports.utils = require('./utils');

View File

@ -0,0 +1,130 @@
var path = require('path'),
_ = require('lodash'),
fs = require('fs'),
database = require('./database'),
logging = require('../../../logging');
exports.errors = {
taskFound: 100,
unknown: 99,
migrationsTableMissing: 98,
dbInitMissing: 97,
databaseConfigMissing: 96
};
/**
* Sephiroth erorr handling for now
*/
exports.throwError = function throwError(options) {
var code = options.code,
err = new Error();
err.code = code;
throw err;
};
exports.readTasks = function readTasks(absolutePath) {
var files = [],
tasks = [];
try {
files = fs.readdirSync(absolutePath);
_.each(files, function (file) {
tasks.push({
execute: require(path.join(absolutePath, file)),
name: file
});
});
return tasks;
} catch (err) {
throw err;
}
};
exports.createTransaction = function createTransaction(callback) {
return database.knex.transaction(callback);
};
/**
* each migration file get's saved into the database
* @TODO: add version
*/
exports.preTask = function preTask(options) {
options = options || {};
var localDatabase = options.database,
task = options.task,
type = options.type;
return (localDatabase || database.knex)('migrations')
.then(function (migrations) {
if (!migrations.length) {
return;
}
if (_.find(migrations, {name: task, type: type})) {
exports.throwError({code: exports.errors.taskFound});
}
})
.catch(function (err) {
// CASE: table does not exist
if (err.errno === 1) {
logging.info('Creating table: migrations');
return (localDatabase || database.knex).schema.createTable('migrations', function (table) {
table.string('name');
table.string('type');
});
}
throw err;
});
};
/**
* write migration key
*/
exports.postTask = function postTask(options) {
options = options || {};
var localDatabase = options.database,
task = options.task,
type = options.type;
return (localDatabase || database.knex)('migrations')
.insert({
name: task,
type: type
});
};
/**
* - check init
* - check seed
*
* @TODO: optimise!
*/
exports.isDatabaseOK = function isDatabaseOK(options) {
options = options || {};
var localDatabase = options.database;
return (localDatabase || database.knex)('migrations')
.then(function (migrations) {
if (_.find(migrations, {type: 'init'})) {
return;
}
exports.throwError({code: exports.errors.dbInitMissing});
})
.catch(function (err) {
// CASE: table does not exist
if (err.errno === 1) {
exports.throwError({code: exports.errors.dbInitMissing});
}
exports.throwError({code: exports.errors.unknown});
});
};

View File

@ -34,6 +34,7 @@
"bunyan": "1.8.1",
"chalk": "1.1.3",
"cheerio": "0.22.0",
"commander": "2.9.0",
"compression": "1.6.2",
"connect-slashes": "1.3.1",
"cookie-session": "1.2.0",