diff --git a/.changeset/real-parrots-chew.md b/.changeset/real-parrots-chew.md new file mode 100644 index 0000000..6f5d15c --- /dev/null +++ b/.changeset/real-parrots-chew.md @@ -0,0 +1,5 @@ +--- +'@emigrate/cli': patch +--- + +Support stored migration histories that have only stored the migration file names without file extension and assume it's .js files in that case. This is to be compatible with a migration history generated by Immigration. diff --git a/packages/cli/src/collect-migrations.ts b/packages/cli/src/collect-migrations.ts index d583a44..dc1ce8f 100644 --- a/packages/cli/src/collect-migrations.ts +++ b/packages/cli/src/collect-migrations.ts @@ -1,3 +1,4 @@ +import { extname } from 'node:path'; import { type MigrationHistoryEntry, type MigrationMetadata, type MigrationMetadataFinished } from '@emigrate/types'; import { toMigrationMetadata } from './to-migration-metadata.js'; import { getMigrations as getMigrationsOriginal } from './get-migrations.js'; @@ -11,7 +12,9 @@ export async function* collectMigrations( const allMigrations = await getMigrations(cwd, directory); const seen = new Set(); - for await (const entry of history) { + for await (const entry_ of history) { + const entry = extname(entry_.name) === '' ? { ...entry_, name: `${entry_.name}.js` } : entry_; + const index = allMigrations.findIndex((migrationFile) => migrationFile.name === entry.name); if (index === -1) { diff --git a/packages/cli/src/commands/up.test.ts b/packages/cli/src/commands/up.test.ts index fe88b74..5cdae25 100644 --- a/packages/cli/src/commands/up.test.ts +++ b/packages/cli/src/commands/up.test.ts @@ -48,6 +48,60 @@ describe('up', () => { assert.deepStrictEqual(reporter.onFinished.mock.calls[0]?.arguments, [[], undefined]); }); + it('returns 0 and finishes without an error when all migrations have already been run', async () => { + const { reporter, run } = getUpCommand(['my_migration.js'], getStorage(['my_migration.js'])); + + const exitCode = await run(); + + assert.strictEqual(exitCode, 0); + assert.strictEqual(reporter.onInit.mock.calls.length, 1); + assert.deepStrictEqual(reporter.onInit.mock.calls[0]?.arguments, [ + { + command: 'up', + cwd: '/emigrate', + dry: false, + color: undefined, + version, + directory: 'migrations', + }, + ]); + assert.strictEqual(reporter.onCollectedMigrations.mock.calls.length, 1); + assert.strictEqual(reporter.onLockedMigrations.mock.calls.length, 1); + assert.strictEqual(reporter.onMigrationStart.mock.calls.length, 0); + assert.strictEqual(reporter.onMigrationSuccess.mock.calls.length, 0); + assert.strictEqual(reporter.onMigrationError.mock.calls.length, 0); + assert.strictEqual(reporter.onMigrationSkip.mock.calls.length, 0); + assert.strictEqual(reporter.onFinished.mock.calls.length, 1); + assert.deepStrictEqual(reporter.onFinished.mock.calls[0]?.arguments, [[], undefined]); + }); + + it('returns 0 and finishes without an error when all migrations have already been run even when the history responds without file extensions', async () => { + const { reporter, run } = getUpCommand(['my_migration.js'], getStorage(['my_migration'])); + + const exitCode = await run(); + + assert.strictEqual(exitCode, 0); + assert.strictEqual(reporter.onInit.mock.calls.length, 1); + assert.deepStrictEqual(reporter.onInit.mock.calls[0]?.arguments, [ + { + command: 'up', + cwd: '/emigrate', + dry: false, + color: undefined, + version, + directory: 'migrations', + }, + ]); + assert.strictEqual(reporter.onCollectedMigrations.mock.calls.length, 1); + assert.strictEqual(reporter.onLockedMigrations.mock.calls.length, 1); + assert.strictEqual(reporter.onMigrationStart.mock.calls.length, 0); + assert.strictEqual(reporter.onMigrationSuccess.mock.calls.length, 0); + assert.strictEqual(reporter.onMigrationError.mock.calls.length, 0); + assert.strictEqual(reporter.onMigrationSkip.mock.calls.length, 0); + assert.strictEqual(reporter.onFinished.mock.calls.length, 1); + assert.deepStrictEqual(reporter.onFinished.mock.calls[0]?.arguments, [[], undefined]); + }); + it('returns 1 and finishes with an error when there are migration file extensions without a corresponding loader plugin', async () => { const { reporter, run } = getUpCommand(['some_other.js', 'some_file.sql'], getStorage([])); diff --git a/packages/cli/src/get-migrations.ts b/packages/cli/src/get-migrations.ts index 735fc49..e208ac7 100644 --- a/packages/cli/src/get-migrations.ts +++ b/packages/cli/src/get-migrations.ts @@ -23,7 +23,10 @@ export const getMigrations = async (cwd: string, directory: string): Promise file.isFile() && !file.name.startsWith('.') && !file.name.startsWith('_')) + .filter( + (file) => + file.isFile() && !file.name.startsWith('.') && !file.name.startsWith('_') && path.extname(file.name) !== '', + ) .sort((a, b) => a.name.localeCompare(b.name)) .map(({ name }) => { const filePath = path.join(directoryPath, name);