From a2ebb6f8396d62e96ab51c6f29e5cd9addc434fd Mon Sep 17 00:00:00 2001 From: cupcakearmy Date: Fri, 26 Mar 2021 17:20:34 +0100 Subject: [PATCH] single and dry run --- src/cli.ts | 7 ++++--- src/index.ts | 45 ++++++++++++++++++++++++++++++++++++++------- 2 files changed, 42 insertions(+), 10 deletions(-) diff --git a/src/cli.ts b/src/cli.ts index fe29a84..c396f39 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -10,11 +10,12 @@ program.version(spec.version).name(spec.name) program .command('migrate') .description('run migrations') - // .option('--dry-run', 'run simulation without committing changes') + .option('--dry-run', 'run simulation without committing changes') .option('-m, --migrations ', 'migration files', './migrations/*.js') - .option('--force', 'ignore remote state and rerun migrations') + .option('--only ', 'only run specific migration') + .option('-f, --force', 'ignore remote state and rerun migrations') .action(async (args) => { - await migrate({ directory: args.migrations, ignoreRemote: args.force }) + await migrate({ directory: args.migrations, ignoreRemote: args.force, single: args.only, dryRun: args.dryRun }) }) program.parse(process.argv) diff --git a/src/index.ts b/src/index.ts index 8689dc2..8e2d4dc 100644 --- a/src/index.ts +++ b/src/index.ts @@ -32,16 +32,23 @@ export type Options = { directory: string delimiter: string ignoreRemote: boolean + single?: string[] + dryRun: boolean } const defaults: Options = { directory: './migrations', delimiter: '__', ignoreRemote: false, + dryRun: false, } const extension = /\..*$/ +function sortMigrationFiles(arr: MigrationFile[]): MigrationFile[] { + return arr.sort((a, b) => (semver.gt(a.version, b.version) ? 1 : -1)) +} + async function gather(options: Options): Promise { const files = glob .sync(path.join(options.directory, '*.js')) @@ -68,11 +75,11 @@ async function gather(options: Options): Promise { }) ) - const sorted = contents.sort((a, b) => (semver.gt(a.version, b.version) ? 1 : -1)) - return sorted.map(({ version, ...rest }) => ({ + const asMigrationFile = contents.map(({ version, ...rest }) => ({ ...rest, version: version.version, })) + return sortMigrationFiles(asMigrationFile) } function getIdFromMigration(migration: MigrationFile): string { @@ -93,6 +100,11 @@ async function runMigrations(migrations: MigrationFile[], options: Options) { continue } + if (options.dryRun) { + printMigration(migration, 'Skip due to dry-run.') + return + } + const start = process.hrtime.bigint() let error = false try { @@ -115,15 +127,34 @@ async function runMigrations(migrations: MigrationFile[], options: Options) { await remoteDoc.ref.set(result) if (error) { - console.log('⚠️ Skipping next migrations') - break + throw new Error('⚠️ Skipping next migrations') } } } } export async function migrate(options?: Partial) { - const merged: Options = Object.assign(defaults, options) - const migrations = await gather(merged) - await runMigrations(migrations, merged) + try { + const merged: Options = Object.assign(defaults, options) + let migrations = await gather(merged) + console.log(`Found ${chalk.bold(migrations.length)} migrations.`) + if (options?.single) { + const singleVersions = options.single.map((v) => { + const parsed = semver.coerce(v) + if (!parsed) throw new Error(`Invalid version specified: "${v}". Could not parse.`) + return parsed.version + }) + const filtered = singleVersions.map((v) => { + const selected = migrations.find((m) => m.version === v) + if (!selected) throw new Error(`Version "${v}" specified in --only does not exist in as migration.`) + return selected + }) + migrations = sortMigrationFiles(filtered) + console.log(`Only running specified versions: ${singleVersions.join(', ')}`) + } + await runMigrations(migrations, merged) + } catch (e) { + console.error(chalk.red(e.message)) + process.exit(1) + } }