From 10c22687aac88b85b6438aebe83fe335dc9d7a9b Mon Sep 17 00:00:00 2001 From: Lewis Patterson Date: Thu, 12 Mar 2015 11:42:18 -0500 Subject: [PATCH 1/3] Added 'do' and 'undo' commands. --- .gitignore | 3 +- .mite | 11 +-- README.md | 16 +++-- database/createDB | 10 +++ database/miteTest.sql | 12 ++++ lib/cli/do.js | 52 ++++++++++++++ lib/cli/index.js | 22 +++++- lib/cli/undo.js | 52 ++++++++++++++ lib/cli/up.js | 12 +++- lib/mite.js | 145 +++++++++++++++++++++++++++++++------- migrations/migration1.sql | 5 ++ migrations/migration2.sql | 5 ++ migrations/migration3.sql | 5 ++ migrations/migration4.sql | 5 ++ migrations/migration5.sql | 5 ++ migrations/migration6.sql | 5 ++ migrations/migration7.sql | 5 ++ package.json | 4 +- 18 files changed, 332 insertions(+), 42 deletions(-) create mode 100644 database/createDB create mode 100644 database/miteTest.sql create mode 100644 lib/cli/do.js create mode 100644 lib/cli/undo.js create mode 100644 migrations/migration1.sql create mode 100644 migrations/migration2.sql create mode 100644 migrations/migration3.sql create mode 100644 migrations/migration4.sql create mode 100644 migrations/migration5.sql create mode 100644 migrations/migration6.sql create mode 100644 migrations/migration7.sql diff --git a/.gitignore b/.gitignore index 1403724..49cf9a3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,2 @@ node_modules/* -mite_test_home/* -mite.sublime-workspace \ No newline at end of file +.idea diff --git a/.mite b/.mite index 7f8b471..ef943b3 100644 --- a/.mite +++ b/.mite @@ -1,5 +1,8 @@ { - "ignorePaths": [ - "**/*" - ] -} + "host": "localhost", + "port": 3306, + "user": "whiteboard", + "password": "fnb1015", + "dialect": "mysql", + "database": "miteTest" +} \ No newline at end of file diff --git a/README.md b/README.md index 79c12e1..3bd5bde 100644 --- a/README.md +++ b/README.md @@ -55,21 +55,25 @@ mite uses a lot of `git` style subcommands. You can execute mite commands from a **create** - creates a new empty migration in the `migrations/` directory. -**up** - runs any unexecuted migrations. The status must not be dirty prior to runing `mite up` +**up** - runs any unexecuted migrations. The status must not be dirty prior to running `mite up`. -**stepup** - run the first unexecuted migration +**do file-name** - runs the specified migration. The status must not be dirty. + +**undo file-name** - reverses the specified migration. The status must not be dirty. + +**stepup** - run the first unexecuted migration. **down** - run the down migrations from the current *head* all the way down to the first migration. This is destructive and should never be run in a production environment. **stepdown** - run the down of the last executed migration. This is destructive and should never be run in a production environment. -**submodules** - list all submodules +**submodules** - list all submodules. -**compare** - list the current disk hash + the tracked hash of all migrations +**compare** - list the current disk hash + the tracked hash of all migrations. -**help** - print usage information +**help** - print usage information. -**help [command]** - print usage information for a specific command +**help [command]** - print usage information for a specific command. ## Configuration diff --git a/database/createDB b/database/createDB new file mode 100644 index 0000000..4e248f6 --- /dev/null +++ b/database/createDB @@ -0,0 +1,10 @@ +# Remove any existing instance of miteTest database. +drop database `miteTest`; +# Create a new instance of miteTest database. +CREATE DATABASE `miteTest`; +# Grant access to the database. +grant all privileges on `miteTest`.* to whiteboard@localhost identified by 'fnb1015'; +# --------------------------------------------------------------------------------------------------------------------- +# Load copy of database for testing. +use miteTest; +source miteTest.sql diff --git a/database/miteTest.sql b/database/miteTest.sql new file mode 100644 index 0000000..14a2fbc --- /dev/null +++ b/database/miteTest.sql @@ -0,0 +1,12 @@ +DROP TABLE IF EXISTS `Files`; +CREATE TABLE `Files` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `filename` varchar(2048) NOT NULL, + `fileSize` int(11) DEFAULT NULL, + `userId` char(36) DEFAULT NULL, + `createdAt` datetime NOT NULL, + `updatedAt` datetime NOT NULL, + `visibleToAll` tinyint(1) DEFAULT '1', + PRIMARY KEY (`id`), + UNIQUE KEY `id` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; diff --git a/lib/cli/do.js b/lib/cli/do.js new file mode 100644 index 0000000..bf11c4e --- /dev/null +++ b/lib/cli/do.js @@ -0,0 +1,52 @@ +var Mite = require("../mite"), + reporter = require("./reporter"), + MigrationProvider = require("../migrationProvider"); + +var cliUtil = require("./util"), + printMigrationList = cliUtil.printMigrationList, + connectionError = cliUtil.connectionError, + handleUninitialized = cliUtil.handleUninitialized; + +module.exports = DoCommand; + +function DoCommand(fileName) { + this.fileName= fileName; +} + +DoCommand.prototype.preExecute = function(config) { + var mite = new Mite(config); + + return mite.connect() + .fail(connectionError) + .then(mite._requireInit.bind(mite)) + .fail(handleUninitialized) + .then(function(){ + return mite; + }); +}; + +DoCommand.prototype.execute = function(mite) { + var opts = { + submodule: mite.config.name + }, + provider = new MigrationProvider(mite.config.migrationRoot, opts), + executed = function(key) { + reporter.success("do: %s...", key); + }; + + mite.on("migrationExecuted", executed); + + return mite.doMigration(provider.getMigrations(), this.fileName, opts).then(function(doStatus) { + if(doStatus.error){ + reporter.err(doStatus.error); + } else if (doStatus.updated) { + reporter.success("complete"); + } else if (!doStatus.updated && doStatus.wasClean) { + reporter.warn("no migration executed. status was clean"); + } else if (doStatus.dirtyMigrations) { + printMigrationList("no migration executed. status is dirty. fix it.", "err", doStatus.dirtyMigrations, "err"); + } + + mite.removeListener("migrationExecuted", executed); + }); +}; diff --git a/lib/cli/index.js b/lib/cli/index.js index 32d5bf9..a1bd02d 100755 --- a/lib/cli/index.js +++ b/lib/cli/index.js @@ -22,6 +22,8 @@ var UpCommand = require("./up"), StatusCommand = require("./status"), CreateCommand = require("./create"), StepUpCommand = require("./stepUp"), + DoCommand = require("./do"), + UnDoCommand = require("./undo"), StepDownCommand = require("./stepDown"), InitCommand = require("./init"), DownCommand = require("./down"), @@ -60,9 +62,11 @@ var upCli = program .command("up") .description("run all unexecuted migrations") .option("-a --all", "execute an up on all submodules and then the main project migrations") + .option("-f --force","force the up action") .action(function() { runCommand(new UpCommand({ - all: upCli.all + all: upCli.all, + force: upCli.force })); }); @@ -77,6 +81,22 @@ program runCommand(new StepUpCommand()); }); +program + .command("do") + .description("run the specified migration; syntax: do file-name") + .action(function(fileName) { + console.log("do: fileName="+fileName); + runCommand(new DoCommand(fileName)); + }); + +program + .command("undo") + .description("reverse the specified migration; syntax: undo file-name") + .action(function(fileName) { + console.log("undo: fileName="+fileName); + runCommand(new UnDoCommand(fileName)); + }); + var downCli = program .command("down") .description("run all the down migrations") diff --git a/lib/cli/undo.js b/lib/cli/undo.js new file mode 100644 index 0000000..663341b --- /dev/null +++ b/lib/cli/undo.js @@ -0,0 +1,52 @@ +var Mite = require("../mite"), + reporter = require("./reporter"), + MigrationProvider = require("../migrationProvider"); + +var cliUtil = require("./util"), + printMigrationList = cliUtil.printMigrationList, + connectionError = cliUtil.connectionError, + handleUninitialized = cliUtil.handleUninitialized; + +module.exports = UnDoCommand; + +function UnDoCommand(fileName) { + this.fileName= fileName; +} + +UnDoCommand.prototype.preExecute = function(config) { + var mite = new Mite(config); + + return mite.connect() + .fail(connectionError) + .then(mite._requireInit.bind(mite)) + .fail(handleUninitialized) + .then(function(){ + return mite; + }); +}; + +UnDoCommand.prototype.execute = function(mite) { + var opts = { + submodule: mite.config.name + }, + provider = new MigrationProvider(mite.config.migrationRoot, opts), + executed = function(key) { + reporter.success("undo: %s...", key); + }; + + mite.on("migrationExecuted", executed); + + return mite.undoMigration(provider.getMigrations(), this.fileName, opts).then(function(undoStatus) { + if(undoStatus.error){ + reporter.err(undoStatus.error); + } else if (undoStatus.updated) { + reporter.success("complete"); + } else if (!undoStatus.updated && undoStatus.wasClean) { + reporter.warn("no migration executed. status was clean"); + } else if (undoStatus.dirtyMigrations) { + printMigrationList("no migration executed. status is dirty. fix it.", "err", undoStatus.dirtyMigrations, "err"); + } + + mite.removeListener("migrationExecuted", executed); + }); +}; diff --git a/lib/cli/up.js b/lib/cli/up.js index e3166ea..893c234 100644 --- a/lib/cli/up.js +++ b/lib/cli/up.js @@ -15,7 +15,8 @@ module.exports = UpCommand; function UpCommand(opts) { this.options = _.extend({ - all: false + all: false, + force: false }, opts); } @@ -80,7 +81,8 @@ UpCommand.prototype.execute = function(mite) { UpCommand.prototype._up = function(mite) { var opts = { - submodule: mite.config.name + submodule: mite.config.name, + force: this.options.force }, provider = new MigrationProvider(mite.config.migrationRoot, opts), notifier = function(migrationKey) { @@ -90,7 +92,11 @@ UpCommand.prototype._up = function(mite) { mite.on("migrationExecuted", notifier); var p = mite.up(provider.getMigrations(), opts).then(function(upStatus) { - if (!upStatus.updated && upStatus.wasClean) { + if(upStatus.someExecuted){ + reporter.warn("No migrations executed. The following migrations have already been performed."); + reporter.warn("Conflict list: "+upStatus.conflictList.join(',')); + reporter.warn("Use -f (or --force) flag to force execution or 'undo' the migrations listed."); + }else if (!upStatus.updated && upStatus.wasClean) { reporter.warn("no migrations executed. status is clean."); } else if (!upStatus.updated && upStatus.dirtyMigrations) { printMigrationList("no migrations executed. there are dirty migrations. fix it.", "err", upStatus.dirtyMigrations, "err"); diff --git a/lib/mite.js b/lib/mite.js index dc4dd2a..99d2546 100755 --- a/lib/mite.js +++ b/lib/mite.js @@ -71,7 +71,7 @@ Mite.prototype.init = function () { Mite.prototype.allMigrations = function(opts) { opts = _.extend({ - submodule: ".", + submodule: "." }, opts || {}); return this.migrationRepo.all(opts.submodule); @@ -146,6 +146,7 @@ Mite.prototype.status = function (diskMigrations, opts) { }; Mite.prototype.up = function (diskMigrations, opts) { + var localDebug= false; var self = this; opts = opts || {}; @@ -167,10 +168,29 @@ Mite.prototype.up = function (diskMigrations, opts) { }; } else { // unexecuted migrations var target = diskMigrations[diskMigrations.length - 1]; - return self.stepTo(diskMigrations, target, _.extend(opts, { - status: miteStatus, - init: true - })); + var someExecuted= _.some(miteStatus.executedMigrations,function(key){ + return (key > miteStatus.unexecutedMigrations[0] && key < miteStatus.unexecutedMigrations[miteStatus.unexecutedMigrations.length - 1]) || key > miteStatus.unexecutedMigrations[miteStatus.unexecutedMigrations.length - 1] + }); + if(localDebug){ + console.log("up: diskMigrations="+JSON.stringify(diskMigrations)); + console.log("up: target="+JSON.stringify(target)); + console.log("up: miteStatus="+JSON.stringify(miteStatus)); + console.log("up: opts="+JSON.stringify(opts)); + console.log("up: someExecuted="+someExecuted); + } + if(someExecuted && !opts.force){ + return { + someExecuted: true, + conflictList: _.filter(miteStatus.executedMigrations,function(key){ + return (key > miteStatus.unexecutedMigrations[0] && key < miteStatus.unexecutedMigrations[miteStatus.unexecutedMigrations.length - 1]) || key > miteStatus.unexecutedMigrations[miteStatus.unexecutedMigrations.length - 1] + }) + }; + }else{ + return self.stepTo("up", diskMigrations, target, _.extend(opts, { + status: miteStatus, + init: true + })); + } } }); }); @@ -201,7 +221,7 @@ Mite.prototype.stepUp = function(diskMigrations, opts) { return m.key === miteStatus.unexecutedMigrations[0]; })[0]; - return self.stepTo(diskMigrations, target, _.extend(opts, { + return self.stepTo("up", diskMigrations, target, _.extend(opts, { init: true, status: miteStatus })); @@ -223,7 +243,7 @@ Mite.prototype.down = function(diskMigrations, opts) { noExecutedMigrations: true }; } else { - return self.stepTo(diskMigrations, "bottom", _.extend(opts, { + return self.stepTo("down", diskMigrations, "bottom", _.extend(opts, { init: true, status: miteStatus })); @@ -264,7 +284,7 @@ Mite.prototype.stepDown = function(diskMigrations, opts) { })[0]; } - return self.stepTo(diskMigrations, target, _.extend(opts, { + return self.stepTo("down", diskMigrations, target, _.extend(opts, { init: true, status: miteStatus })); @@ -273,12 +293,19 @@ Mite.prototype.stepDown = function(diskMigrations, opts) { }); }; -Mite.prototype.stepTo = function(diskMigrations, target, opts) { - var self = this, - bottom = target === "bottom"; +Mite.prototype.stepTo = function(direction, diskMigrations, target, opts) { + var localDebug= false; + var self = this; + var bottom = target === "bottom"; opts = opts || {}; + if(localDebug){ + console.log("stepTo: diskMigrations="+JSON.stringify(diskMigrations)); + console.log("stepTo: target="+JSON.stringify(target)); + console.log("stepTo: opts="+JSON.stringify(opts)); + } + return q.when(opts.init || self._requireInit()).then(function() { if (typeof target === "string" && !bottom) { target = diskMigrations.filter(function(m) { @@ -293,17 +320,18 @@ Mite.prototype.stepTo = function(diskMigrations, target, opts) { }; } - // make double sure they are in ascending order + // Make sure diskMigrations are in ascending order. diskMigrations = _.sortBy(diskMigrations, function(m) { return m.key; }); return q.when(opts.status || self.status(diskMigrations, _.extend(opts, {init: true}))).then(function(mStatus) { - var executed = mStatus.executedMigrations, - head = executed[executed.length - 1]; + var executed = mStatus.executedMigrations ? mStatus.executedMigrations.sort() : []; + var unexecuted = mStatus.unexecutedMigrations ? mStatus.unexecutedMigrations.sort() : []; + var head = executed[executed.length - 1]; - // up - if (!head || (!bottom && (head < target.key))) { + // Which direction? + if (direction === "up") { // cant go up with dirty migrations. bail out if (mStatus.dirtyMigrations) { return { @@ -312,12 +340,12 @@ Mite.prototype.stepTo = function(diskMigrations, target, opts) { }; } + // We need the migrations which have not been executed. var upMigrations = diskMigrations.filter(function(m) { - return m.key <= target.key && !(mStatus.executedMigrations || []).some(function(em) { - return em === m.key; - }); + return _.indexOf(unexecuted, m.key) > -1 && m.key <= target.key; }); + // Execute the migrations serially. return upMigrations.reduce(function(p, m) { return p.then(self.migrationRepo.executeUpMigration.bind(self.migrationRepo, m)).then(function() { self.emit("migrationExecuted", m.key); @@ -328,19 +356,19 @@ Mite.prototype.stepTo = function(diskMigrations, target, opts) { }; }); } else { // down - // migrations in the path, in descsending order + // Execute migrations in descending order. var downMigrations = diskMigrations.filter(function(m) { return (bottom ? true : m.key > target.key) && !(mStatus.unexecutedMigrations || []).some(function(um) { return um === m.key; }); }).reverse(); - // validate that we can actually step all the way to the target + // Validate that we can actually step all the way to the target. var missingDowns = downMigrations.filter(function(m) { return !m.down || m.down.length === 0; }); - // bail out + // Do we have a problem? if (missingDowns.length > 0) { return { updated: false, @@ -352,7 +380,7 @@ Mite.prototype.stepTo = function(diskMigrations, target, opts) { }; } - // execute the downs serially + // Execute the migrations serially. return downMigrations.reduce(function(p, m) { return p.then(self.migrationRepo.executeDownMigration.bind(self.migrationRepo, m)).then(function() { self.emit("migrationExecuted", m.key); @@ -365,4 +393,73 @@ Mite.prototype.stepTo = function(diskMigrations, target, opts) { } }); }); -}; \ No newline at end of file +}; + +Mite.prototype.doMigration = function(diskMigrations, target, opts) { + var self = this; + opts = opts || {}; + + return q.when(opts.init || self._requireInit()).then(function() { + return q.when(opts.status || self.status(diskMigrations, _.extend(opts, {init: true}))).then(function(miteStatus) { + // Find the target in diskMigrations. + var index= _.findIndex(diskMigrations,function(m){ + return m.key === target; + }); + if(index > -1){ + // Found it so execute the up migration. + var m= diskMigrations[index]; + var f= self.migrationRepo.executeUpMigration.bind(self.migrationRepo, m); + return f().then( + function(result) { + self.emit("migrationExecuted", m.key); + if(result.affectedRows > 0){ + miteStatus.updated= true; + return miteStatus; + }else{ + return miteStatus; + } + } + ); + }else{ + // Could not find the specified migration. + miteStatus.error= "The specified migration could not be found."; + return miteStatus; + } + }); + }); +}; + +Mite.prototype.undoMigration = function(diskMigrations, target, opts) { + var self = this; + opts = opts || {}; + + return q.when(opts.init || self._requireInit()).then(function() { + return q.when(opts.status || self.status(diskMigrations, _.extend(opts, {init: true}))).then(function(miteStatus) { + // Find the target in diskMigrations. + var index= _.findIndex(diskMigrations,function(m){ + return m.key === target; + }); + if(index > -1){ + // Found it so execute the down migration. + var m= diskMigrations[index]; + var f= self.migrationRepo.executeDownMigration.bind(self.migrationRepo, m); + return f().then( + function(result) { + self.emit("migrationExecuted", m.key); + if(result.affectedRows > 0){ + miteStatus.updated= true; + return miteStatus; + }else{ + return miteStatus; + } + } + ); + }else{ + // Could not find the specified migration. + miteStatus.error= "The specified migration could not be found."; + return miteStatus; + } + }); + }); +}; + diff --git a/migrations/migration1.sql b/migrations/migration1.sql new file mode 100644 index 0000000..c1e83c4 --- /dev/null +++ b/migrations/migration1.sql @@ -0,0 +1,5 @@ +/* mite:up */ +alter table Files add column mfield1 char(40) after userId; + +/* mite:down */ +alter table Files drop column mfield1; diff --git a/migrations/migration2.sql b/migrations/migration2.sql new file mode 100644 index 0000000..236768e --- /dev/null +++ b/migrations/migration2.sql @@ -0,0 +1,5 @@ +/* mite:up */ +alter table Files add column mfield2 char(40) after userId; + +/* mite:down */ +alter table Files drop column mfield2; diff --git a/migrations/migration3.sql b/migrations/migration3.sql new file mode 100644 index 0000000..d995846 --- /dev/null +++ b/migrations/migration3.sql @@ -0,0 +1,5 @@ +/* mite:up */ +alter table Files add column mfield3 char(40) after userId; + +/* mite:down */ +alter table Files drop column mfield3; diff --git a/migrations/migration4.sql b/migrations/migration4.sql new file mode 100644 index 0000000..dd7d506 --- /dev/null +++ b/migrations/migration4.sql @@ -0,0 +1,5 @@ +/* mite:up */ +alter table Files add column mfield4 char(40) after userId; + +/* mite:down */ +alter table Files drop column mfield4; diff --git a/migrations/migration5.sql b/migrations/migration5.sql new file mode 100644 index 0000000..d2d24cc --- /dev/null +++ b/migrations/migration5.sql @@ -0,0 +1,5 @@ +/* mite:up */ +alter table Files add column mfield5 char(40) after userId; + +/* mite:down */ +alter table Files drop column mfield5; diff --git a/migrations/migration6.sql b/migrations/migration6.sql new file mode 100644 index 0000000..b51dd1b --- /dev/null +++ b/migrations/migration6.sql @@ -0,0 +1,5 @@ +/* mite:up */ +alter table Files add column mfield6 char(40) after userId; + +/* mite:down */ +alter table Files drop column mfield6; diff --git a/migrations/migration7.sql b/migrations/migration7.sql new file mode 100644 index 0000000..3f447c3 --- /dev/null +++ b/migrations/migration7.sql @@ -0,0 +1,5 @@ +/* mite:up */ +alter table Files add column mfield7 char(40) after userId; + +/* mite:down */ +alter table Files drop column mfield7; diff --git a/package.json b/package.json index 919f9e3..e5da621 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,7 @@ }, "dependencies": { "chalk": "^0.4.0", - "commander": "~2.1.0", + "commander": "~2.7.0", "dependency-graph": "^0.1.0", "diff": "~1.0.8", "minimatch": "^0.2.14", @@ -41,7 +41,7 @@ "mysql": "~2.0.1", "q": "~1.0.0", "tedious": "^1.1.0", - "underscore": "~1.6.0" + "underscore": ">0.0.0" }, "devDependencies": { "jasmine-node": "~1.13.1", From 203c5376d2f911b3081a050fbf7cdaa1799f1b75 Mon Sep 17 00:00:00 2001 From: Lewis Patterson Date: Fri, 3 Apr 2015 13:50:17 -0500 Subject: [PATCH 2/3] Corrections. Changes to permit 'mite up -f' to operate even when the 'dirty' state is detected. --- lib/mite.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/mite.js b/lib/mite.js index 99d2546..0156c61 100755 --- a/lib/mite.js +++ b/lib/mite.js @@ -161,7 +161,7 @@ Mite.prototype.up = function (diskMigrations, opts) { updated: false, wasClean: true }; - } else if (miteStatus.dirtyMigrations) { + } else if (miteStatus.dirtyMigrations && !opts.force) { return { updated: false, dirtyMigrations: miteStatus.dirtyMigrations @@ -169,7 +169,7 @@ Mite.prototype.up = function (diskMigrations, opts) { } else { // unexecuted migrations var target = diskMigrations[diskMigrations.length - 1]; var someExecuted= _.some(miteStatus.executedMigrations,function(key){ - return (key > miteStatus.unexecutedMigrations[0] && key < miteStatus.unexecutedMigrations[miteStatus.unexecutedMigrations.length - 1]) || key > miteStatus.unexecutedMigrations[miteStatus.unexecutedMigrations.length - 1] + return (miteStatus.unexecutedMigrations && key > miteStatus.unexecutedMigrations[0] && key < miteStatus.unexecutedMigrations[miteStatus.unexecutedMigrations.length - 1]) || (miteStatus.unexecutedMigrations && key > miteStatus.unexecutedMigrations[miteStatus.unexecutedMigrations.length - 1]) }); if(localDebug){ console.log("up: diskMigrations="+JSON.stringify(diskMigrations)); @@ -182,7 +182,7 @@ Mite.prototype.up = function (diskMigrations, opts) { return { someExecuted: true, conflictList: _.filter(miteStatus.executedMigrations,function(key){ - return (key > miteStatus.unexecutedMigrations[0] && key < miteStatus.unexecutedMigrations[miteStatus.unexecutedMigrations.length - 1]) || key > miteStatus.unexecutedMigrations[miteStatus.unexecutedMigrations.length - 1] + return (miteStatus.unexecutedMigrations && key > miteStatus.unexecutedMigrations[0] && key < miteStatus.unexecutedMigrations[miteStatus.unexecutedMigrations.length - 1]) || (miteStatus.unexecutedMigrations && key > miteStatus.unexecutedMigrations[miteStatus.unexecutedMigrations.length - 1]) }) }; }else{ @@ -333,7 +333,7 @@ Mite.prototype.stepTo = function(direction, diskMigrations, target, opts) { // Which direction? if (direction === "up") { // cant go up with dirty migrations. bail out - if (mStatus.dirtyMigrations) { + if (mStatus.dirtyMigrations && !opts.force) { return { updated: false, dirtyMigrations: mStatus.dirtyMigrations From 39e5507eddc21af12c04f9233f674caf66eb9fda Mon Sep 17 00:00:00 2001 From: Lewis Patterson Date: Fri, 1 May 2015 10:26:29 -0500 Subject: [PATCH 3/3] Changes to provide more information for 'dirty' condition. During an 'up --force' operation, 'dirty' actions which are ignored are reported. --- lib/cli/status.js | 2 +- lib/cli/up.js | 8 ++++++-- lib/mite.js | 12 ++++++++++-- migrations/migration8.sql | 5 +++++ 4 files changed, 22 insertions(+), 5 deletions(-) create mode 100644 migrations/migration8.sql diff --git a/lib/cli/status.js b/lib/cli/status.js index 4722836..881beb6 100644 --- a/lib/cli/status.js +++ b/lib/cli/status.js @@ -133,7 +133,7 @@ StatusCommand.prototype._printStatus = function(status) { if (status.dirtyMigrations) { var messages = status.dirtyMigrations.map(function(dirtyMigration) { - return dirtyMigration.key + ":: expected [" + dirtyMigration.hash + "]"; + return dirtyMigration.key + ":: expected [" + dirtyMigration.hash + "] " + (dirtyMigration.explanation ? dirtyMigration.explanation : ""); }); printMigrationList("dirty migrations:", "info", messages, "err"); diff --git a/lib/cli/up.js b/lib/cli/up.js index 893c234..6d9d4e8 100644 --- a/lib/cli/up.js +++ b/lib/cli/up.js @@ -4,6 +4,7 @@ var Mite = require("../mite"), submoduleProviderFactory = require("../submoduleProviderFactory"), SubmoduleTree = require("../submoduleTree"), _ = require("underscore"), + util = require("util"), q = require("q"); var cliUtil = require("./util"), @@ -97,9 +98,12 @@ UpCommand.prototype._up = function(mite) { reporter.warn("Conflict list: "+upStatus.conflictList.join(',')); reporter.warn("Use -f (or --force) flag to force execution or 'undo' the migrations listed."); }else if (!upStatus.updated && upStatus.wasClean) { - reporter.warn("no migrations executed. status is clean."); + reporter.warn("No migrations executed. status is clean."); } else if (!upStatus.updated && upStatus.dirtyMigrations) { - printMigrationList("no migrations executed. there are dirty migrations. fix it.", "err", upStatus.dirtyMigrations, "err"); + reporter.warn("No migrations executed. There are dirty migrations. Please fix the problem(s)."); + _.each(upStatus.dirtyMigrations,function(dm){ + reporter.warn(util.format(" dirty: key=%s hash=%s explanation=%s",dm.key,dm.hash,dm.explanation)) + }); } else if (upStatus.updated) { reporter.success("complete"); } diff --git a/lib/mite.js b/lib/mite.js index 0156c61..ba8eb3e 100755 --- a/lib/mite.js +++ b/lib/mite.js @@ -2,7 +2,8 @@ var path = require("path"), q = require("q"), EventEmitter = require("events").EventEmitter, util = require("util"), - _ = require("underscore"); + reporter = require("./cli/reporter"), + _ = require("underscore"); module.exports = Mite; @@ -106,8 +107,10 @@ Mite.prototype.status = function (diskMigrations, opts) { if(disk && !db) { unexecuted.push(disk); } else if(db && !disk) { + db.explanation= "In database but not found on disk."; dirty.push(db); } else if(disk.hash !== db.hash) { + disk.explanation= "The disk file hash cannot be found in database."; dirty.push(disk); } }); @@ -136,7 +139,7 @@ Mite.prototype.status = function (diskMigrations, opts) { if (dirty.length > 0) { result.dirtyMigrations = dirty.map(function (x) { - return {key:x.key, hash: x.hash}; + return {key:x.key, hash: x.hash, explanation: x.explanation}; }).sort(function(a,b){ return a.key - b.key}); } @@ -340,6 +343,11 @@ Mite.prototype.stepTo = function(direction, diskMigrations, target, opts) { }; } + // Report problems which we are ignoring. + _.each(mStatus.dirtyMigrations,function(dm){ + reporter.warn(util.format(" ignored: key=%s hash=%s explanation=%s",dm.key,dm.hash,dm.explanation)) + }); + // We need the migrations which have not been executed. var upMigrations = diskMigrations.filter(function(m) { return _.indexOf(unexecuted, m.key) > -1 && m.key <= target.key; diff --git a/migrations/migration8.sql b/migrations/migration8.sql new file mode 100644 index 0000000..96195a5 --- /dev/null +++ b/migrations/migration8.sql @@ -0,0 +1,5 @@ +/* mite:up */ +alter table Files add column mfield8 char(40) after userId; + +/* mite:down */ +alter table Files drop column mfield8;