Skip to content

Commit 93e9f18

Browse files
RandomSeededwzrdtales
RandomSeeded
authored andcommitted
feat(check): add check functionality to determine migrations to run
Signed-off-by: RandomSeeded <nate@blend.com>
1 parent 721b31e commit 93e9f18

File tree

7 files changed

+172
-3
lines changed

7 files changed

+172
-3
lines changed

api.js

+25
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,31 @@ dbmigrate.prototype = {
227227
).asCallback(callback);
228228
},
229229

230+
check: function (specification, opts, callback) {
231+
var executeCheck = load('check');
232+
233+
if (arguments.length > 0) {
234+
if (typeof specification === 'number') {
235+
this.internals.argv.count = arguments[0];
236+
} else if (typeof specification === 'function') {
237+
callback = specification;
238+
}
239+
240+
if (typeof opts === 'string') {
241+
this.internals.migrationMode = opts;
242+
this.internals.matching = opts;
243+
} else if (typeof opts === 'function') {
244+
callback = opts;
245+
}
246+
}
247+
248+
return Promise.fromCallback(
249+
function (callback) {
250+
executeCheck(this.internals, this.config, callback);
251+
}.bind(this)
252+
).asCallback(callback);
253+
},
254+
230255
/**
231256
* Executes up a given number of migrations or a specific one.
232257
*

lib/commands/check.js

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
var path = require('path');
2+
var log = require('db-migrate-shared').log;
3+
var assert = require('./helper/assert.js');
4+
var migrationHook = require('./helper/migration-hook.js');
5+
6+
module.exports = function (internals, config, callback) {
7+
migrationHook(internals)
8+
.then(function () {
9+
var Migrator = require('../migrator.js');
10+
var index = require('../../connect');
11+
12+
if (!internals.argv.count) {
13+
internals.argv.count = Number.MAX_VALUE;
14+
}
15+
index.connect({
16+
config: config.getCurrent().settings,
17+
internals: internals
18+
}, Migrator, function (err, migrator) {
19+
if (!assert(err, callback)) return;
20+
21+
if (internals.locTitle) {
22+
migrator.migrationsDir = path.resolve(internals.argv['migrations-dir'],
23+
internals.locTitle);
24+
} else { migrator.migrationsDir = path.resolve(internals.argv['migrations-dir']); }
25+
26+
internals.migrationsDir = migrator.migrationsDir;
27+
28+
migrator.driver.createMigrationsTable(function (err) {
29+
if (!assert(err, callback)) return;
30+
log.verbose('migration table created');
31+
32+
migrator.check(internals.argv, internals.onComplete.bind(this,
33+
migrator, internals, callback));
34+
});
35+
});
36+
});
37+
};

lib/commands/run.js

+6-2
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ function run (internals, config) {
4242
break;
4343
case 'up':
4444
case 'down':
45+
case 'check':
4546
case 'reset':
4647
if (action === 'reset') internals.argv.count = Number.MAX_VALUE;
4748

@@ -62,9 +63,12 @@ function run (internals, config) {
6263
if (action === 'up') {
6364
var executeUp = load('up');
6465
executeUp(internals, config);
65-
} else {
66+
} else if (action === 'down') {
6667
var executeDown = load('down');
6768
executeDown(internals, config);
69+
} else {
70+
var executeCheck = load('check');
71+
executeCheck(internals, config);
6872
}
6973
break;
7074

@@ -104,7 +108,7 @@ function run (internals, config) {
104108
);
105109
} else {
106110
log.error(
107-
'Invalid Action: Must be [up|down|create|reset|sync|' +
111+
'Invalid Action: Must be [up|down|check|create|reset|sync|' +
108112
'db|transition].'
109113
);
110114
optimist.showHelp();

lib/commands/set-default-argv.js

+7-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ module.exports = function (internals, isModule) {
2222
internals.argv = optimist
2323
.default(defaultConfig)
2424
.usage(
25-
'Usage: db-migrate [up|down|reset|sync|create|db|transition] ' +
25+
'Usage: db-migrate [up|down|check|reset|sync|create|db|transition] ' +
2626
'[[dbname/]migrationName|all] [options]'
2727
)
2828
.describe(
@@ -42,6 +42,8 @@ module.exports = function (internals, isModule) {
4242
.string('c')
4343
.describe('dry-run', "Prints the SQL but doesn't run it.")
4444
.boolean('dry-run')
45+
.describe('check', 'Prints the migrations to be run without running them.')
46+
.boolean('check')
4547
.describe(
4648
'force-exit',
4749
'Forcibly exit the migration process on completion.'
@@ -145,8 +147,12 @@ module.exports = function (internals, isModule) {
145147
internals.notransactions = internals.argv['non-transactional'];
146148
internals.dryRun = internals.argv['dry-run'];
147149
global.dryRun = internals.dryRun;
150+
internals.check = internals.argv['check'];
148151

149152
if (internals.dryRun) {
150153
log.info('dry run');
151154
}
155+
if (internals.check) {
156+
log.info('check');
157+
}
152158
};

lib/migrator.js

+48
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,40 @@ Migrator.prototype = {
119119
}
120120
},
121121

122+
check: function (funcOrOpts, callback) {
123+
var self = this;
124+
Migration.loadFromFilesystem(self.migrationsDir, self.internals, function (
125+
err,
126+
allMigrations
127+
) {
128+
if (err) {
129+
callback(err);
130+
return;
131+
}
132+
133+
Migration.loadFromDatabase(
134+
self.migrationsDir,
135+
self._driver,
136+
self.internals,
137+
function (err, completedMigrations) {
138+
if (err) {
139+
callback(err);
140+
return;
141+
}
142+
143+
// Requires pr to export filterCompleted from db-migrate-shared
144+
var toRun = dbmUtil.filterCompleted(
145+
allMigrations,
146+
completedMigrations
147+
);
148+
149+
log.info('Migrations to run:', toRun.map(migration => migration.name));
150+
callback(null, toRun);
151+
}
152+
);
153+
});
154+
},
155+
122156
sync: function (funcOrOpts, callback) {
123157
var self = this;
124158

@@ -180,6 +214,13 @@ Migrator.prototype = {
180214
return;
181215
}
182216

217+
if (self.internals.check) {
218+
var toRunNames = toRun.map(migration => migration.name);
219+
log.info('Migrations to run:', toRunNames);
220+
callback(null, toRunNames);
221+
return;
222+
}
223+
183224
return Promise.resolve(toRun)
184225
.each(function (migration) {
185226
log.verbose('preparing to run up migration:', migration.name);
@@ -234,6 +275,13 @@ Migrator.prototype = {
234275
return;
235276
}
236277

278+
if (self.internals.check) {
279+
var toRunNames = toRun.map(migration => migration.name);
280+
log.info('Migrations to run:', toRunNames);
281+
callback(null, toRunNames);
282+
return;
283+
}
284+
237285
return Promise.resolve(toRun)
238286
.each(function (migration) {
239287
log.verbose('preparing to run down migration:', migration.name);

test/integration/api_test.js

+17
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,23 @@ lab.experiment('api', function () {
168168
}
169169
);
170170

171+
lab.test(
172+
'should handle all check parameter variations properly',
173+
174+
function () {
175+
return Promise.resolve([
176+
[], // promise
177+
[sinon.spy()],
178+
[1, sinon.spy()], // targeted migration
179+
[1], // promise targeted migration
180+
[1, 'testscope', sinon.spy()], // scoped target
181+
[1, 'testscope'] // promise scope target
182+
])
183+
.each(defaultExecParams('check'))
184+
.each(spyCallback);
185+
}
186+
);
187+
171188
lab.test(
172189
'should handle all reset parameter variations properly',
173190

test/migrator_test.js

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
var Code = require('code');
2+
var Lab = require('lab');
3+
var proxyquire = require('proxyquire').noPreserveCache();
4+
var lab = (exports.lab = Lab.script());
5+
6+
lab.experiment('migrators', function () {
7+
lab.experiment('check', function () {
8+
lab.test('should return the migrations to be run', function (done) {
9+
var completedMigration = {
10+
name: '20180330020329-thisMigrationIsCompleted'
11+
};
12+
var uncompletedMigration = {
13+
name: '20180330020330-thisMigrationIsNotCompleted'
14+
};
15+
var Migrator = proxyquire('../lib/migrator.js', {
16+
'./migration': {
17+
loadFromFilesystem: (migrationsDir, internals, cb) => {
18+
return cb(null, [completedMigration, uncompletedMigration]);
19+
},
20+
loadFromDatabase: (migrationsDir, driver, internals, cb) => {
21+
return cb(null, [completedMigration]);
22+
}
23+
}
24+
});
25+
Migrator.prototype.check(null, function (err, res) {
26+
Code.expect(res.length).to.equal(1);
27+
Code.expect(res[0].name).to.equal(uncompletedMigration.name);
28+
done(err, res);
29+
});
30+
});
31+
});
32+
});

0 commit comments

Comments
 (0)