1 | import * as assert from 'assert';
|
2 | import * as path from 'path';
|
3 | import { spinner, Command, Project, unwrap } from 'denali-cli';
|
4 | import * as tryRequire from 'try-require';
|
5 | import * as cmdExists from 'command-exists';
|
6 | import * as Bluebird from 'bluebird';
|
7 | import { exec } from 'child_process';
|
8 |
|
9 | const run = Bluebird.promisify<string, string>(exec);
|
10 | const commandExists = Bluebird.promisify<boolean, string>(cmdExists);
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 | export default class MigrateCommand extends Command {
|
18 |
|
19 |
|
20 | static commandName = 'migrate';
|
21 | static description = 'Run migrations to update your database schema';
|
22 | static longDescription = unwrap`
|
23 | Runs (or rolls back) schema migrations for your database. Typically only
|
24 | applies when use SQL-based databases.`;
|
25 |
|
26 | static flags = {
|
27 | environment: {
|
28 | description: 'The target environment to build for.',
|
29 | default: process.env.NODE_ENV || 'development',
|
30 | type: <any>'string'
|
31 | },
|
32 | rollback: {
|
33 | description: 'Rollback the latest migration, or latest --step migrations. Defaults to 1 step.',
|
34 | default: false,
|
35 | type: <any>'boolean'
|
36 | },
|
37 | redo: {
|
38 | description: 'Shortcut for rolling back then migrating up again. If used with --step, it will replay that many migrations. If used with --version, it will roll back to that version then replay. If neither, defaults to --step 1',
|
39 | default: false,
|
40 | type: <any>'boolean'
|
41 | }
|
42 | };
|
43 |
|
44 | static runsInApp = true;
|
45 |
|
46 | async run(argv: any) {
|
47 | let knex = tryRequire('knex');
|
48 | if (!knex) {
|
49 | await spinner.start('Installing knex (required for migrations)');
|
50 | let yarnExists = await commandExists('yarn');
|
51 | if (yarnExists) {
|
52 | await run('yarn add knex --mutex network');
|
53 | } else {
|
54 | await run('npm install --save knex');
|
55 | }
|
56 | knex = require('knex');
|
57 | await spinner.succeed('Knex installed');
|
58 | }
|
59 | let project = new Project({
|
60 | environment: argv.environment,
|
61 | buildDummy: true
|
62 | });
|
63 | let application = await project.createApplication();
|
64 | assert(application.config.migrations && application.config.migrations.db, 'DB connection info is missing. You must supply the knex connection info in config.migrations.db.');
|
65 | let db = knex(application.config.migrations.db);
|
66 | let migrationsDir = path.join(application.dir, 'config', 'migrations');
|
67 | try {
|
68 | if (argv.rollback) {
|
69 | await spinner.start('Rolling back last migration');
|
70 | await db.migrate.rollback({ directory: migrationsDir });
|
71 | } else if (argv.redo) {
|
72 | await spinner.start('Rolling back and replaying last migration');
|
73 | await db.migrate.rollback({ directory: migrationsDir });
|
74 | await db.migrate.latest({ directory: migrationsDir });
|
75 | } else {
|
76 | await spinner.start('Running migrations to latest');
|
77 | await db.migrate.latest({ directory: migrationsDir });
|
78 | }
|
79 | let newVersion = await db.migrate.currentVersion();
|
80 | await spinner.succeed(`Migrated to ${ newVersion }`);
|
81 | } catch (error) {
|
82 | await spinner.fail(`Migrations failed:\n${ error.stack }`);
|
83 | } finally {
|
84 | await db.destroy();
|
85 | }
|
86 | }
|
87 |
|
88 | }
|