Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ npm install -D @types/better-sqlite3
For Postgres:

```bash
npm install pg pg-connection-string
npm install pg
npm install -D @types/pg
```

Expand Down
29 changes: 29 additions & 0 deletions packages/cli/src/actions/migrate.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import fs from 'node:fs';
import path from 'node:path';
import { CliError } from '../cli-error';
import { execPackage } from '../utils/exec-utils';
import { generateTempPrismaSchema, getSchemaFile } from './action-utils';

Expand All @@ -21,6 +22,11 @@ type DeployOptions = CommonOptions;

type StatusOptions = CommonOptions;

type ResolveOptions = CommonOptions & {
applied?: string;
rolledBack?: string;
};

/**
* CLI action for migration-related commands
*/
Expand All @@ -46,6 +52,10 @@ export async function run(command: string, options: CommonOptions) {
case 'status':
await runStatus(prismaSchemaFile, options as StatusOptions);
break;

case 'resolve':
await runResolve(prismaSchemaFile, options as ResolveOptions);
break;
}
} finally {
if (fs.existsSync(prismaSchemaFile)) {
Expand Down Expand Up @@ -100,6 +110,25 @@ async function runStatus(prismaSchemaFile: string, _options: StatusOptions) {
}
}

async function runResolve(prismaSchemaFile: string, options: ResolveOptions) {
if (!options.applied && !options.rolledBack) {
throw new CliError('Either --applied or --rolled-back option must be provided');
}

try {
const cmd = [
'prisma migrate resolve',
` --schema "${prismaSchemaFile}"`,
options.applied ? ` --applied ${options.applied}` : '',
options.rolledBack ? ` --rolled-back ${options.rolledBack}` : '',
].join('');

await execPackage(cmd);
} catch (err) {
handleSubProcessError(err);
}
}

function handleSubProcessError(err: unknown) {
if (err instanceof Error && 'status' in err && typeof err.status === 'number') {
process.exit(err.status);
Expand Down
11 changes: 10 additions & 1 deletion packages/cli/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,16 @@ export function createProgram() {
.command('status')
.addOption(schemaOption)
.addOption(migrationsOption)
.description('check the status of your database migrations.')
.description('Check the status of your database migrations.')
.action((options) => migrateAction('status', options));

migrateCommand
.command('resolve')
.addOption(schemaOption)
.addOption(migrationsOption)
.addOption(new Option('--applied <migration>', 'record a specific migration as applied'))
.addOption(new Option('--rolled-back <migration>', 'record a specific migration as rolled back'))
.description('Resolve issues with database migrations in deployment databases')
.action((options) => migrateAction('status', options));

const dbCommand = program.command('db').description('Manage your database schema during development.');
Expand Down
16 changes: 16 additions & 0 deletions packages/cli/test/migrate.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,20 @@ describe('CLI migrate commands test', () => {
runCli('migrate dev --name init', workDir);
runCli('migrate status', workDir);
});

it('supports migrate resolve', () => {
const workDir = createProject(model);
runCli('migrate dev --name init', workDir);

// find the migration record "timestamp_init"
const migrationRecords = fs.readdirSync(path.join(workDir, 'zenstack/migrations'));
const migration = migrationRecords.find((f) => f.endsWith('_init'));
expect(migration).toBeDefined();

// --rolled-back
runCli(`migrate resolve --rolled-back ${migration}`, workDir);

// --applied
runCli(`migrate resolve --applied ${migration}`, workDir);
});
});