mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-03-11 02:12:21 -05:00
✨ 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:
parent
637d177cac
commit
c4fa34224f
9 changed files with 257 additions and 0 deletions
14
core/server/data/migrations/init/1-create-tables.js
Normal file
14
core/server/data/migrations/init/1-create-tables.js
Normal 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);
|
||||
});
|
||||
};
|
26
core/server/data/sephiroth/bin/sephiroth.js
Normal file
26
core/server/data/sephiroth/bin/sephiroth.js
Normal 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);
|
15
core/server/data/sephiroth/index.js
Normal file
15
core/server/data/sephiroth/index.js
Normal 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;
|
1
core/server/data/sephiroth/lib/commands/index.js
Normal file
1
core/server/data/sephiroth/lib/commands/index.js
Normal file
|
@ -0,0 +1 @@
|
|||
exports.init = require('./init');
|
49
core/server/data/sephiroth/lib/commands/init.js
Normal file
49
core/server/data/sephiroth/lib/commands/init.js
Normal 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);
|
||||
});
|
||||
};
|
19
core/server/data/sephiroth/lib/database.js
Normal file
19
core/server/data/sephiroth/lib/database.js
Normal 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);
|
||||
};
|
2
core/server/data/sephiroth/lib/index.js
Normal file
2
core/server/data/sephiroth/lib/index.js
Normal file
|
@ -0,0 +1,2 @@
|
|||
exports.commands = require('./commands');
|
||||
exports.utils = require('./utils');
|
130
core/server/data/sephiroth/lib/utils.js
Normal file
130
core/server/data/sephiroth/lib/utils.js
Normal 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});
|
||||
});
|
||||
};
|
|
@ -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",
|
||||
|
|
Loading…
Add table
Reference in a new issue