From 0232bec305595ccfad34116b24d40b082cb73e5e Mon Sep 17 00:00:00 2001 From: Kory Nunn Date: Wed, 30 Mar 2022 15:37:08 +1000 Subject: [PATCH] Override knexfile options with CLI options (#4047) Co-authored-by: Olivier Cavadenti --- bin/cli.js | 27 +++++++++++-------- bin/utils/cli-config-utils.js | 40 ++++++++++++++++++++++----- test/cli/cli-test-utils.js | 10 +++++++ test/cli/esm-interop.spec.js | 10 +------ test/cli/migrate-make.spec.js | 10 +++---- test/cli/migrate.spec.js | 51 +++++++++++++++++++++++++++++++++++ 6 files changed, 116 insertions(+), 32 deletions(-) diff --git a/bin/cli.js b/bin/cli.js index 6034186046..c3e3268d25 100755 --- a/bin/cli.js +++ b/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'); @@ -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, @@ -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() { @@ -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' diff --git a/bin/utils/cli-config-utils.js b/bin/utils/cli-config-utils.js index d447ba8ddd..fb98ffcd76 100644 --- a/bin/utils/cli-config-utils.js +++ b/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) { + config.migrations.directory = opts.migrationsDirectory; + } + + if (opts.migrationsTableName) { + config.migrations.tableName = opts.migrationsTableName; + } + + return config; +} + function mkConfigObj(opts) { if (!opts.client) { throw new Error( @@ -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, }, }; } @@ -174,6 +199,7 @@ function findUpConfig(cwd, name, extensions) { } module.exports = { + parseConfigObj, mkConfigObj, resolveEnvironmentConfig, exit, diff --git a/test/cli/cli-test-utils.js b/test/cli/cli-test-utils.js index 39b69b0b5b..b45339529b 100644 --- a/test/cli/cli-test-utils.js +++ b/test/cli/cli-test-utils.js @@ -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, }; diff --git a/test/cli/esm-interop.spec.js b/test/cli/esm-interop.spec.js index f3b2480028..3607f74350 100644 --- a/test/cli/esm-interop.spec.js +++ b/test/cli/esm-interop.spec.js @@ -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'; @@ -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) => { diff --git a/test/cli/migrate-make.spec.js b/test/cli/migrate-make.spec.js index ae5a08bbed..ce9032d00c 100644 --- a/test/cli/migrate-make.spec.js +++ b/test/cli/migrate-make.spec.js @@ -124,7 +124,7 @@ module.exports = { migrations: { directory: __dirname + '/test/jake-util/knexfile_migrations', }, -}; +}; `, { isPathAbsolute: true } ); @@ -152,7 +152,7 @@ module.exports = { migrations: { directory: __dirname + '/test/jake-util/knexfile_migrations', }, -}; +}; `, { isPathAbsolute: true } ); @@ -187,7 +187,7 @@ module.exports = { directory: __dirname + '/test/jake-util/knexfile_migrations', } } -}; +}; `, { isPathAbsolute: true } ); @@ -237,7 +237,7 @@ module.exports = { extension: 'ts', directory: __dirname + '/test/jake-util/knexfile_migrations', }, -}; +}; `, { isPathAbsolute: true } ); @@ -272,7 +272,7 @@ development: { directory: __dirname + '/test/jake-util/knexfile_migrations', }, } -}; +}; `, { isPathAbsolute: true } ); diff --git a/test/cli/migrate.spec.js b/test/cli/migrate.spec.js index 435c19288c..ecd060c35a 100644 --- a/test/cli/migrate.spec.js +++ b/test/cli/migrate.spec.js @@ -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(); + }); });