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 interpret = require('interpret');
|
|
|
|
const path = require('path');
|
|
|
|
const tildify = require('tildify');
|
|
|
|
const commander = require('commander');
|
2018-12-31 01:25:48 +09:00
|
|
|
const color = require('colorette');
|
|
|
|
const argv = require('getopts')(process.argv.slice(2));
|
2018-11-16 13:23:22 +01:00
|
|
|
const cliPkg = require('../package');
|
2018-12-05 08:30:55 +01:00
|
|
|
const {
|
|
|
|
mkConfigObj,
|
2019-07-04 20:08:01 +00:00
|
|
|
resolveEnvironmentConfig,
|
|
|
|
exit,
|
|
|
|
success,
|
|
|
|
checkLocalModule,
|
|
|
|
getMigrationExtension,
|
2019-10-13 16:49:53 +05:30
|
|
|
getSeedExtension,
|
2019-07-04 20:08:01 +00:00
|
|
|
getStubPath,
|
2018-12-05 08:30:55 +01:00
|
|
|
} = require('./utils/cli-config-utils');
|
2020-03-24 20:35:47 +05:45
|
|
|
const { readFile, writeFile } = require('./../lib/util/fs');
|
2019-10-13 16:49:53 +05:30
|
|
|
|
2019-08-24 15:31:12 +05:30
|
|
|
const { listMigrations } = require('./utils/migrationsLister');
|
2015-05-09 13:58:18 -04:00
|
|
|
|
2020-03-24 12:07:05 -04:00
|
|
|
async function openKnexfile(configPath) {
|
|
|
|
let config = require(configPath);
|
2020-04-19 00:40:23 +02:00
|
|
|
if (typeof config === 'function') {
|
2020-03-24 12:07:05 -04:00
|
|
|
config = await config();
|
|
|
|
}
|
2020-02-13 02:58:33 -05:00
|
|
|
return config;
|
|
|
|
}
|
2020-02-08 11:23:44 -05:00
|
|
|
|
2020-03-24 12:07:05 -04:00
|
|
|
async function initKnex(env, opts) {
|
2015-05-09 13:58:18 -04:00
|
|
|
checkLocalModule(env);
|
|
|
|
if (process.cwd() !== env.cwd) {
|
|
|
|
process.chdir(env.cwd);
|
2019-01-31 06:23:05 +01:00
|
|
|
console.log(
|
|
|
|
'Working directory changed to',
|
|
|
|
color.magenta(tildify(env.cwd))
|
|
|
|
);
|
2015-05-09 13:58:18 -04:00
|
|
|
}
|
|
|
|
|
2020-02-13 02:58:33 -05:00
|
|
|
if (opts.esm) {
|
|
|
|
// enable esm interop via 'esm' module
|
|
|
|
require = require('esm')(module);
|
|
|
|
}
|
|
|
|
|
|
|
|
env.configuration = env.configPath
|
2020-03-24 12:07:05 -04:00
|
|
|
? await openKnexfile(env.configPath)
|
2020-02-13 02:58:33 -05:00
|
|
|
: mkConfigObj(opts);
|
|
|
|
|
2020-05-05 19:10:50 +02:00
|
|
|
const resolvedConfig = resolveEnvironmentConfig(
|
|
|
|
opts,
|
|
|
|
env.configuration,
|
|
|
|
env.configPath
|
|
|
|
);
|
2019-06-19 01:33:16 +02:00
|
|
|
const knex = require(env.modulePath);
|
|
|
|
return knex(resolvedConfig);
|
|
|
|
}
|
2015-07-28 02:06:24 -05:00
|
|
|
|
2015-05-09 13:58:18 -04:00
|
|
|
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'];
|
2015-05-09 13:58:18 -04:00
|
|
|
|
2019-06-30 19:23:50 +03:00
|
|
|
const cliVersion = [
|
|
|
|
color.blue('Knex CLI version:'),
|
|
|
|
color.green(cliPkg.version),
|
|
|
|
].join(' ');
|
|
|
|
|
|
|
|
const localVersion = [
|
|
|
|
color.blue('Knex Local version:'),
|
|
|
|
color.green(env.modulePackage.version || 'None'),
|
|
|
|
].join(' ');
|
|
|
|
|
2015-05-09 13:58:18 -04:00
|
|
|
commander
|
2019-06-30 19:23:50 +03:00
|
|
|
.version(`${cliVersion}\n${localVersion}`)
|
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.'
|
|
|
|
)
|
2019-05-21 19:41:50 -05:00
|
|
|
.option(
|
|
|
|
'--migrations-table-name [path]',
|
|
|
|
'Set migrations table name without a knexfile.'
|
|
|
|
)
|
2018-07-09 08:10:34 -04:00
|
|
|
.option(
|
|
|
|
'--env [name]',
|
|
|
|
'environment, default: process.env.NODE_ENV || development'
|
2020-01-14 22:01:31 +02:00
|
|
|
)
|
2020-04-19 00:40:23 +02:00
|
|
|
.option('--esm', 'Enable ESM interop.')
|
|
|
|
.option('--specific [path]', 'Specify one seed file to execute.');
|
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}`;
|
2020-04-23 15:51:32 -04:00
|
|
|
readFile(
|
2020-03-24 20:35:47 +05:45
|
|
|
path.dirname(env.modulePath) +
|
|
|
|
'/lib/migrate/stub/knexfile-' +
|
|
|
|
type +
|
|
|
|
'.stub'
|
|
|
|
)
|
2018-11-16 13:23:22 +01:00
|
|
|
.then((code) => {
|
2020-03-24 20:35:47 +05:45
|
|
|
return writeFile(stubPath, code);
|
2018-07-09 08:10:34 -04:00
|
|
|
})
|
2018-11-16 13:23:22 +01:00
|
|
|
.then(() => {
|
2018-12-31 01:25:48 +09:00
|
|
|
success(color.green(`Created ${stubPath}`));
|
2018-07-09 08:10:34 -04:00
|
|
|
})
|
|
|
|
.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)'
|
|
|
|
)
|
2019-07-04 20:08:01 +00:00
|
|
|
.option(
|
|
|
|
`--stub [<relative/path/from/knexfile>|<name>]`,
|
|
|
|
'Specify the migration stub to use. If using <name> the file must be located in config.migrations.directory'
|
|
|
|
)
|
2020-03-24 12:07:05 -04:00
|
|
|
.action(async (name) => {
|
2018-11-16 13:23:22 +01:00
|
|
|
const opts = commander.opts();
|
|
|
|
opts.client = opts.client || 'sqlite3'; // We don't really care about client when creating migrations
|
2020-03-24 12:07:05 -04:00
|
|
|
const instance = await initKnex(env, opts);
|
2019-06-19 01:33:16 +02:00
|
|
|
const ext = getMigrationExtension(env, opts);
|
2019-07-10 22:45:47 +03:00
|
|
|
const configOverrides = { extension: ext };
|
|
|
|
|
2019-10-13 16:49:53 +05:30
|
|
|
const stub = getStubPath('migrations', env, opts);
|
2019-07-10 22:45:47 +03:00
|
|
|
if (stub) {
|
|
|
|
configOverrides.stub = stub;
|
|
|
|
}
|
|
|
|
|
2020-04-23 15:51:32 -04:00
|
|
|
instance.migrate
|
2019-07-10 22:45:47 +03:00
|
|
|
.make(name, configOverrides)
|
2018-11-16 13:23:22 +01:00
|
|
|
.then((name) => {
|
2018-12-31 01:25:48 +09:00
|
|
|
success(color.green(`Created Migration: ${name}`));
|
2018-07-09 08:10:34 -04:00
|
|
|
})
|
|
|
|
.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-12-30 18:11:57 +02:00
|
|
|
.option('--verbose', 'verbose')
|
2020-05-06 00:13:09 +02:00
|
|
|
.action(async () => {
|
|
|
|
try {
|
|
|
|
const instance = await initKnex(env, commander.opts());
|
|
|
|
const [batchNo, log] = await instance.migrate.latest();
|
|
|
|
if (log.length === 0) {
|
|
|
|
success(color.cyan('Already up to date'));
|
|
|
|
}
|
|
|
|
success(
|
|
|
|
color.green(`Batch ${batchNo} run: ${log.length} migrations`) +
|
|
|
|
(argv.verbose ? `\n${color.cyan(log.join('\n'))}` : '')
|
|
|
|
);
|
|
|
|
} catch (err) {
|
|
|
|
exit(err);
|
|
|
|
}
|
2019-05-19 07:14:52 -04:00
|
|
|
});
|
|
|
|
|
|
|
|
commander
|
2019-10-06 19:00:04 +03:00
|
|
|
.command('migrate:up [<name>]')
|
|
|
|
.description(
|
|
|
|
' Run the next or the specified migration that has not yet been run.'
|
|
|
|
)
|
|
|
|
.action((name) => {
|
2020-04-23 15:51:32 -04:00
|
|
|
initKnex(env, commander.opts())
|
2020-03-24 12:07:05 -04:00
|
|
|
.then((instance) => instance.migrate.up({ name }))
|
2019-06-17 02:14:17 +02:00
|
|
|
.then(([batchNo, log]) => {
|
2019-05-19 07:14:52 -04:00
|
|
|
if (log.length === 0) {
|
|
|
|
success(color.cyan('Already up to date'));
|
|
|
|
}
|
|
|
|
|
|
|
|
success(
|
|
|
|
color.green(
|
|
|
|
`Batch ${batchNo} ran the following migrations:\n${log.join(
|
|
|
|
'\n'
|
|
|
|
)}`
|
|
|
|
)
|
|
|
|
);
|
|
|
|
})
|
|
|
|
.catch(exit);
|
2015-05-09 13:58:18 -04:00
|
|
|
});
|
|
|
|
|
|
|
|
commander
|
|
|
|
.command('migrate:rollback')
|
2019-05-11 19:35:51 -04:00
|
|
|
.description(' Rollback the last batch of migrations performed.')
|
|
|
|
.option('--all', 'rollback all completed migrations')
|
2018-12-30 18:11:57 +02:00
|
|
|
.option('--verbose', 'verbose')
|
2019-05-11 19:35:51 -04:00
|
|
|
.action((cmd) => {
|
|
|
|
const { all } = cmd;
|
|
|
|
|
2020-04-23 15:51:32 -04:00
|
|
|
initKnex(env, commander.opts())
|
2020-03-24 12:07:05 -04:00
|
|
|
.then((instance) => instance.migrate.rollback(null, all))
|
2019-06-17 02:14:17 +02:00
|
|
|
.then(([batchNo, log]) => {
|
2018-07-09 08:10:34 -04:00
|
|
|
if (log.length === 0) {
|
2018-12-31 01:25:48 +09:00
|
|
|
success(color.cyan('Already at the base migration'));
|
2018-07-09 08:10:34 -04:00
|
|
|
}
|
|
|
|
success(
|
2018-12-31 01:25:48 +09:00
|
|
|
color.green(
|
2018-12-30 18:11:57 +02:00
|
|
|
`Batch ${batchNo} rolled back: ${log.length} migrations`
|
2018-12-31 01:25:48 +09:00
|
|
|
) + (argv.verbose ? `\n${color.cyan(log.join('\n'))}` : '')
|
2018-07-09 08:10:34 -04:00
|
|
|
);
|
|
|
|
})
|
|
|
|
.catch(exit);
|
2015-05-09 13:58:18 -04:00
|
|
|
});
|
|
|
|
|
2019-05-29 18:37:18 -04:00
|
|
|
commander
|
2019-10-06 19:00:04 +03:00
|
|
|
.command('migrate:down [<name>]')
|
|
|
|
.description(
|
|
|
|
' Undo the last or the specified migration that was already run.'
|
|
|
|
)
|
|
|
|
.action((name) => {
|
2020-04-23 15:51:32 -04:00
|
|
|
initKnex(env, commander.opts())
|
2020-03-24 12:07:05 -04:00
|
|
|
.then((instance) => instance.migrate.down({ name }))
|
2019-06-17 02:14:17 +02:00
|
|
|
.then(([batchNo, log]) => {
|
2019-05-29 18:37:18 -04:00
|
|
|
if (log.length === 0) {
|
|
|
|
success(color.cyan('Already at the base migration'));
|
|
|
|
}
|
|
|
|
success(
|
|
|
|
color.green(
|
|
|
|
`Batch ${batchNo} rolled back the following migrations:\n${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(() => {
|
2020-04-23 15:51:32 -04:00
|
|
|
initKnex(env, commander.opts())
|
2020-03-24 12:07:05 -04:00
|
|
|
.then((instance) => instance.migrate.currentVersion())
|
2018-11-16 13:23:22 +01:00
|
|
|
.then((version) => {
|
2018-12-31 01:25:48 +09:00
|
|
|
success(color.green('Current Version: ') + color.blue(version));
|
2018-07-09 08:10:34 -04:00
|
|
|
})
|
|
|
|
.catch(exit);
|
2019-08-24 15:31:12 +05:30
|
|
|
});
|
|
|
|
|
|
|
|
commander
|
|
|
|
.command('migrate:list')
|
|
|
|
.alias('migrate:status')
|
|
|
|
.description(' List all migrations files with status.')
|
|
|
|
.action(() => {
|
2020-04-23 15:51:32 -04:00
|
|
|
initKnex(env, commander.opts())
|
2020-03-24 12:07:05 -04:00
|
|
|
.then((instance) => {
|
|
|
|
return instance.migrate.list();
|
|
|
|
})
|
2019-08-24 15:31:12 +05:30
|
|
|
.then(([completed, newMigrations]) => {
|
|
|
|
listMigrations(completed, newMigrations);
|
|
|
|
})
|
|
|
|
.catch(exit);
|
2015-05-09 13:58:18 -04:00
|
|
|
});
|
|
|
|
|
2020-04-23 15:51:32 -04:00
|
|
|
commander
|
|
|
|
.command('migrate:unlock')
|
2020-05-05 19:10:50 +02:00
|
|
|
.description(' Forcibly unlocks the migrations lock table.')
|
2020-04-23 15:51:32 -04:00
|
|
|
.action(() => {
|
|
|
|
initKnex(env, commander.opts())
|
|
|
|
.then((instance) => instance.migrate.forceFreeMigrationsLock())
|
|
|
|
.then(() => {
|
|
|
|
success(
|
2020-05-05 19:10:50 +02:00
|
|
|
color.green(`Succesfully unlocked the migrations lock table`)
|
2020-04-23 15:51:32 -04:00
|
|
|
);
|
|
|
|
})
|
|
|
|
.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)'
|
|
|
|
)
|
2019-10-13 16:49:53 +05:30
|
|
|
.option(
|
|
|
|
`--stub [<relative/path/from/knexfile>|<name>]`,
|
|
|
|
'Specify the seed stub to use. If using <name> the file must be located in config.seeds.directory'
|
|
|
|
)
|
2020-03-24 12:07:05 -04:00
|
|
|
.action(async (name) => {
|
2018-11-16 13:23:22 +01:00
|
|
|
const opts = commander.opts();
|
|
|
|
opts.client = opts.client || 'sqlite3'; // We don't really care about client when creating seeds
|
2020-03-24 12:07:05 -04:00
|
|
|
const instance = await initKnex(env, opts);
|
2019-10-13 16:49:53 +05:30
|
|
|
const ext = getSeedExtension(env, opts);
|
|
|
|
const configOverrides = { extension: ext };
|
|
|
|
const stub = getStubPath('seeds', env, opts);
|
|
|
|
if (stub) {
|
|
|
|
configOverrides.stub = stub;
|
|
|
|
}
|
|
|
|
|
2020-04-23 15:51:32 -04:00
|
|
|
instance.seed
|
2019-10-13 16:49:53 +05:30
|
|
|
.make(name, configOverrides)
|
2018-11-16 13:23:22 +01:00
|
|
|
.then((name) => {
|
2018-12-31 01:25:48 +09:00
|
|
|
success(color.green(`Created seed file: ${name}`));
|
2018-07-09 08:10:34 -04:00
|
|
|
})
|
|
|
|
.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-12-30 18:11:57 +02:00
|
|
|
.option('--verbose', 'verbose')
|
2019-07-07 12:11:39 +03:00
|
|
|
.option('--specific', 'run specific seed file')
|
2018-11-16 13:23:22 +01:00
|
|
|
.action(() => {
|
2020-04-23 15:51:32 -04:00
|
|
|
initKnex(env, commander.opts())
|
2020-03-24 12:07:05 -04:00
|
|
|
.then((instance) => instance.seed.run({ specific: argv.specific }))
|
2019-06-17 02:14:17 +02:00
|
|
|
.then(([log]) => {
|
2018-07-09 08:10:34 -04:00
|
|
|
if (log.length === 0) {
|
2018-12-31 01:25:48 +09:00
|
|
|
success(color.cyan('No seed files exist'));
|
2018-07-09 08:10:34 -04:00
|
|
|
}
|
|
|
|
success(
|
2018-12-31 01:25:48 +09:00
|
|
|
color.green(`Ran ${log.length} seed files`) +
|
|
|
|
(argv.verbose ? `\n${color.cyan(log.join('\n'))}` : '')
|
2018-07-09 08:10:34 -04:00
|
|
|
);
|
|
|
|
})
|
|
|
|
.catch(exit);
|
2015-05-09 13:58:18 -04:00
|
|
|
});
|
|
|
|
|
2020-01-15 04:58:28 +09:00
|
|
|
if (!process.argv.slice(2).length) {
|
2018-01-18 23:29:38 +01:00
|
|
|
commander.outputHelp();
|
2020-01-05 03:27:12 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
commander.parse(process.argv);
|
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'),
|
2019-11-25 01:44:43 +01:00
|
|
|
moduleName: require('../package.json').name,
|
2015-05-09 13:58:18 -04:00
|
|
|
});
|
|
|
|
|
2020-04-19 00:40:23 +02:00
|
|
|
cli.on('require', function (name) {
|
2018-12-31 01:25:48 +09:00
|
|
|
console.log('Requiring external module', color.magenta(name));
|
2015-05-09 13:58:18 -04:00
|
|
|
});
|
|
|
|
|
2020-04-19 00:40:23 +02:00
|
|
|
cli.on('requireFail', function (name) {
|
2018-12-31 01:25:48 +09:00
|
|
|
console.log(color.red('Failed to load external module'), color.magenta(name));
|
2015-05-09 13:58:18 -04:00
|
|
|
});
|
|
|
|
|
2020-02-08 11:23:44 -05:00
|
|
|
// FYI: The handling for the `--cwd` and `--knexfile` arguments is a bit strange,
|
|
|
|
// but we decided to retain the behavior for backwards-compatibility. In
|
|
|
|
// particular: if `--knexfile` is a relative path, then it will be resolved
|
|
|
|
// relative to `--cwd` instead of the shell's CWD.
|
|
|
|
//
|
|
|
|
// So, the easiest way to replicate this behavior is to have the CLI change
|
|
|
|
// its CWD to `--cwd` immediately before initializing everything else. This
|
|
|
|
// ensures that Liftoff will then resolve the path to `--knexfile` correctly.
|
|
|
|
if (argv.cwd) {
|
|
|
|
process.chdir(argv.cwd);
|
|
|
|
}
|
|
|
|
|
2018-07-09 08:10:34 -04:00
|
|
|
cli.launch(
|
|
|
|
{
|
2020-02-08 11:23:44 -05:00
|
|
|
configPath: argv.knexfile,
|
2018-07-09 08:10:34 -04:00
|
|
|
require: argv.require,
|
|
|
|
completion: argv.completion,
|
|
|
|
},
|
|
|
|
invoke
|
|
|
|
);
|