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

Fix some issues around seed and migration generation #3479

Merged
merged 3 commits into from Oct 13, 2019
Merged
Show file tree
Hide file tree
Changes from all 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
23 changes: 15 additions & 8 deletions bin/cli.js
Expand Up @@ -18,9 +18,10 @@ const {
success,
checkLocalModule,
getMigrationExtension,
getSeedExtension,
getStubPath,
} = require('./utils/cli-config-utils');
const { DEFAULT_EXT } = require('./utils/constants');

const { listMigrations } = require('./utils/migrationsLister');

function initKnex(env, opts) {
Expand Down Expand Up @@ -157,7 +158,7 @@ function invoke(env) {
const ext = getMigrationExtension(env, opts);
const configOverrides = { extension: ext };

const stub = getStubPath(env, opts);
const stub = getStubPath('migrations', env, opts);
if (stub) {
configOverrides.stub = stub;
}
Expand Down Expand Up @@ -292,17 +293,23 @@ function invoke(env) {
`-x [${filetypes.join('|')}]`,
'Specify the stub extension (default js)'
)
.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'
)
.action((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 ext = (
argv.x ||
env.configuration.ext ||
DEFAULT_EXT
).toLowerCase();
const ext = getSeedExtension(env, opts);
const configOverrides = { extension: ext };
const stub = getStubPath('seeds', env, opts);
if (stub) {
configOverrides.stub = stub;
}

pending = instance.seed
.make(name, { extension: ext })
.make(name, configOverrides)
.then((name) => {
success(color.green(`Created seed file: ${name}`));
})
Expand Down
23 changes: 19 additions & 4 deletions bin/utils/cli-config-utils.js
Expand Up @@ -119,9 +119,23 @@ function getMigrationExtension(env, opts) {
return ext.toLowerCase();
}

function getStubPath(env, opts) {
function getSeedExtension(env, opts) {
const config = resolveEnvironmentConfig(opts, env.configuration);
const stubDirectory = config.migrations && config.migrations.directory;

let ext = DEFAULT_EXT;
if (argv.x) {
ext = argv.x;
} else if (config.seeds && config.seeds.extension) {
ext = config.seeds.extension;
} else if (config.ext) {
ext = config.ext;
}
return ext.toLowerCase();
}

function getStubPath(configKey, env, opts) {
const config = resolveEnvironmentConfig(opts, env.configuration);
const stubDirectory = config[configKey] && config[configKey].directory;

const { stub } = argv;
if (!stub) {
Expand All @@ -131,10 +145,10 @@ function getStubPath(env, opts) {
return stub;
}

// using stub <name> must have config.migrations.directory defined
// using stub <name> must have config[configKey].directory defined
if (!stubDirectory) {
console.log(color.red('Failed to load stub'), color.magenta(stub));
exit('config.migrations.directory in knexfile must be defined');
exit(`config.${configKey}.directory in knexfile must be defined`);
}

return path.join(stubDirectory, stub);
Expand All @@ -147,6 +161,7 @@ module.exports = {
exit,
success,
checkLocalModule,
getSeedExtension,
getMigrationExtension,
getStubPath,
};
60 changes: 29 additions & 31 deletions lib/migrate/MigrationGenerator.js
@@ -1,8 +1,8 @@
const fs = require('fs');
const path = require('path');
const mkdirp = require('mkdirp');
const bluebird = require('bluebird');
const { template } = require('lodash');
const {promisify} = require('util');
const { writeJsFileUsingTemplate } = require('../util/template');
const { getMergedConfig } = require('./configuration-merger');

class MigrationGenerator {
Expand All @@ -11,17 +11,16 @@ class MigrationGenerator {
}

// Creates a new migration, with a given name.
make(name, config) {
async make(name, config) {
this.config = getMergedConfig(config, this.config);
if (!name) {
return Promise.reject(
new Error('A name must be specified for the generated migration')
);
}

return this._ensureFolder(config)
.then((val) => this._generateStubTemplate(val))
.then((val) => this._writeNewMigration(name, val));
await this._ensureFolder();
const createdMigrationFilePath = await this._writeNewMigration(name);
return createdMigrationFilePath;
}

// Ensures a folder for the migrations exist, dependent on the migration
Expand All @@ -30,42 +29,41 @@ class MigrationGenerator {
const dirs = this._absoluteConfigDirs();

const promises = dirs.map((dir) => {
return bluebird
.promisify(fs.stat, { context: fs })(dir)
.catch(() => bluebird.promisify(mkdirp)(dir));
return promisify(fs.stat)(dir)
.catch(() => promisify(mkdirp)(dir));
});

return Promise.all(promises);
}

// Generates the stub template for the current migration, returning a compiled
// template.
_generateStubTemplate() {
const stubPath =
this.config.stub ||
_getStubPath() {
return this.config.stub ||
path.join(__dirname, 'stub', this.config.extension + '.stub');
}

return bluebird
.promisify(fs.readFile, { context: fs })(stubPath)
.then((stub) => template(stub.toString(), { variable: 'd' }));
_getNewMigrationName(name) {
if (name[0] === '-') name = name.slice(1);
return yyyymmddhhmmss() + '_' + name + '.' + this.config.extension;
}

// Write a new migration to disk, using the config and generated filename,
// passing any `variables` given in the config to the template.
_writeNewMigration(name, tmpl) {
const { config } = this;
_getNewMigrationPath(name) {
const fileName = this._getNewMigrationName(name);
const dirs = this._absoluteConfigDirs();
const dir = dirs.slice(-1)[0]; // Get last specified directory
return path.join(dir, fileName);
}

if (name[0] === '-') name = name.slice(1);
const filename = yyyymmddhhmmss() + '_' + name + '.' + config.extension;

return bluebird
.promisify(fs.writeFile, { context: fs })(
path.join(dir, filename),
tmpl(config.variables || {})
)
.return(path.join(dir, filename));
// Write a new migration to disk, using the config and generated filename,
// passing any `variables` given in the config to the template.
async _writeNewMigration(name) {
const migrationPath = this._getNewMigrationPath(name);
await writeJsFileUsingTemplate(
migrationPath,
this._getStubPath(),
{ variable: 'd' },
this.config.variables || {}
);
return migrationPath;
}

_absoluteConfigDirs() {
Expand Down