feat(cli): add --no-execution option to the "up" command
...which can be used to log manually run migrations as successful or for baselining a database.
This commit is contained in:
parent
e71c318ea5
commit
f515c8a854
7 changed files with 138 additions and 18 deletions
|
|
@ -42,6 +42,8 @@ Options:
|
|||
--dry List the pending migrations that would be run without actually running them
|
||||
--color Force color output (this option is passed to the reporter)
|
||||
--no-color Disable color output (this option is passed to the reporter)
|
||||
--no-execution Mark the migrations as executed and successful without actually running them,
|
||||
which is useful if you want to mark migrations as successful after running them manually
|
||||
|
||||
Examples:
|
||||
|
||||
|
|
@ -51,6 +53,7 @@ Examples:
|
|||
emigrate up -d ./migrations -s mysql --import dotenv/config
|
||||
emigrate up --limit 1
|
||||
emigrate up --to 20231122120529381_some_migration_file.js
|
||||
emigrate up --to 20231122120529381_some_migration_file.js --no-execution
|
||||
```
|
||||
|
||||
### Examples
|
||||
|
|
|
|||
|
|
@ -72,6 +72,9 @@ const up: Action = async (args) => {
|
|||
color: {
|
||||
type: 'boolean',
|
||||
},
|
||||
'no-execution': {
|
||||
type: 'boolean',
|
||||
},
|
||||
'no-color': {
|
||||
type: 'boolean',
|
||||
},
|
||||
|
|
@ -100,6 +103,8 @@ Options:
|
|||
--dry List the pending migrations that would be run without actually running them
|
||||
--color Force color output (this option is passed to the reporter)
|
||||
--no-color Disable color output (this option is passed to the reporter)
|
||||
--no-execution Mark the migrations as executed and successful without actually running them,
|
||||
which is useful if you want to mark migrations as successful after running them manually
|
||||
|
||||
Examples:
|
||||
|
||||
|
|
@ -109,6 +114,7 @@ Examples:
|
|||
emigrate up -d ./migrations -s mysql --import dotenv/config
|
||||
emigrate up --limit 1
|
||||
emigrate up --to 20231122120529381_some_migration_file.js
|
||||
emigrate up --to 20231122120529381_some_migration_file.js --no-execution
|
||||
`;
|
||||
|
||||
if (values.help) {
|
||||
|
|
@ -127,6 +133,7 @@ Examples:
|
|||
to,
|
||||
limit: limitString,
|
||||
import: imports = [],
|
||||
'no-execution': noExecution,
|
||||
} = values;
|
||||
const plugins = [...(config.plugins ?? []), ...(values.plugin ?? [])];
|
||||
|
||||
|
|
@ -153,6 +160,7 @@ Examples:
|
|||
limit,
|
||||
from,
|
||||
to,
|
||||
noExecution,
|
||||
color: useColors(values),
|
||||
});
|
||||
} catch (error) {
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ describe('up', () => {
|
|||
|
||||
const exitCode = await run();
|
||||
|
||||
assert.strictEqual(exitCode, 1);
|
||||
assert.strictEqual(exitCode, 1, 'Exit code');
|
||||
assertPreconditionsFailed({ dry: false }, reporter, StorageInitError.fromError(new Error('No storage configured')));
|
||||
});
|
||||
|
||||
|
|
@ -37,7 +37,7 @@ describe('up', () => {
|
|||
|
||||
const exitCode = await run();
|
||||
|
||||
assert.strictEqual(exitCode, 0);
|
||||
assert.strictEqual(exitCode, 0, 'Exit code');
|
||||
assertPreconditionsFulfilled({ dry: false }, reporter, []);
|
||||
});
|
||||
|
||||
|
|
@ -46,7 +46,7 @@ describe('up', () => {
|
|||
|
||||
const exitCode = await run();
|
||||
|
||||
assert.strictEqual(exitCode, 0);
|
||||
assert.strictEqual(exitCode, 0, 'Exit code');
|
||||
assertPreconditionsFulfilled({ dry: false }, reporter, []);
|
||||
});
|
||||
|
||||
|
|
@ -55,7 +55,7 @@ describe('up', () => {
|
|||
|
||||
const exitCode = await run();
|
||||
|
||||
assert.strictEqual(exitCode, 0);
|
||||
assert.strictEqual(exitCode, 0, 'Exit code');
|
||||
assertPreconditionsFulfilled({ dry: false }, reporter, []);
|
||||
});
|
||||
|
||||
|
|
@ -78,7 +78,7 @@ describe('up', () => {
|
|||
|
||||
const exitCode = await run();
|
||||
|
||||
assert.strictEqual(exitCode, 0);
|
||||
assert.strictEqual(exitCode, 0, 'Exit code');
|
||||
assertPreconditionsFulfilled({ dry: false }, reporter, [
|
||||
{ name: 'some_migration.js', status: 'done', started: true },
|
||||
{ name: 'some_other_migration.js', status: 'done', started: true },
|
||||
|
|
@ -106,7 +106,7 @@ describe('up', () => {
|
|||
|
||||
const exitCode = await run();
|
||||
|
||||
assert.strictEqual(exitCode, 1);
|
||||
assert.strictEqual(exitCode, 1, 'Exit code');
|
||||
assertPreconditionsFulfilled(
|
||||
{ dry: false },
|
||||
reporter,
|
||||
|
|
@ -125,7 +125,7 @@ describe('up', () => {
|
|||
|
||||
const exitCode = await run();
|
||||
|
||||
assert.strictEqual(exitCode, 1);
|
||||
assert.strictEqual(exitCode, 1, 'Exit code');
|
||||
assertPreconditionsFulfilled(
|
||||
{ dry: false },
|
||||
reporter,
|
||||
|
|
@ -146,7 +146,7 @@ describe('up', () => {
|
|||
|
||||
const exitCode = await run({ dry: true });
|
||||
|
||||
assert.strictEqual(exitCode, 1);
|
||||
assert.strictEqual(exitCode, 1, 'Exit code');
|
||||
assertPreconditionsFulfilled(
|
||||
{ dry: true },
|
||||
reporter,
|
||||
|
|
@ -170,7 +170,7 @@ describe('up', () => {
|
|||
|
||||
const exitCode = await run();
|
||||
|
||||
assert.strictEqual(exitCode, 1);
|
||||
assert.strictEqual(exitCode, 1, 'Exit code');
|
||||
assertPreconditionsFulfilled(
|
||||
{ dry: false },
|
||||
reporter,
|
||||
|
|
@ -198,7 +198,7 @@ describe('up', () => {
|
|||
|
||||
const exitCode = await run({ dry: true });
|
||||
|
||||
assert.strictEqual(exitCode, 1);
|
||||
assert.strictEqual(exitCode, 1, 'Exit code');
|
||||
assertPreconditionsFulfilled(
|
||||
{ dry: true },
|
||||
reporter,
|
||||
|
|
@ -226,7 +226,7 @@ describe('up', () => {
|
|||
|
||||
const exitCode = await run();
|
||||
|
||||
assert.strictEqual(exitCode, 0);
|
||||
assert.strictEqual(exitCode, 0, 'Exit code');
|
||||
assertPreconditionsFulfilled({ dry: false }, reporter, []);
|
||||
});
|
||||
});
|
||||
|
|
@ -250,7 +250,7 @@ describe('up', () => {
|
|||
|
||||
const exitCode = await run({ limit: 1 });
|
||||
|
||||
assert.strictEqual(exitCode, 0);
|
||||
assert.strictEqual(exitCode, 0, 'Exit code');
|
||||
assertPreconditionsFulfilled({ dry: false }, reporter, [
|
||||
{ name: 'some_migration.js', status: 'done', started: true },
|
||||
{ name: 'some_other_migration.js', status: 'skipped' },
|
||||
|
|
@ -267,7 +267,7 @@ describe('up', () => {
|
|||
|
||||
const exitCode = await run({ dry: true, limit: 1 });
|
||||
|
||||
assert.strictEqual(exitCode, 0);
|
||||
assert.strictEqual(exitCode, 0, 'Exit code');
|
||||
assertPreconditionsFulfilled({ dry: true }, reporter, [
|
||||
{ name: 'some_migration.js', status: 'pending' },
|
||||
{ name: 'some_other_migration.js', status: 'skipped' },
|
||||
|
|
@ -293,7 +293,7 @@ describe('up', () => {
|
|||
|
||||
const exitCode = await run({ from: '3_non_existing_migration.js' });
|
||||
|
||||
assert.strictEqual(exitCode, 0);
|
||||
assert.strictEqual(exitCode, 0, 'Exit code');
|
||||
assertPreconditionsFulfilled({ dry: false }, reporter, [
|
||||
{ name: '2_some_migration.js', status: 'skipped' },
|
||||
{ name: '4_some_other_migration.js', status: 'done', started: true },
|
||||
|
|
@ -309,7 +309,7 @@ describe('up', () => {
|
|||
|
||||
const exitCode = await run({ dry: true, from: '3_non_existing_migration.js' });
|
||||
|
||||
assert.strictEqual(exitCode, 0);
|
||||
assert.strictEqual(exitCode, 0, 'Exit code');
|
||||
assertPreconditionsFulfilled({ dry: true }, reporter, [
|
||||
{ name: '2_some_migration.js', status: 'skipped' },
|
||||
{ name: '4_some_other_migration.js', status: 'pending' },
|
||||
|
|
@ -335,7 +335,7 @@ describe('up', () => {
|
|||
|
||||
const exitCode = await run({ to: '3_non_existing_migration.js' });
|
||||
|
||||
assert.strictEqual(exitCode, 0);
|
||||
assert.strictEqual(exitCode, 0, 'Exit code');
|
||||
assertPreconditionsFulfilled({ dry: false }, reporter, [
|
||||
{ name: '2_some_migration.js', status: 'done', started: true },
|
||||
{ name: '4_some_other_migration.js', status: 'skipped' },
|
||||
|
|
@ -351,7 +351,7 @@ describe('up', () => {
|
|||
|
||||
const exitCode = await run({ dry: true, to: '3_non_existing_migration.js' });
|
||||
|
||||
assert.strictEqual(exitCode, 0);
|
||||
assert.strictEqual(exitCode, 0, 'Exit code');
|
||||
assertPreconditionsFulfilled({ dry: true }, reporter, [
|
||||
{ name: '2_some_migration.js', status: 'pending' },
|
||||
{ name: '4_some_other_migration.js', status: 'skipped' },
|
||||
|
|
@ -384,7 +384,7 @@ describe('up', () => {
|
|||
|
||||
const exitCode = await run({ from: '3_another_migration.js', to: '5_yet_another_migration.js', limit: 2 });
|
||||
|
||||
assert.strictEqual(exitCode, 0);
|
||||
assert.strictEqual(exitCode, 0, 'Exit code');
|
||||
assertPreconditionsFulfilled({ dry: false }, reporter, [
|
||||
{ name: '2_some_migration.js', status: 'skipped' },
|
||||
{ name: '3_another_migration.js', status: 'done', started: true },
|
||||
|
|
@ -395,6 +395,92 @@ describe('up', () => {
|
|||
assert.strictEqual(migration.mock.calls.length, 2);
|
||||
});
|
||||
});
|
||||
|
||||
describe('marking migrations as successful without running them', () => {
|
||||
it('returns 0 and finishes without an error when the pending migrations have been marked as successful without executing them', async () => {
|
||||
const migration = mock.fn(async () => {
|
||||
// Success
|
||||
});
|
||||
const { reporter, run } = getUpCommand(
|
||||
[
|
||||
'1_some_already_run_migration.js',
|
||||
'2_some_migration.js',
|
||||
'3_another_migration.js',
|
||||
'4_some_other_migration.js',
|
||||
'5_yet_another_migration.js',
|
||||
'6_some_more_migration.js',
|
||||
],
|
||||
getStorage(['1_some_already_run_migration.js']),
|
||||
[
|
||||
{
|
||||
loadableExtensions: ['.js'],
|
||||
async loadMigration() {
|
||||
return migration;
|
||||
},
|
||||
},
|
||||
],
|
||||
);
|
||||
|
||||
const exitCode = await run({
|
||||
from: '3_another_migration.js',
|
||||
to: '5_yet_another_migration.js',
|
||||
limit: 2,
|
||||
noExecution: true,
|
||||
});
|
||||
|
||||
assert.strictEqual(exitCode, 0, 'Exit code');
|
||||
assertPreconditionsFulfilled({ dry: false }, reporter, [
|
||||
{ name: '2_some_migration.js', status: 'skipped' },
|
||||
{ name: '3_another_migration.js', status: 'done', started: true },
|
||||
{ name: '4_some_other_migration.js', status: 'done', started: true },
|
||||
{ name: '5_yet_another_migration.js', status: 'skipped' },
|
||||
{ name: '6_some_more_migration.js', status: 'skipped' },
|
||||
]);
|
||||
assert.strictEqual(migration.mock.calls.length, 0);
|
||||
});
|
||||
});
|
||||
|
||||
it('returns 0 and finishes without an error when the pending migrations have been marked as successful without executing them even though they have no corresponding loader', async () => {
|
||||
const migration = mock.fn(async () => {
|
||||
// Success
|
||||
});
|
||||
const { reporter, run } = getUpCommand(
|
||||
[
|
||||
'1_some_already_run_migration.js',
|
||||
'2_some_migration.js',
|
||||
'3_another_migration.js',
|
||||
'4_some_other_migration.sql',
|
||||
'5_yet_another_migration.js',
|
||||
'6_some_more_migration.js',
|
||||
],
|
||||
getStorage(['1_some_already_run_migration.js']),
|
||||
[
|
||||
{
|
||||
loadableExtensions: ['.js'],
|
||||
async loadMigration() {
|
||||
return migration;
|
||||
},
|
||||
},
|
||||
],
|
||||
);
|
||||
|
||||
const exitCode = await run({
|
||||
from: '3_another_migration.js',
|
||||
to: '5_yet_another_migration.js',
|
||||
limit: 2,
|
||||
noExecution: true,
|
||||
});
|
||||
|
||||
assert.strictEqual(exitCode, 0, 'Exit code');
|
||||
assertPreconditionsFulfilled({ dry: false }, reporter, [
|
||||
{ name: '2_some_migration.js', status: 'skipped' },
|
||||
{ name: '3_another_migration.js', status: 'done', started: true },
|
||||
{ name: '4_some_other_migration.sql', status: 'done', started: true },
|
||||
{ name: '5_yet_another_migration.js', status: 'skipped' },
|
||||
{ name: '6_some_more_migration.js', status: 'skipped' },
|
||||
]);
|
||||
assert.strictEqual(migration.mock.calls.length, 0);
|
||||
});
|
||||
});
|
||||
|
||||
function getErrorCause(error: Error | undefined): Error | SerializedError | undefined {
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ type ExtraFlags = {
|
|||
limit?: number;
|
||||
from?: string;
|
||||
to?: string;
|
||||
noExecution?: boolean;
|
||||
getMigrations?: GetMigrationsFunction;
|
||||
};
|
||||
|
||||
|
|
@ -31,6 +32,7 @@ export default async function upCommand({
|
|||
limit,
|
||||
from,
|
||||
to,
|
||||
noExecution,
|
||||
dry = false,
|
||||
plugins = [],
|
||||
cwd,
|
||||
|
|
@ -96,6 +98,10 @@ export default async function upCommand({
|
|||
storage,
|
||||
migrations: await arrayFromAsync(collectedMigrations),
|
||||
async validate(migration) {
|
||||
if (noExecution) {
|
||||
return;
|
||||
}
|
||||
|
||||
const loader = getLoaderByExtension(migration.extension);
|
||||
|
||||
if (!loader) {
|
||||
|
|
@ -106,6 +112,10 @@ export default async function upCommand({
|
|||
}
|
||||
},
|
||||
async execute(migration) {
|
||||
if (noExecution) {
|
||||
return;
|
||||
}
|
||||
|
||||
const loader = getLoaderByExtension(migration.extension)!;
|
||||
const [migrationFunction, loadError] = await exec(async () => loader.loadMigration(migration));
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue