Skip to content
This repository has been archived by the owner on Jun 15, 2020. It is now read-only.

Commit

Permalink
Migrations with CLI and without knexfile (knex#2884)
Browse files Browse the repository at this point in the history
* Migrations with CLI and without knexfile

Allows to create and run migrations.

closes knex#2819

* Apply tweaks to migrate without knexfile

Thanks to @kibertoad comments!
The bigger change was to remove the bash script to test knex's cli command, and replace it with a jake file. I believe this jake file may be used as a base for future cli tests.

* Replace `fs.promises` with `new Promise`
  • Loading branch information
aurium authored and mwilliammyers committed Dec 12, 2018
1 parent 8ded57c commit 94c9ea1
Show file tree
Hide file tree
Showing 3 changed files with 147 additions and 17 deletions.
66 changes: 50 additions & 16 deletions bin/cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ var commander = require('commander');
var argv = require('minimist')(process.argv.slice(2));
var fs = Promise.promisifyAll(require('fs'));
var cliPkg = require('../package');
var { isObject } = require('lodash');

function exit(text) {
if (text instanceof Error) {
Expand All @@ -35,11 +36,31 @@ function checkLocalModule(env) {
}
}

function initKnex(env) {
function mkConfigObj(opts) {
let envName = opts.env || process.env.NODE_ENV || 'development';
let useNullAsDefault = opts.client === 'sqlite3';
return {
ext: 'js',
[envName]: {
useNullAsDefault,
client: opts.client,
connection: opts.connection,
migrations: {
directory: opts.migrationsDirectory,
},
},
};
}

function initKnex(env, opts) {
checkLocalModule(env);

if (!env.configPath) {
exit('No knexfile found in this directory. Specify a path with --knexfile');
if (!env.configuration) {
if (opts.client) env.configuration = mkConfigObj(opts);
else
exit(
'No knexfile found in this directory. Specify a path with --knexfile'
);
}

if (process.cwd() !== env.cwd) {
Expand All @@ -50,9 +71,11 @@ function initKnex(env) {
);
}

var environment = commander.env || process.env.NODE_ENV;
var environment = opts.env || process.env.NODE_ENV;
var defaultEnv = 'development';
var config = require(env.configPath);
var config = isObject(env.configuration)
? env.configuration
: require(env.configuration);

if (!environment && typeof config[defaultEnv] === 'object') {
environment = defaultEnv;
Expand All @@ -74,6 +97,7 @@ function initKnex(env) {
}

function invoke(env) {
env.modulePath = env.modulePath || process.env.KNEX_PATH;
var filetypes = ['js', 'coffee', 'ts', 'eg', 'ls'];
var pending = null;

Expand All @@ -90,6 +114,12 @@ function invoke(env) {
.option('--debug', 'Run with debugging.')
.option('--knexfile [path]', 'Specify the knexfile path.')
.option('--cwd [path]', 'Specify the working directory.')
.option('--client [name]', 'Set DB client without a knexfile.')
.option('--connection [address]', 'Set DB connection without a knexfile.')
.option(
'--migrations-directory [path]',
'Set migrations directory without a knexfile.'
)
.option(
'--env [name]',
'environment, default: process.env.NODE_ENV || development'
Expand All @@ -107,8 +137,8 @@ function invoke(env) {
if (filetypes.indexOf(type) === -1) {
exit(`Invalid filetype specified: ${type}`);
}
if (env.configPath) {
exit(`Error: ${env.configPath} already exists`);
if (env.configuration) {
exit(`Error: ${env.configuration} already exists`);
}
checkLocalModule(env);
var stubPath = `./knexfile.${type}`;
Expand Down Expand Up @@ -136,8 +166,12 @@ function invoke(env) {
'Specify the stub extension (default js)'
)
.action(function(name) {
var instance = initKnex(env);
var ext = (argv.x || env.configPath.split('.').pop()).toLowerCase();
var instance = initKnex(env, commander.opts());
var ext = (
argv.x ||
env.configuration.ext ||
env.configuration.split('.').pop()
).toLowerCase();
pending = instance.migrate
.make(name, { extension: ext })
.then(function(name) {
Expand All @@ -150,7 +184,7 @@ function invoke(env) {
.command('migrate:latest')
.description(' Run all migrations that have not yet been run.')
.action(function() {
pending = initKnex(env)
pending = initKnex(env, commander.opts())
.migrate.latest()
.spread(function(batchNo, log) {
if (log.length === 0) {
Expand All @@ -168,7 +202,7 @@ function invoke(env) {
.command('migrate:rollback')
.description(' Rollback the last set of migrations performed.')
.action(function() {
pending = initKnex(env)
pending = initKnex(env, commander.opts())
.migrate.rollback()
.spread(function(batchNo, log) {
if (log.length === 0) {
Expand All @@ -187,7 +221,7 @@ function invoke(env) {
.command('migrate:currentVersion')
.description(' View the current version for the migration.')
.action(function() {
pending = initKnex(env)
pending = initKnex(env, commander.opts())
.migrate.currentVersion()
.then(function(version) {
success(chalk.green('Current Version: ') + chalk.blue(version));
Expand All @@ -203,8 +237,8 @@ function invoke(env) {
'Specify the stub extension (default js)'
)
.action(function(name) {
var instance = initKnex(env);
var ext = (argv.x || env.configPath.split('.').pop()).toLowerCase();
var instance = initKnex(env, commander.opts());
var ext = (argv.x || env.configuration.split('.').pop()).toLowerCase();
pending = instance.seed
.make(name, { extension: ext })
.then(function(name) {
Expand All @@ -217,7 +251,7 @@ function invoke(env) {
.command('seed:run')
.description(' Run seed files.')
.action(function() {
pending = initKnex(env)
pending = initKnex(env, commander.opts())
.seed.run()
.spread(function(log) {
if (log.length === 0) {
Expand Down Expand Up @@ -257,7 +291,7 @@ cli.on('requireFail', function(name) {
cli.launch(
{
cwd: argv.cwd,
configPath: argv.knexfile,
configuration: argv.knexfile,
require: argv.require,
completion: argv.completion,
},
Expand Down
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
"eslint-config-prettier": "^3.1.0",
"eslint-plugin-import": "^2.14.0",
"husky": "^1.1.1",
"jake": "^8.0.18",
"json-loader": "^0.5.7",
"lint-staged": "^7.3.0",
"mocha": "^5.2.0",
Expand Down Expand Up @@ -85,9 +86,10 @@
"plaintest": "mocha --exit -t 10000 -b -R spec test/index.js && npm run tape",
"prepublish": "npm run babel",
"pre_test": "npm run lint",
"bin_test": "jake -f test/jake/*",
"tape": "node test/tape/index.js | tap-spec",
"debug_tape": "node --inspect-brk test/tape/index.js",
"test": "npm run pre_test && nyc mocha --exit --check-leaks --globals __core-js_shared__ -t 10000 -R spec test/index.js && npm run tape",
"test": "npm run pre_test && nyc mocha --exit --check-leaks --globals __core-js_shared__ -t 10000 -R spec test/index.js && npm run tape && npm run bin_test",
"oracledb:test": "docker rmi -f --no-prune knex-test-oracledb && docker build -f scripts/oracle-tests-Dockerfile --tag knex-test-oracledb . && docker run --rm -i -t -e KNEX_TEST_TIMEOUT=$KNEX_TEST_TIMEOUT -e NODE_VER=$NODE_VER knex-test-oracledb",
"mssql:init": "docker-compose -f scripts/mssql-docker-compose.yml up --no-start && docker-compose -f scripts/mssql-docker-compose.yml start",
"postmssql:init": "node scripts/wait-for-mssql-server.js && npm run mssql:logs || (npm run mssql:logs;false)",
Expand Down
94 changes: 94 additions & 0 deletions test/jake/migrate
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
#!/usr/bin/env jake
"use strict";

const os = require('os');
const fs = require('fs');
const path = require('path');
const sqlite3 = require('sqlite3');
const assert = require('assert');

const KNEX_PATH = path.normalize(__dirname + '/../../knex.js')
const KNEX = path.normalize(__dirname + '/../../bin/cli.js')

/* * * HELPERS * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

function assertExec(cmd, desc) {
if (!desc) desc = 'Run '+cmd
return new Promise((resolve, reject)=> {
var stderr = '';
var bin = jake.createExec([`KNEX_PATH=${KNEX_PATH} ${cmd}`]);
bin.addListener('error', (msg, code)=> reject(Error(desc +' FAIL. '+ stderr)));
bin.addListener('cmdEnd', resolve);
bin.addListener('stderr', (data)=> stderr += data.toString());
bin.run();
});
}

var taskList = [];
function test(description, func) {
var tempFolder, itFails=false;
tempFolder = fs.mkdtempSync(os.tmpdir() + '/knex-test-');
fs.mkdirSync(tempFolder + '/migrations');
desc(description);
let taskName = description.replace(/[^a-z0-9]/g, '');
taskList.push(taskName);
task(taskName, {async: true}, ()=> func(tempFolder)
.then(()=> console.log('☑ '+description) )
.catch((err)=> {
console.log('☒ '+err.message);
itFails = true;
})
.then(()=> {
jake.exec(`rm -r ${tempFolder}`);
if (itFails) process.exit(1);
})
);
}


/* * * TESTS * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

test('Create a migration file', (temp)=>
assertExec(`${KNEX} migrate:make \
--client=sqlite3 \
--migrations-directory=${temp}/migrations \
create_rule_table`)
.then(()=>
assertExec(`ls ${temp}/migrations/*_create_rule_table.js`,
'Find the migration file')
)
.then(()=>
assertExec(`grep exports.up ${temp}/migrations/*_create_rule_table.js`,
'Migration created with boilerplate')
)
);

test('Run migrations', (temp)=>
(new Promise((resolve, reject)=>
fs.writeFile(temp+'/migrations/000_create_rule_table.js', `
exports.up = (knex)=> knex.schema.createTable('rules', (table)=> {
table.string('name');
});
exports.down = (knex)=> knex.schema.dropTable('rules');
`, (err)=> err ? reject(err) : resolve())
))
.then(()=>
assertExec(`${KNEX} migrate:latest \
--client=sqlite3 --connection=${temp}/db \
--migrations-directory=${temp}/migrations \
create_rule_table`)
)
.then(()=> assertExec(`ls ${temp}/db`, 'Find the database file') )
.then(()=> new sqlite3.Database(temp+'/db') )
.then((db)=>
new Promise((resolve, reject)=>
db.get(
'SELECT name FROM knex_migrations',
function(err, row){ err ? reject(err) : resolve(row) }
)
)
)
.then((row)=> assert.equal(row.name, '000_create_rule_table.js') )
);

task('default', taskList);

0 comments on commit 94c9ea1

Please sign in to comment.