cli.js 6.35 KB
#!/usr/bin/env node
/* eslint no-console:0, no-var:0 */
var Liftoff = require('liftoff');
var Promise = require('bluebird');
var interpret = require('interpret');
var path = require('path');
var chalk = require('chalk');
var tildify = require('tildify');
var commander = require('commander');
var argv = require('minimist')(process.argv.slice(2));
var fs = Promise.promisifyAll(require('fs'));
var cliPkg = require('../package');

function exit(text) {
  if (text instanceof Error) {
    chalk.red(console.error(text.stack));
  } else {
    chalk.red(console.error(text));
  }
  process.exit(1);
}

function success(text) {
  console.log(text);
  process.exit(0);
}

function checkLocalModule(env) {
  if (!env.modulePath) {
    console.log(chalk.red('No local knex install found in:'), chalk.magenta(tildify(env.cwd)));
    exit('Try running: npm install knex.');
  }
}

function initKnex(env) {

  checkLocalModule(env);

  if (!env.configPath) {
    exit('No knexfile found in this directory. Specify a path with --knexfile');
  }

  if (process.cwd() !== env.cwd) {
    process.chdir(env.cwd);
    console.log('Working directory changed to', chalk.magenta(tildify(env.cwd)));
  }

  var environment = commander.env || process.env.NODE_ENV;
  var defaultEnv = 'development';
  var config = require(env.configPath);

  if (!environment && typeof config[defaultEnv] === 'object') {
    environment = defaultEnv;
  }

  if (environment) {
    console.log('Using environment:', chalk.magenta(environment));
    config = config[environment] || config;
  }

  if (!config) {
    console.log(chalk.red('Warning: unable to read knexfile config'));
    process.exit(1);
  }

  if (argv.debug !== undefined)
    config.debug = argv.debug;
  var knex = require(env.modulePath);
  return knex(config);
}

function invoke(env) {

  var filetypes = ['js', 'coffee', 'ts', 'eg', 'ls'];
  var pending = null;

  commander
    .version(
      chalk.blue('Knex CLI version: ', chalk.green(cliPkg.version)) + '\n' +
      chalk.blue('Local Knex version: ', chalk.green(env.modulePackage.version)) + '\n'
    )
    .option('--debug', 'Run with debugging.')
    .option('--knexfile [path]', 'Specify the knexfile path.')
    .option('--cwd [path]', 'Specify the working directory.')
    .option('--env [name]', 'environment, default: process.env.NODE_ENV || development');


  commander
    .command('init')
    .description('        Create a fresh knexfile.')
    .option(`-x [${filetypes.join('|')}]`, 'Specify the knexfile extension (default js)')
    .action(function() {
      var type = (argv.x || 'js').toLowerCase();
      if (filetypes.indexOf(type) === -1) {
        exit(`Invalid filetype specified: ${type}`);
      }
      if (env.configPath) {
        exit(`Error: ${env.configPath} already exists`);
      }
      checkLocalModule(env);
      var stubPath = `./knexfile.${type}`;
      pending = fs.readFileAsync(
        path.dirname(env.modulePath) +
        '/lib/migrate/stub/knexfile-' +
        type + '.stub'
      ).then(function(code) { return fs.writeFileAsync(stubPath, code) }).then(function() {
        success(chalk.green(`Created ${stubPath}`));
      }).catch(exit);
    });

  commander
    .command('migrate:make <name>')
    .description('        Create a named migration file.')
    .option(`-x [${filetypes.join('|')}]`, 'Specify the stub extension (default js)')
    .action(function(name) {
      var instance = initKnex(env);
      var ext = (argv.x || env.configPath.split('.').pop()).toLowerCase();
      pending = instance.migrate.make(name, {extension: ext}).then(function(name) {
        success(chalk.green(`Created Migration: ${name}`));
      }).catch(exit);
    });

  commander
    .command('migrate:latest')
    .description('        Run all migrations that have not yet been run.')
    .action(function() {
      pending = initKnex(env).migrate.latest().spread(function(batchNo, log) {
        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);
    });

  commander
    .command('migrate:rollback')
    .description('        Rollback the last set of migrations performed.')
    .action(function() {
      pending = initKnex(env).migrate.rollback().spread(function(batchNo, log) {
        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);
    });

  commander
    .command('migrate:currentVersion')
    .description('        View the current version for the migration.')
    .action(function () {
      pending = initKnex(env).migrate.currentVersion().then(function(version) {
        success(chalk.green('Current Version: ') + chalk.blue(version));
      }).catch(exit);
    });

  commander
    .command('seed:make <name>')
    .description('        Create a named seed file.')
    .option(`-x [${filetypes.join('|')}]`, 'Specify the stub extension (default js)')
    .action(function(name) {
      var instance = initKnex(env);
      var ext = (argv.x || env.configPath.split('.').pop()).toLowerCase();
      pending = instance.seed.make(name, {extension: ext}).then(function(name) {
        success(chalk.green(`Created seed file: ${name}`));
      }).catch(exit);
    });

  commander
    .command('seed:run')
    .description('        Run seed files.')
    .action(function() {
      pending = initKnex(env).seed.run().spread(function(log) {
        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);
    });

  commander.parse(process.argv);

  Promise.resolve(pending).then(function() {
    commander.help();
  });
}

var cli = new Liftoff({
  name: 'knex',
  extensions: interpret.jsVariants,
  v8flags: require('v8flags')
});

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));
});

cli.launch({
  cwd: argv.cwd,
  configPath: argv.knexfile,
  require: argv.require,
  completion: argv.completion
}, invoke);