2015-05-09 13:58:18 -04:00
|
|
|
#!/usr/bin/env node
|
2016-09-13 09:31:58 -04:00
|
|
|
/* eslint no-console:0, no-var:0 */
|
2018-11-16 13:23:22 +01:00
|
|
|
const Liftoff = require('liftoff');
|
|
|
|
const Promise = require('bluebird');
|
|
|
|
const interpret = require('interpret');
|
|
|
|
const path = require('path');
|
|
|
|
const chalk = require('chalk');
|
|
|
|
const tildify = require('tildify');
|
|
|
|
const commander = require('commander');
|
|
|
|
const argv = require('minimist')(process.argv.slice(2));
|
|
|
|
const fs = Promise.promisifyAll(require('fs'));
|
|
|
|
const cliPkg = require('../package');
|
|
|
|
const { resolveClientNameWithAliases } = require('../lib/helpers');
|
2018-12-03 23:14:34 +01:00
|
|
|
const DEFAULT_EXT = 'js';
|
2015-05-09 13:58:18 -04:00
|
|
|
|
|
|
|
function exit(text) {
|
|
|
|
if (text instanceof Error) {
|
2018-01-18 23:29:38 +01:00
|
|
|
console.error(chalk.red(text.stack));
|
2015-05-09 13:58:18 -04:00
|
|
|
} else {
|
2018-01-18 23:29:38 +01:00
|
|
|
console.error(chalk.red(text));
|
2015-05-09 13:58:18 -04:00
|
|
|
}
|
|
|
|
process.exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
function success(text) {
|
|
|
|
console.log(text);
|
|
|
|
process.exit(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
function checkLocalModule(env) {
|
|
|
|
if (!env.modulePath) {
|
2018-07-09 08:10:34 -04:00
|
|
|
console.log(
|
|
|
|
chalk.red('No local knex install found in:'),
|
|
|
|
chalk.magenta(tildify(env.cwd))
|
|
|
|
);
|
2015-05-09 13:58:18 -04:00
|
|
|
exit('Try running: npm install knex.');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-06 06:03:18 -03:00
|
|
|
function mkConfigObj(opts) {
|
2018-11-16 13:23:22 +01:00
|
|
|
const envName = opts.env || process.env.NODE_ENV || 'development';
|
|
|
|
const resolvedClientName = resolveClientNameWithAliases(opts.client);
|
|
|
|
const useNullAsDefault = resolvedClientName === 'sqlite3';
|
2018-11-06 06:03:18 -03:00
|
|
|
return {
|
2018-12-03 23:14:34 +01:00
|
|
|
ext: DEFAULT_EXT,
|
2018-11-06 06:03:18 -03:00
|
|
|
[envName]: {
|
|
|
|
useNullAsDefault,
|
|
|
|
client: opts.client,
|
|
|
|
connection: opts.connection,
|
|
|
|
migrations: {
|
|
|
|
directory: opts.migrationsDirectory,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
function initKnex(env, opts) {
|
2015-05-09 13:58:18 -04:00
|
|
|
checkLocalModule(env);
|
|
|
|
if (process.cwd() !== env.cwd) {
|
|
|
|
process.chdir(env.cwd);
|
2018-07-09 08:10:34 -04:00
|
|
|
console.log(
|
|
|
|
'Working directory changed to',
|
|
|
|
chalk.magenta(tildify(env.cwd))
|
|
|
|
);
|
2015-05-09 13:58:18 -04:00
|
|
|
}
|
|
|
|
|
2018-12-03 23:14:34 +01:00
|
|
|
if (!opts.knexfile) {
|
|
|
|
env.configuration = mkConfigObj(opts);
|
|
|
|
}
|
|
|
|
// If knexfile is specified
|
|
|
|
else {
|
|
|
|
const resolvedKnexfilePath = path.resolve(opts.knexfile);
|
|
|
|
env.configuration = require(resolvedKnexfilePath);
|
|
|
|
|
|
|
|
if (!env.configuration) {
|
|
|
|
exit(
|
|
|
|
'No knexfile found in this directory. Specify a path with --knexfile or pass --client and --connection params in commandline'
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-16 13:23:22 +01:00
|
|
|
let environment = opts.env || process.env.NODE_ENV;
|
|
|
|
const defaultEnv = 'development';
|
|
|
|
|
|
|
|
let config = env.configuration;
|
2015-07-28 02:06:24 -05:00
|
|
|
|
2015-05-09 13:58:18 -04:00
|
|
|
if (!environment && typeof config[defaultEnv] === 'object') {
|
|
|
|
environment = defaultEnv;
|
|
|
|
}
|
2015-12-17 10:57:11 -06:00
|
|
|
|
2015-05-09 13:58:18 -04:00
|
|
|
if (environment) {
|
|
|
|
console.log('Using environment:', chalk.magenta(environment));
|
2015-12-17 10:57:11 -06:00
|
|
|
config = config[environment] || config;
|
2015-05-09 13:58:18 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!config) {
|
|
|
|
console.log(chalk.red('Warning: unable to read knexfile config'));
|
|
|
|
process.exit(1);
|
|
|
|
}
|
|
|
|
|
2018-11-22 10:19:06 +01:00
|
|
|
if (argv.debug !== undefined) {
|
|
|
|
config.debug = argv.debug;
|
|
|
|
}
|
|
|
|
|
2018-11-16 13:23:22 +01:00
|
|
|
const knex = require(env.modulePath);
|
2015-05-09 13:58:18 -04:00
|
|
|
return knex(config);
|
|
|
|
}
|
|
|
|
|
|
|
|
function invoke(env) {
|
2018-12-03 23:14:34 +01:00
|
|
|
env.modulePath = env.modulePath || env.knexpath || process.env.KNEX_PATH;
|
|
|
|
|
2018-11-16 13:23:22 +01:00
|
|
|
const filetypes = ['js', 'coffee', 'ts', 'eg', 'ls'];
|
|
|
|
let pending = null;
|
2015-05-09 13:58:18 -04:00
|
|
|
|
|
|
|
commander
|
|
|
|
.version(
|
2018-07-09 08:10:34 -04:00
|
|
|
chalk.blue('Knex CLI version: ', chalk.green(cliPkg.version)) +
|
|
|
|
'\n' +
|
|
|
|
chalk.blue(
|
|
|
|
'Local Knex version: ',
|
|
|
|
chalk.green(env.modulePackage.version)
|
|
|
|
) +
|
|
|
|
'\n'
|
2015-05-09 13:58:18 -04:00
|
|
|
)
|
|
|
|
.option('--debug', 'Run with debugging.')
|
|
|
|
.option('--knexfile [path]', 'Specify the knexfile path.')
|
2018-12-03 23:14:34 +01:00
|
|
|
.option('--knexpath [path]', 'Specify the path to knex instance.')
|
2015-05-09 13:58:18 -04:00
|
|
|
.option('--cwd [path]', 'Specify the working directory.')
|
2018-11-06 06:03:18 -03:00
|
|
|
.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.'
|
|
|
|
)
|
2018-07-09 08:10:34 -04:00
|
|
|
.option(
|
|
|
|
'--env [name]',
|
|
|
|
'environment, default: process.env.NODE_ENV || development'
|
|
|
|
);
|
2015-05-09 13:58:18 -04:00
|
|
|
|
|
|
|
commander
|
|
|
|
.command('init')
|
|
|
|
.description(' Create a fresh knexfile.')
|
2018-07-09 08:10:34 -04:00
|
|
|
.option(
|
|
|
|
`-x [${filetypes.join('|')}]`,
|
|
|
|
'Specify the knexfile extension (default js)'
|
|
|
|
)
|
2018-11-16 13:23:22 +01:00
|
|
|
.action(() => {
|
|
|
|
const type = (argv.x || 'js').toLowerCase();
|
2015-05-09 13:58:18 -04:00
|
|
|
if (filetypes.indexOf(type) === -1) {
|
2016-05-17 01:01:34 +10:00
|
|
|
exit(`Invalid filetype specified: ${type}`);
|
2015-05-09 13:58:18 -04:00
|
|
|
}
|
2018-11-06 06:03:18 -03:00
|
|
|
if (env.configuration) {
|
2018-12-03 23:14:34 +01:00
|
|
|
exit(`Error: ${env.knexfile} already exists`);
|
2015-05-09 13:58:18 -04:00
|
|
|
}
|
|
|
|
checkLocalModule(env);
|
2018-11-16 13:23:22 +01:00
|
|
|
const stubPath = `./knexfile.${type}`;
|
2018-07-09 08:10:34 -04:00
|
|
|
pending = fs
|
|
|
|
.readFileAsync(
|
|
|
|
path.dirname(env.modulePath) +
|
|
|
|
'/lib/migrate/stub/knexfile-' +
|
|
|
|
type +
|
|
|
|
'.stub'
|
|
|
|
)
|
2018-11-16 13:23:22 +01:00
|
|
|
.then((code) => {
|
2018-07-09 08:10:34 -04:00
|
|
|
return fs.writeFileAsync(stubPath, code);
|
|
|
|
})
|
2018-11-16 13:23:22 +01:00
|
|
|
.then(() => {
|
2018-07-09 08:10:34 -04:00
|
|
|
success(chalk.green(`Created ${stubPath}`));
|
|
|
|
})
|
|
|
|
.catch(exit);
|
2015-05-09 13:58:18 -04:00
|
|
|
});
|
|
|
|
|
|
|
|
commander
|
|
|
|
.command('migrate:make <name>')
|
2017-01-11 18:47:24 +05:45
|
|
|
.description(' Create a named migration file.')
|
2018-07-09 08:10:34 -04:00
|
|
|
.option(
|
|
|
|
`-x [${filetypes.join('|')}]`,
|
|
|
|
'Specify the stub extension (default js)'
|
|
|
|
)
|
2018-11-16 13:23:22 +01:00
|
|
|
.action((name) => {
|
|
|
|
const opts = commander.opts();
|
|
|
|
opts.client = opts.client || 'sqlite3'; // We don't really care about client when creating migrations
|
|
|
|
const instance = initKnex(env, opts);
|
2018-12-03 23:14:34 +01:00
|
|
|
const ext = (
|
|
|
|
argv.x ||
|
|
|
|
env.configuration.ext ||
|
|
|
|
DEFAULT_EXT
|
|
|
|
).toLowerCase();
|
2018-07-09 08:10:34 -04:00
|
|
|
pending = instance.migrate
|
|
|
|
.make(name, { extension: ext })
|
2018-11-16 13:23:22 +01:00
|
|
|
.then((name) => {
|
2018-07-09 08:10:34 -04:00
|
|
|
success(chalk.green(`Created Migration: ${name}`));
|
|
|
|
})
|
|
|
|
.catch(exit);
|
2015-05-09 13:58:18 -04:00
|
|
|
});
|
|
|
|
|
|
|
|
commander
|
|
|
|
.command('migrate:latest')
|
|
|
|
.description(' Run all migrations that have not yet been run.')
|
2018-11-16 13:23:22 +01:00
|
|
|
.action(() => {
|
2018-11-06 06:03:18 -03:00
|
|
|
pending = initKnex(env, commander.opts())
|
2018-07-09 08:10:34 -04:00
|
|
|
.migrate.latest()
|
2018-11-16 13:23:22 +01:00
|
|
|
.spread((batchNo, log) => {
|
2018-07-09 08:10:34 -04:00
|
|
|
if (log.length === 0) {
|
|
|
|
success(chalk.cyan('Already up to date'));
|
|
|
|
}
|
|
|
|
success(
|
|
|
|
chalk.green(`Batch ${batchNo} run: ${log.length} migrations \n`) +
|
|
|
|
chalk.cyan(log.join('\n'))
|
|
|
|
);
|
|
|
|
})
|
|
|
|
.catch(exit);
|
2015-05-09 13:58:18 -04:00
|
|
|
});
|
|
|
|
|
|
|
|
commander
|
|
|
|
.command('migrate:rollback')
|
|
|
|
.description(' Rollback the last set of migrations performed.')
|
2018-11-16 13:23:22 +01:00
|
|
|
.action(() => {
|
2018-11-06 06:03:18 -03:00
|
|
|
pending = initKnex(env, commander.opts())
|
2018-07-09 08:10:34 -04:00
|
|
|
.migrate.rollback()
|
2018-11-16 13:23:22 +01:00
|
|
|
.spread((batchNo, log) => {
|
2018-07-09 08:10:34 -04:00
|
|
|
if (log.length === 0) {
|
|
|
|
success(chalk.cyan('Already at the base migration'));
|
|
|
|
}
|
|
|
|
success(
|
|
|
|
chalk.green(
|
|
|
|
`Batch ${batchNo} rolled back: ${log.length} migrations \n`
|
|
|
|
) + chalk.cyan(log.join('\n'))
|
|
|
|
);
|
|
|
|
})
|
|
|
|
.catch(exit);
|
2015-05-09 13:58:18 -04:00
|
|
|
});
|
|
|
|
|
|
|
|
commander
|
|
|
|
.command('migrate:currentVersion')
|
2017-01-11 18:47:24 +05:45
|
|
|
.description(' View the current version for the migration.')
|
2018-11-16 13:23:22 +01:00
|
|
|
.action(() => {
|
2018-11-06 06:03:18 -03:00
|
|
|
pending = initKnex(env, commander.opts())
|
2018-07-09 08:10:34 -04:00
|
|
|
.migrate.currentVersion()
|
2018-11-16 13:23:22 +01:00
|
|
|
.then((version) => {
|
2018-07-09 08:10:34 -04:00
|
|
|
success(chalk.green('Current Version: ') + chalk.blue(version));
|
|
|
|
})
|
|
|
|
.catch(exit);
|
2015-05-09 13:58:18 -04:00
|
|
|
});
|
|
|
|
|
|
|
|
commander
|
|
|
|
.command('seed:make <name>')
|
2017-01-11 18:47:24 +05:45
|
|
|
.description(' Create a named seed file.')
|
2018-07-09 08:10:34 -04:00
|
|
|
.option(
|
|
|
|
`-x [${filetypes.join('|')}]`,
|
|
|
|
'Specify the stub extension (default js)'
|
|
|
|
)
|
2018-11-16 13:23:22 +01:00
|
|
|
.action((name) => {
|
|
|
|
const opts = commander.opts();
|
|
|
|
opts.client = opts.client || 'sqlite3'; // We don't really care about client when creating seeds
|
|
|
|
const instance = initKnex(env, opts);
|
2018-12-03 23:14:34 +01:00
|
|
|
const ext = (
|
|
|
|
argv.x ||
|
|
|
|
env.configuration.ext ||
|
|
|
|
DEFAULT_EXT
|
|
|
|
).toLowerCase();
|
2018-07-09 08:10:34 -04:00
|
|
|
pending = instance.seed
|
|
|
|
.make(name, { extension: ext })
|
2018-11-16 13:23:22 +01:00
|
|
|
.then((name) => {
|
2018-07-09 08:10:34 -04:00
|
|
|
success(chalk.green(`Created seed file: ${name}`));
|
|
|
|
})
|
|
|
|
.catch(exit);
|
2015-05-09 13:58:18 -04:00
|
|
|
});
|
|
|
|
|
|
|
|
commander
|
|
|
|
.command('seed:run')
|
2017-01-11 18:47:24 +05:45
|
|
|
.description(' Run seed files.')
|
2018-11-16 13:23:22 +01:00
|
|
|
.action(() => {
|
2018-11-06 06:03:18 -03:00
|
|
|
pending = initKnex(env, commander.opts())
|
2018-07-09 08:10:34 -04:00
|
|
|
.seed.run()
|
2018-11-16 13:23:22 +01:00
|
|
|
.spread((log) => {
|
2018-07-09 08:10:34 -04:00
|
|
|
if (log.length === 0) {
|
|
|
|
success(chalk.cyan('No seed files exist'));
|
|
|
|
}
|
|
|
|
success(
|
|
|
|
chalk.green(
|
|
|
|
`Ran ${log.length} seed files \n${chalk.cyan(log.join('\n'))}`
|
|
|
|
)
|
|
|
|
);
|
|
|
|
})
|
|
|
|
.catch(exit);
|
2015-05-09 13:58:18 -04:00
|
|
|
});
|
|
|
|
|
|
|
|
commander.parse(process.argv);
|
|
|
|
|
2018-11-16 13:23:22 +01:00
|
|
|
Promise.resolve(pending).then(() => {
|
2018-01-18 23:29:38 +01:00
|
|
|
commander.outputHelp();
|
|
|
|
exit('Unknown command-line options, exiting');
|
2015-05-09 13:58:18 -04:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2018-11-16 13:23:22 +01:00
|
|
|
const cli = new Liftoff({
|
2015-05-09 13:58:18 -04:00
|
|
|
name: 'knex',
|
|
|
|
extensions: interpret.jsVariants,
|
2018-07-09 08:10:34 -04:00
|
|
|
v8flags: require('v8flags'),
|
2015-05-09 13:58:18 -04:00
|
|
|
});
|
|
|
|
|
|
|
|
cli.on('require', function(name) {
|
|
|
|
console.log('Requiring external module', chalk.magenta(name));
|
|
|
|
});
|
|
|
|
|
|
|
|
cli.on('requireFail', function(name) {
|
|
|
|
console.log(chalk.red('Failed to load external module'), chalk.magenta(name));
|
|
|
|
});
|
|
|
|
|
2018-07-09 08:10:34 -04:00
|
|
|
cli.launch(
|
|
|
|
{
|
|
|
|
cwd: argv.cwd,
|
2018-12-03 23:14:34 +01:00
|
|
|
knexfile: argv.knexfile,
|
|
|
|
knexpath: argv.knexpath,
|
2018-07-09 08:10:34 -04:00
|
|
|
require: argv.require,
|
|
|
|
completion: argv.completion,
|
|
|
|
},
|
|
|
|
invoke
|
|
|
|
);
|