Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CLI: adds support for asynchronous knexfile loading #3748

Merged
merged 3 commits into from Mar 24, 2020
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
35 changes: 20 additions & 15 deletions bin/cli.js
Expand Up @@ -28,8 +28,11 @@ const fsPromised = {
writeFile: promisify(fs.writeFile),
};

function openKnexfile(configPath) {
const config = require(configPath);
async function openKnexfile(configPath) {
let config = require(configPath);
if (typeof config === 'function') {
config = await config();
}

// FYI: By default, the extension for the migration files is inferred
// from the knexfile's extension. So, the following lines are in
Expand All @@ -39,7 +42,7 @@ function openKnexfile(configPath) {
return config;
}

function initKnex(env, opts) {
async function initKnex(env, opts) {
checkLocalModule(env);
if (process.cwd() !== env.cwd) {
process.chdir(env.cwd);
Expand All @@ -55,7 +58,7 @@ function initKnex(env, opts) {
}

env.configuration = env.configPath
? openKnexfile(env.configPath)
? await openKnexfile(env.configPath)
: mkConfigObj(opts);

const resolvedConfig = resolveEnvironmentConfig(opts, env.configuration);
Expand Down Expand Up @@ -145,10 +148,10 @@ function invoke(env) {
`--stub [<relative/path/from/knexfile>|<name>]`,
'Specify the migration stub to use. If using <name> the file must be located in config.migrations.directory'
)
.action((name) => {
.action(async (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);
const instance = await initKnex(env, opts);
const ext = getMigrationExtension(env, opts);
const configOverrides = { extension: ext };

Expand All @@ -171,7 +174,7 @@ function invoke(env) {
.option('--verbose', 'verbose')
.action(() => {
pending = initKnex(env, commander.opts())
.migrate.latest()
.then((instance) => instance.migrate.latest())
.then(([batchNo, log]) => {
if (log.length === 0) {
success(color.cyan('Already up to date'));
Expand All @@ -191,7 +194,7 @@ function invoke(env) {
)
.action((name) => {
pending = initKnex(env, commander.opts())
.migrate.up({ name })
.then((instance) => instance.migrate.up({ name }))
.then(([batchNo, log]) => {
if (log.length === 0) {
success(color.cyan('Already up to date'));
Expand All @@ -217,7 +220,7 @@ function invoke(env) {
const { all } = cmd;

pending = initKnex(env, commander.opts())
.migrate.rollback(null, all)
.then((instance) => instance.migrate.rollback(null, all))
.then(([batchNo, log]) => {
if (log.length === 0) {
success(color.cyan('Already at the base migration'));
Expand All @@ -238,7 +241,7 @@ function invoke(env) {
)
.action((name) => {
pending = initKnex(env, commander.opts())
.migrate.down({ name })
.then((instance) => instance.migrate.down({ name }))
.then(([batchNo, log]) => {
if (log.length === 0) {
success(color.cyan('Already at the base migration'));
Expand All @@ -260,7 +263,7 @@ function invoke(env) {
.description(' View the current version for the migration.')
.action(() => {
pending = initKnex(env, commander.opts())
.migrate.currentVersion()
.then((instance) => instance.migrate.currentVersion())
.then((version) => {
success(color.green('Current Version: ') + color.blue(version));
})
Expand All @@ -273,7 +276,9 @@ function invoke(env) {
.description(' List all migrations files with status.')
.action(() => {
pending = initKnex(env, commander.opts())
.migrate.list()
.then((instance) => {
return instance.migrate.list();
})
.then(([completed, newMigrations]) => {
listMigrations(completed, newMigrations);
})
Expand All @@ -291,10 +296,10 @@ function invoke(env) {
`--stub [<relative/path/from/knexfile>|<name>]`,
'Specify the seed stub to use. If using <name> the file must be located in config.seeds.directory'
)
.action((name) => {
.action(async (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);
const instance = await initKnex(env, opts);
const ext = getSeedExtension(env, opts);
const configOverrides = { extension: ext };
const stub = getStubPath('seeds', env, opts);
Expand All @@ -317,7 +322,7 @@ function invoke(env) {
.option('--specific', 'run specific seed file')
.action(() => {
pending = initKnex(env, commander.opts())
.seed.run({ specific: argv.specific })
.then((instance) => instance.seed.run({ specific: argv.specific }))
.then(([log]) => {
if (log.length === 0) {
success(color.cyan('No seed files exist'));
Expand Down
27 changes: 27 additions & 0 deletions test/cli/knexfile-test.spec.js
Expand Up @@ -65,6 +65,33 @@ module.exports = {
);
});

it('Run migrations with knexfile returning function passed', () => {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you also add same tests for seeder execution? Ideally the two should work symmetrically.

return execCommand(
`node ${KNEX} migrate:latest --knexfile=test/jake-util/knexfile/knexfile_func.js --knexpath=../knex.js`,
{
expectedOutput: 'Batch 1 run: 1 migrations',
}
);
});

it('Run migrations with knexfile with async passed', () => {
return execCommand(
`node ${KNEX} migrate:latest --knexfile=test/jake-util/knexfile/knexfile_async.js --knexpath=../knex.js`,
{
expectedOutput: 'Batch 1 run: 1 migrations',
}
);
});

it('Run migrations with knexfile with promise passed', () => {
return execCommand(
`node ${KNEX} migrate:latest --knexfile=test/jake-util/knexfile/knexfile_promise.js --knexpath=../knex.js`,
{
expectedOutput: 'Batch 1 run: 1 migrations',
}
);
});

it("changes the process's cwd to the directory that contains the knexfile", () => {
const knexfile = 'test/jake-util/knexfile-relative/knexfile.js';
const expectedCWD = tildify(path.resolve(path.dirname(knexfile)));
Expand Down
10 changes: 10 additions & 0 deletions test/cli/seed.spec.js
Expand Up @@ -20,6 +20,16 @@ describe('seed:run', () => {
);
});

it('supports async configuration', () => {
return execCommand(
`node ${KNEX} seed:run --knexfile=test/jake-util/seeds-knexfile-async.js`,
{
expectedOutput: 'Ran 2 seed files',
notExpectedOutput: ['first.js', 'second.js'],
}
);
});

it('prints verbose logs', () => {
return execCommand(
`node ${KNEX} seed:run --knexfile=test/jake-util/seeds-knexfile.js --verbose`,
Expand Down
12 changes: 12 additions & 0 deletions test/jake-util/knexfile/knexfile_async.js
@@ -0,0 +1,12 @@
module.exports = async () => ({
client: 'sqlite3',
connection: {
filename: __dirname + '/../test.sqlite3',
},
migrations: {
directory: __dirname + '/../knexfile_migrations',
},
seeds: {
directory: __dirname + '/../knexfile_seeds',
},
});
12 changes: 12 additions & 0 deletions test/jake-util/knexfile/knexfile_func.js
@@ -0,0 +1,12 @@
module.exports = () => ({
client: 'sqlite3',
connection: {
filename: __dirname + '/../test.sqlite3',
},
migrations: {
directory: __dirname + '/../knexfile_migrations',
},
seeds: {
directory: __dirname + '/../knexfile_seeds',
},
});
12 changes: 12 additions & 0 deletions test/jake-util/knexfile/knexfile_promise.js
@@ -0,0 +1,12 @@
module.exports = Promise.resolve({
client: 'sqlite3',
connection: {
filename: __dirname + '/../test.sqlite3',
},
migrations: {
directory: __dirname + '/../knexfile_migrations',
},
seeds: {
directory: __dirname + '/../knexfile_seeds',
},
});
9 changes: 9 additions & 0 deletions test/jake-util/seeds-knexfile-async.js
@@ -0,0 +1,9 @@
module.exports = async () => ({
client: 'sqlite3',
connection: {
filename: __dirname + '/../test.sqlite3',
},
seeds: {
directory: __dirname + '/seeds',
},
});