mirror of https://github.com/TryGhost/Ghost.git
✨ 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:
parent
d55f5b9860
commit
8d8d7bdb26
|
@ -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')
|
||||
}
|
|
@ -49,6 +49,12 @@ Install grunt
|
|||
npm install -g grunt-cli
|
||||
```
|
||||
|
||||
Install knex-migrator
|
||||
|
||||
```bash
|
||||
npm install -g knex-migrator
|
||||
```
|
||||
|
||||
Install Ghost
|
||||
|
||||
```bash
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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();
|
||||
};
|
|
@ -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');
|
||||
|
|
|
@ -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);
|
|
@ -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');
|
|
@ -1 +0,0 @@
|
|||
exports.init = require('./init');
|
|
@ -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);
|
||||
});
|
||||
};
|
|
@ -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);
|
||||
};
|
|
@ -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;
|
|
@ -1,2 +0,0 @@
|
|||
exports.commands = require('./commands');
|
||||
exports.utils = require('./utils');
|
|
@ -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;
|
||||
};
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
});
|
||||
|
|
|
@ -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({}, {
|
||||
|
|
|
@ -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();
|
||||
});
|
||||
|
|
|
@ -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",
|
||||
|
|
Loading…
Reference in New Issue