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

Override knexfile options with CLI options #4047

Merged
merged 13 commits into from Mar 30, 2022
Merged
27 changes: 16 additions & 11 deletions bin/cli.js
@@ -1,5 +1,6 @@
#!/usr/bin/env node
const rechoir = require('rechoir');
const merge = require('lodash/merge');
const interpret = require('interpret');
const resolveFrom = require('resolve-from');
const path = require('path');
Expand All @@ -9,6 +10,7 @@ const color = require('colorette');
const argv = require('getopts')(process.argv.slice(2));
const cliPkg = require('../package');
const {
parseConfigObj,
mkConfigObj,
resolveEnvironmentConfig,
exit,
Expand Down Expand Up @@ -59,8 +61,17 @@ async function initKnex(env, opts) {
env.configuration,
env.configPath
);

const optionsConfig = parseConfigObj(opts);
const config = merge(resolvedConfig, optionsConfig);

// Migrations directory gets defaulted if it is undefined.
if (!env.configPath && !config.migrations.directory) {
config.migrations.directory = null;
}

const knex = require(env.modulePath);
return knex(resolvedConfig);
return knex(config);
}

function invoke() {
Expand Down Expand Up @@ -138,16 +149,10 @@ function invoke() {
.option('--knexfile [path]', 'Specify the knexfile path.')
.option('--knexpath [path]', 'Specify the path to knex instance.')
.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(
'--migrations-table-name [path]',
'Set migrations table name without a knexfile.'
)
.option('--client [name]', 'Set DB client.')
.option('--connection [address]', 'Set DB connection.')
.option('--migrations-directory [path]', 'Set migrations directory.')
.option('--migrations-table-name [path]', 'Set migrations table name.')
.option(
'--env [name]',
'environment, default: process.env.NODE_ENV || development'
Expand Down
40 changes: 33 additions & 7 deletions bin/utils/cli-config-utils.js
@@ -1,12 +1,39 @@
const { DEFAULT_EXT, DEFAULT_TABLE_NAME } = require('./constants');
const { resolveClientNameWithAliases } = require('../../lib/util/helpers');
const fs = require('fs');
const path = require('path');
const escalade = require('escalade/sync');
const tildify = require('tildify');
const color = require('colorette');
const argv = require('getopts')(process.argv.slice(2));

function findCaseInsensitiveProperty(propertyName, object) {
return Object.keys(object).find(
(key) => key.toLowerCase() === propertyName.toLowerCase()
);
}

function parseConfigObj(opts) {
const config = { migrations: {} };

if (opts.client) {
config.client = opts.client;
}

if (opts.connection) {
config.connection = opts.connection;
}

if (opts.migrationsDirectory) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

I wonder if there is a way to make all overrides case-insensitive. Probably we can do check on lowercased entry pairs.

Copy link
Collaborator

Choose a reason for hiding this comment

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

As I can understand, the goal is to lower case the properties in options object before merge with config in knexfile ?

Copy link
Collaborator

Choose a reason for hiding this comment

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

But some options are camelCase, no?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Mmh, I will check that this week.

config.migrations.directory = opts.migrationsDirectory;
}

if (opts.migrationsTableName) {
config.migrations.tableName = opts.migrationsTableName;
}

return config;
}

function mkConfigObj(opts) {
if (!opts.client) {
throw new Error(
Expand All @@ -17,16 +44,14 @@ function mkConfigObj(opts) {
const envName = opts.env || process.env.NODE_ENV || 'development';
const resolvedClientName = resolveClientNameWithAliases(opts.client);
const useNullAsDefault = resolvedClientName === 'sqlite3';
const parsedConfig = parseConfigObj(opts);

return {
ext: DEFAULT_EXT,
[envName]: {
...parsedConfig,
useNullAsDefault,
client: opts.client,
connection: opts.connection,
migrations: {
directory: opts.migrationsDirectory,
tableName: opts.migrationsTableName || DEFAULT_TABLE_NAME,
},
tableName: parsedConfig.tableName || DEFAULT_TABLE_NAME,
},
};
}
Expand Down Expand Up @@ -174,6 +199,7 @@ function findUpConfig(cwd, name, extensions) {
}

module.exports = {
parseConfigObj,
mkConfigObj,
resolveEnvironmentConfig,
exit,
Expand Down
10 changes: 10 additions & 0 deletions test/cli/cli-test-utils.js
Expand Up @@ -75,10 +75,20 @@ function setupFileHelper() {
return fileHelper;
}

function createTable(db, ddl) {
return new Promise((resolve, reject) =>
db.exec(`create TABLE if not exists ${ddl};`, (err) => {
if (err) reject(err);
else resolve();
})
);
}

module.exports = {
expectContentMatchesStub,
getRootDir,
migrationStubOptionSetup,
seedStubOptionSetup,
setupFileHelper,
createTable,
};
10 changes: 1 addition & 9 deletions test/cli/esm-interop.spec.js
Expand Up @@ -5,6 +5,7 @@ const fs = require('fs');
const { execCommand } = require('cli-testlab');
const sqlite3 = require('@vscode/sqlite3');
const semver = require('semver');
const { createTable } = require('./cli-test-utils');
const KNEX = path.normalize(__dirname + '/../../bin/cli.js');
const TEST_BASE = '../test/jake-util';

Expand Down Expand Up @@ -734,15 +735,6 @@ describe('esm interop and mjs support', () => {
}
});

function createTable(db, ddl) {
return new Promise((resolve, reject) =>
db.exec(`create TABLE if not exists ${ddl};`, (err) => {
if (err) reject(err);
else resolve();
})
);
}

function getSchema(db) {
return new Promise((resolve, reject) => {
db.all('SELECT name from SQLITE_MASTER', (err, rows) => {
Expand Down
10 changes: 5 additions & 5 deletions test/cli/migrate-make.spec.js
Expand Up @@ -124,7 +124,7 @@ module.exports = {
migrations: {
directory: __dirname + '/test/jake-util/knexfile_migrations',
},
};
};
`,
{ isPathAbsolute: true }
);
Expand Down Expand Up @@ -152,7 +152,7 @@ module.exports = {
migrations: {
directory: __dirname + '/test/jake-util/knexfile_migrations',
},
};
};
`,
{ isPathAbsolute: true }
);
Expand Down Expand Up @@ -187,7 +187,7 @@ module.exports = {
directory: __dirname + '/test/jake-util/knexfile_migrations',
}
}
};
};
`,
{ isPathAbsolute: true }
);
Expand Down Expand Up @@ -237,7 +237,7 @@ module.exports = {
extension: 'ts',
directory: __dirname + '/test/jake-util/knexfile_migrations',
},
};
};
`,
{ isPathAbsolute: true }
);
Expand Down Expand Up @@ -272,7 +272,7 @@ development: {
directory: __dirname + '/test/jake-util/knexfile_migrations',
},
}
};
};
`,
{ isPathAbsolute: true }
);
Expand Down
51 changes: 51 additions & 0 deletions test/cli/migrate.spec.js
Expand Up @@ -56,4 +56,55 @@ describe('migrate:latest', () => {
expect(row.name).to.equal('000_create_rule_table.js');
db.close();
});

it('CLI Options override knexfile', async () => {
const path = process.cwd() + '/knexfile.js';
fileHelper.createFile(
path,
`
module.exports = {
client: 'sqlite3',
connection: {
filename: '${dbPath}',
},
migrations: {
directory: __dirname + '/test//jake-util/knexfile_migrations',
},
};
`,
{ isPathAbsolute: true }
);

fileHelper.createFile(
'migrations/subdirectory/000_create_rule_table.js',
`
exports.up = (knex)=> knex.schema.createTable('rules', (table)=> {
table.string('name');
});
exports.down = (knex)=> knex.schema.dropTable('rules');
`,
{ willBeCleanedUp: true, isPathAbsolute: false }
);

expect(fileHelper.fileExists(dbPath)).to.equal(false);

await execCommand(`node ${KNEX} migrate:latest \
--knexpath=../knexfile.js \
--migrations-directory=${rootDir}/migrations/subdirectory/ \
--migrations-table-name=migration_table \
create_rule_table`);
expect(fileHelper.fileExists(dbPath)).to.equal(true);

const db = await new sqlite3.Database(dbPath);
const row = await new Promise((resolve, reject) => {
db.get('SELECT name FROM migration_table', {}, (err, row) => {
if (err) {
reject(err);
}
resolve(row);
});
});
expect(row.name).to.equal('000_create_rule_table.js');
db.close();
});
});