knex migrator (#7565)

refs #7489
- remove sephiroth
- use knex migrator npm
- goodbye bootup script
- 🎨  update README
- 🎨  knex migrator @ 0.0.2
This commit is contained in:
Katharina Irrgang 2016-10-17 14:50:29 +02:00 committed by Hannah Wolfe
parent d55f5b9860
commit 8d8d7bdb26
18 changed files with 32 additions and 350 deletions

8
.knex-migrator Normal file
View File

@ -0,0 +1,8 @@
#!/usr/bin/env node
var config = require('./core/server/config');
module.exports = {
database: config.get('database'),
migrationPath: config.get('paths:migrationPath')
}

View File

@ -49,6 +49,12 @@ Install grunt
npm install -g grunt-cli
```
Install knex-migrator
```bash
npm install -g knex-migrator
```
Install Ghost
```bash

View File

@ -8,7 +8,8 @@
"adminViews": "core/server/views/",
"internalAppPath": "core/server/apps/",
"internalStoragePath": "core/server/storage/",
"internalSchedulingPath": "core/server/scheduling/"
"internalSchedulingPath": "core/server/scheduling/",
"migrationPath": "core/server/data/migrations"
},
"internalApps": [
"private-blogging",

View File

@ -1,12 +0,0 @@
var Sephiroth = require('../sephiroth'),
config = require('./../../config'),
sephiroth = new Sephiroth({database: config.get('database')});
/**
* @TODO:
* - move this file out of schema folder
* - key: migrations-kate
*/
module.exports = function bootUp() {
return sephiroth.utils.isDatabaseOK();
};

View File

@ -3,4 +3,3 @@ module.exports.checks = require('./checks');
module.exports.commands = require('./commands');
module.exports.versioning = require('./versioning');
module.exports.defaultSettings = require('./default-settings');
module.exports.bootUp = require('./bootup');

View File

@ -1,26 +0,0 @@
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

@ -1,12 +0,0 @@
function Sephiroth(options) {
options = options || {};
this.commands = require('./lib/commands');
this.utils = require('./lib/utils');
this.database = require('./lib/database');
this.database.connect(options.database);
}
module.exports = Sephiroth;
module.exports.errors = require('./lib/errors');

View File

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

View File

@ -1,61 +0,0 @@
var Promise = require('bluebird'),
utils = require('../utils'),
errors = require('../errors'),
logging = require('../../../../logging');
/**
* @TODO:
* - better error handling
* - prettier code please
* - dirty requires
*
* sephiroth init --only 1 (execute first script only)
* sephiroth init --skip 2 (execute all except of 2)
* sephiroth migrate --version 1.0 --only 1
*/
module.exports = function init(options) {
options = options || {};
var initPath = utils.getPath({type: 'init'}),
dbInitTasks = utils.readTasks(initPath),
skip = options.skip || null,
only = options.only || null;
if (only !== null) {
dbInitTasks = [dbInitTasks[only - 1]];
} else if (skip !== null) {
dbInitTasks.splice(skip - 1, 1);
}
return utils.createTransaction(function executeTasks(transacting) {
return Promise.each(dbInitTasks, function executeInitTask(task) {
return utils.preTask({
transacting: transacting,
task: task.name,
type: 'init'
}).then(function () {
logging.info('Running: ' + task.name);
return task.execute({
transacting: transacting
});
}).then(function () {
return utils.postTask({
transacting: transacting,
task: task.name,
type: 'init'
});
}).catch(function (err) {
if (err instanceof errors.MigrationExistsError) {
logging.warn('Skipping:' + task.name);
return Promise.resolve();
}
throw err;
});
});
}).catch(function (err) {
logging.warn('rolling back...');
return Promise.reject(err);
});
};

View File

@ -1,20 +0,0 @@
var knex = require('knex');
/**
* we only support knex
*/
exports.connect = function connect(options) {
options = 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

@ -1,59 +0,0 @@
var _ = require('lodash'),
util = require('util');
function SephirothError(options) {
options = options || {};
var self = this;
if (_.isString(options)) {
throw new Error('Please instantiate Errors with the option pattern. e.g. new errors.SephirothError({message: ...})');
}
Error.call(this);
Error.captureStackTrace(this, SephirothError);
/**
* defaults
*/
this.statusCode = 500;
this.errorType = this.name = 'SephirothError';
this.id = 0;
/**
* option overrides
*/
this.id = options.id || this.id;
this.message = options.message || this.message;
this.code = options.code || this.code;
this.errorType = this.name = options.errorType || this.errorType;
// error to inherit from, override!
if (options.err) {
Object.getOwnPropertyNames(options.err).forEach(function (property) {
self[property] = options.err[property] || self[property];
});
}
}
// jscs:disable
var errors = {
MigrationExistsError: function MigrationExistsError(options) {
SephirothError.call(this, _.merge({
id: 100,
errorType: 'MigrationExistsError'
}, options));
},
DatabaseIsNotOkError: function DatabaseIsNotOkError(options) {
SephirothError.call(this, _.merge({
id: 200,
errorType: 'DatabaseIsNotOkError'
}, options));
}
};
_.each(errors, function (error) {
util.inherits(error, SephirothError);
});
module.exports = errors;
module.exports.SephirothError = SephirothError;

View File

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

View File

@ -1,139 +0,0 @@
var path = require('path'),
_ = require('lodash'),
fs = require('fs'),
database = require('./database'),
errors = require('./errors'),
logging = require('../../../logging');
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 new errors.SephirothError({err: 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.transacting,
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})) {
throw new errors.MigrationExistsError();
}
})
.catch(function (err) {
// CASE: table does not exist
if (err.errno === 1 || err.errno === 1146) {
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.transacting,
task = options.task,
type = options.type;
return (localDatabase || database.knex)('migrations')
.insert({
name: task,
type: type
});
};
/**
* DB health depends on the amount of executed init scripts right now
*
* @TODO:
* - alternative for checking length of init scripts?
*/
exports.isDatabaseOK = function isDatabaseOK(options) {
options = options || {};
var localDatabase = options.transacting,
initPath = exports.getPath({type: 'init'}),
dbInitTasksLength = exports.readTasks(initPath).length;
return (localDatabase || database.knex)('migrations')
.then(function (migrations) {
if (_.filter(migrations, {type: 'init'}).length === dbInitTasksLength) {
return;
}
throw new errors.DatabaseIsNotOkError({
message: 'Please run node core/server/data/sephiroth/bin/sephiroth init.',
code: 'DB_NOT_INITIALISED'
});
})
.catch(function (err) {
if (err.errno === 1 || err.errno === 1146) {
throw new errors.DatabaseIsNotOkError({
message: 'Please run node core/server/data/sephiroth/bin/sephiroth init.',
code: 'MIGRATION_TABLE_IS_MISSING'
});
}
throw new errors.SephirothError({
err: err
});
});
};
/**
* @TODO:
* - make migrationPath configureable
*/
exports.getPath = function getPath(options) {
options = options || {};
var migrationsPath = path.join(__dirname, '../../migrations');
switch (options.type) {
case 'init':
migrationsPath += '/init';
break;
}
return migrationsPath;
};

View File

@ -16,10 +16,10 @@ require('./overrides');
var debug = require('debug')('ghost:boot:init'),
uuid = require('node-uuid'),
Promise = require('bluebird'),
KnexMigrator = require('knex-migrator'),
config = require('./config'),
i18n = require('./i18n'),
api = require('./api'),
config = require('./config'),
db = require('./data/schema'),
models = require('./models'),
permissions = require('./permissions'),
apps = require('./apps'),
@ -30,6 +30,7 @@ var debug = require('debug')('ghost:boot:init'),
scheduling = require('./scheduling'),
readDirectory = require('./utils/read-directory'),
utils = require('./utils'),
knexMigrator = new KnexMigrator(),
dbHash;
function initDbHashAndFirstRun() {
@ -78,8 +79,8 @@ function init(options) {
debug('Themes & apps done');
models.init();
// @TODO: this is temporary, replace migrations with a warning if a DB exists
return db.bootUp();
}).then(function () {
return knexMigrator.isDatabaseOK();
}).then(function () {
// Populate any missing default settings
return models.Settings.populateDefaults();

View File

@ -10,7 +10,6 @@ var should = require('should'),
errors = require(config.get('paths').corePath + '/server/errors'),
permissions = require(config.get('paths').corePath + '/server/permissions'),
api = require(config.get('paths').corePath + '/server/api'),
Sephiroth = require(config.get('paths').corePath + '/server/data/sephiroth'),
apps = require(config.get('paths').corePath + '/server/apps'),
i18n = require(config.get('paths').corePath + '/server/i18n'),
xmlrpc = require(config.get('paths').corePath + '/server/data/xml/xmlrpc'),
@ -65,7 +64,6 @@ describe('server bootstrap', function () {
.catch(function (err) {
migration.populate.calledOnce.should.eql(false);
config.get('maintenance').enabled.should.eql(false);
(err instanceof Sephiroth.errors.DatabaseIsNotOkError).should.eql(true);
err.code.should.eql('MIGRATION_TABLE_IS_MISSING');
done();
});

View File

@ -5,9 +5,9 @@ var cp = require('child_process'),
net = require('net'),
Promise = require('bluebird'),
path = require('path'),
config = require('../../server/config'),
Sephiroth = require('../../server/data/sephiroth'),
sephiroth = new Sephiroth({database: config.get('database')});
KnexMigrator = require('knex-migrator'),
config = require('../../server/config'),
knexMigrator = new KnexMigrator();
function findFreePort(port) {
return new Promise(function (resolve, reject) {
@ -49,7 +49,7 @@ function forkGhost(newConfig) {
.then(function (_port) {
port = _port;
return sephiroth.commands.init();
return knexMigrator.init();
})
.then(function () {
newConfig.server = _.merge({}, {

View File

@ -4,9 +4,9 @@ var Promise = require('bluebird'),
path = require('path'),
Module = require('module'),
uuid = require('node-uuid'),
KnexMigrator = require('knex-migrator'),
ghost = require('../../server'),
db = require('../../server/data/db'),
Sephiroth = require('../../server/data/sephiroth'),
migration = require('../../server/data/migration/'),
fixtureUtils = require('../../server/data/migration/fixtures/utils'),
models = require('../../server/models'),
@ -19,7 +19,7 @@ var Promise = require('bluebird'),
fork = require('./fork'),
mocks = require('./mocks'),
config = require('../../server/config'),
sephiroth = new Sephiroth({database: config.get('database')}),
knexMigrator = new KnexMigrator(),
fixtures,
getFixtureOps,
toDoList,
@ -402,7 +402,7 @@ fixtures = {
/** Test Utility Functions **/
initData = function initData() {
return sephiroth.commands.init();
return knexMigrator.init();
};
clearData = function clearData() {
@ -478,10 +478,10 @@ getFixtureOps = function getFixtureOps(toDos) {
fixtureOps.push(function initDB() {
// skip adding all fixtures!
if (tablesOnly) {
return sephiroth.commands.init({skip: 2});
return knexMigrator.init({skip: 2});
}
return sephiroth.commands.init();
return knexMigrator.init();
});
delete toDos.default;
@ -656,7 +656,7 @@ unmockNotExistingModule = function unmockNotExistingModule() {
* 2. start ghost
*/
startGhost = function startGhost() {
return sephiroth.commands.init()
return knexMigrator.init()
.then(function () {
return ghost();
});

View File

@ -56,6 +56,7 @@
"intl-messageformat": "1.3.0",
"jsonpath": "0.2.7",
"knex": "0.12.5",
"knex-migrator": "0.0.2",
"lodash": "4.16.4",
"mobiledoc-html-renderer": "0.3.0",
"moment": "2.15.1",