Skip to content

Commit e4653fc

Browse files
committed
Add reset / squash migrations feature. Fixes #46
1 parent 2216747 commit e4653fc

16 files changed

+180
-35
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ A VS Code extension to manage Entity Framework migrations.
1717
- List migrations by [`DbContext`](https://learn.microsoft.com/en-us/dotnet/api/microsoft.entityframeworkcore.dbcontext)
1818
- Add / Remove / Run / Undo migrations
1919
- Show migration applied status
20+
- Reset (Squash) Migrations
2021
- Export `DbContext` as SQL script
2122
- View `DbContext` information
2223
- [Scaffold](https://learn.microsoft.com/en-us/ef/core/cli/dotnet#dotnet-ef-dbcontext-scaffold) `DbContext` & entity types

package.json

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,14 @@
5050
"dark": "icons/trash_dark.svg"
5151
}
5252
},
53+
{
54+
"command": "entityframework.resetMigrations",
55+
"title": "Reset Migrations to Here",
56+
"icon": {
57+
"light": "icons/reply_light.svg",
58+
"dark": "icons/reply_dark.svg"
59+
}
60+
},
5361
{
5462
"command": "entityframework.dbContextInfo",
5563
"title": "Information",
@@ -157,6 +165,10 @@
157165
"command": "entityframework.removeMigrations",
158166
"when": "false"
159167
},
168+
{
169+
"command": "entityframework.resetMigrations",
170+
"when": "false"
171+
},
160172
{
161173
"command": "entityframework.dbContextInfo",
162174
"when": "false"
@@ -204,6 +216,11 @@
204216
"when": "viewItem =~ /^migration-.*\\|?can-remove\\|?.*$/",
205217
"group": "context@2"
206218
},
219+
{
220+
"command": "entityframework.resetMigrations",
221+
"when": "viewItem =~ /^migration-.*\\|?can-reset\\|?.*$/",
222+
"group": "context@3"
223+
},
207224
{
208225
"command": "entityframework.runMigration",
209226
"when": "viewItem =~ /^migration-.*\\|?can-apply\\|?.*$/",

src/actions/AddMigrationAction.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ export class AddMigrationAction extends TerminalAction {
1717
private readonly workspaceRoot: string,
1818
private readonly dbContext: string,
1919
private readonly project: string,
20+
private readonly migrationName?: string,
2021
) {
2122
super(
2223
terminalProvider,
@@ -30,11 +31,13 @@ export class AddMigrationAction extends TerminalAction {
3031
}
3132

3233
public async run() {
33-
const migrationName = await vscode.window.showInputBox({
34-
title: 'Enter Migration Name',
35-
prompt: 'EG: MigrationName',
36-
ignoreFocusOut: true,
37-
});
34+
const migrationName =
35+
this.migrationName ||
36+
(await vscode.window.showInputBox({
37+
title: 'Enter Migration Name',
38+
prompt: 'EG: MigrationName',
39+
ignoreFocusOut: true,
40+
}));
3841
if (!migrationName) {
3942
return '';
4043
}

src/actions/RemoveMigrationAction.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ export class RemoveMigrationAction extends TerminalAction {
3030
}
3131

3232
public async run() {
33-
return vscode.window.withProgress(
33+
return await vscode.window.withProgress(
3434
{
3535
title: 'Removing Migration...',
3636
location: { viewId: TREE_VIEW_ID },

src/actions/RemoveMigrationsAction.ts

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@ import * as vscode from 'vscode';
22
import { CommandProvider } from '../commands/CommandProvider';
33
import { RefreshTreeCommand } from '../commands/RefreshTreeCommand';
44
import type { TerminalProvider } from '../terminal/TerminalProvider';
5+
import {
6+
dbContextsCache,
7+
DbContextTreeItem,
8+
} from '../treeView/DbContextTreeItem';
59
import type { MigrationTreeItem } from '../treeView/MigrationTreeItem';
610
import type { IAction } from './IAction';
711
import { RemoveMigrationAction } from './RemoveMigrationAction';
@@ -12,11 +16,23 @@ export class RemoveMigrationsAction implements IAction {
1216
private readonly workspaceRoot: string,
1317
private readonly dbContext: string,
1418
private readonly project: string,
15-
private readonly migrationsToRemove: MigrationTreeItem[],
19+
private readonly item: MigrationTreeItem,
1620
) {}
1721

1822
public async run() {
19-
for (let i = 0; i < this.migrationsToRemove.length; i++) {
23+
const migrations = dbContextsCache.get(
24+
DbContextTreeItem.getCacheId(
25+
this.workspaceRoot,
26+
this.project,
27+
this.dbContext,
28+
),
29+
);
30+
const index = (migrations || []).indexOf(this.item);
31+
if (index === -1) {
32+
return;
33+
}
34+
const migrationsToRemove = migrations?.slice(index) || [];
35+
for (let i = 0; i < migrationsToRemove.length; i++) {
2036
await new RemoveMigrationAction(
2137
this.terminalProvider,
2238
this.workspaceRoot,
@@ -25,7 +41,7 @@ export class RemoveMigrationsAction implements IAction {
2541
false,
2642
).run();
2743
}
28-
if (this.migrationsToRemove.length > 0) {
44+
if (migrationsToRemove.length > 0) {
2945
await vscode.commands.executeCommand(
3046
CommandProvider.getCommandName(RefreshTreeCommand.commandName),
3147
false,

src/actions/ResetMigrationsAction.ts

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import * as vscode from 'vscode';
2+
import { UndoMigrationCommand } from '../commands/UndoMigrationCommand';
3+
import type { TerminalProvider } from '../terminal/TerminalProvider';
4+
import {
5+
dbContextsCache,
6+
DbContextTreeItem,
7+
} from '../treeView/DbContextTreeItem';
8+
import type { MigrationTreeItem } from '../treeView/MigrationTreeItem';
9+
import { AddMigrationAction } from './AddMigrationAction';
10+
import type { IAction } from './IAction';
11+
import { RemoveMigrationAction } from './RemoveMigrationAction';
12+
13+
export class ResetMigrationsAction implements IAction {
14+
constructor(
15+
private readonly terminalProvider: TerminalProvider,
16+
private readonly workspaceRoot: string,
17+
private readonly dbContext: string,
18+
private readonly project: string,
19+
private readonly item: MigrationTreeItem,
20+
) {}
21+
22+
public async run() {
23+
const migrations = dbContextsCache.get(
24+
DbContextTreeItem.getCacheId(
25+
this.workspaceRoot,
26+
this.project,
27+
this.dbContext,
28+
),
29+
);
30+
const index = (migrations || []).indexOf(this.item);
31+
if (index === -1) {
32+
return;
33+
}
34+
const migrationsToReset = migrations?.slice(index) || [];
35+
if (!migrationsToReset.length) {
36+
return;
37+
}
38+
const options = ['Yes', 'Cancel'] as const;
39+
const answer = await vscode.window.showInformationMessage(
40+
'Any manual migration modifications will be lost! Are you sure you want to do this?',
41+
...options,
42+
);
43+
if (answer === 'Cancel') {
44+
return;
45+
}
46+
const migrationName = await vscode.window.showInputBox({
47+
title: 'Enter Migration Name',
48+
prompt: 'EG: MigrationName',
49+
ignoreFocusOut: true,
50+
});
51+
if (!migrationName) {
52+
return '';
53+
}
54+
await new UndoMigrationCommand(
55+
this.terminalProvider,
56+
this.item,
57+
false,
58+
).run();
59+
for (let i = 0; i < migrationsToReset.length; i++) {
60+
await new RemoveMigrationAction(
61+
this.terminalProvider,
62+
this.workspaceRoot,
63+
this.dbContext,
64+
this.project,
65+
false,
66+
).run();
67+
}
68+
await new AddMigrationAction(
69+
this.terminalProvider,
70+
this.workspaceRoot,
71+
this.dbContext,
72+
this.project,
73+
migrationName,
74+
).run();
75+
}
76+
}

src/actions/RunMigrationAction.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ export class RunMigrationAction extends TerminalAction {
1717
private readonly dbContext: string,
1818
private readonly project: string,
1919
migrationId: string,
20+
private readonly refresh?: boolean,
2021
) {
2122
super(
2223
terminalProvider,
@@ -51,10 +52,12 @@ export class RunMigrationAction extends TerminalAction {
5152
);
5253
dbContextsCache.clear(cacheId);
5354

54-
await vscode.commands.executeCommand(
55-
CommandProvider.getCommandName(RefreshTreeCommand.commandName),
56-
false,
57-
);
55+
if (this.refresh) {
56+
await vscode.commands.executeCommand(
57+
CommandProvider.getCommandName(RefreshTreeCommand.commandName),
58+
false,
59+
);
60+
}
5861

5962
return output;
6063
},

src/commands/CommandProvider.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import { RefreshDbContextTreeCommand } from './RefreshDbContextTreeCommand';
1818
import { RefreshProjectTreeCommand } from './RefreshProjectTreeCommand';
1919
import { RefreshTreeCommand } from './RefreshTreeCommand';
2020
import { RemoveMigrationsCommand } from './RemoveMigrationsCommand';
21+
import { ResetMigrationsCommand } from './ResetMigrationsCommand';
2122
import { RunMigrationCommand } from './RunMigrationCommand';
2223
import { ScaffoldCommand } from './ScaffoldCommand';
2324
import { UndoMigrationCommand } from './UndoMigrationCommand';
@@ -42,6 +43,12 @@ export class CommandProvider extends Disposable {
4243
return new RemoveMigrationsCommand(terminalProvider, item);
4344
},
4445
);
46+
this.registerCommand(
47+
ResetMigrationsCommand.commandName,
48+
(item?: MigrationTreeItem) => {
49+
return new ResetMigrationsCommand(terminalProvider, item);
50+
},
51+
);
4552
this.registerCommand(
4653
RunMigrationCommand.commandName,
4754
(item?: MigrationTreeItem) =>
Lines changed: 1 addition & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,5 @@
11
import { RemoveMigrationsAction } from '../actions/RemoveMigrationsAction';
22
import type { TerminalProvider } from '../terminal/TerminalProvider';
3-
import {
4-
dbContextsCache,
5-
DbContextTreeItem,
6-
} from '../treeView/DbContextTreeItem';
73
import { type MigrationTreeItem } from '../treeView/MigrationTreeItem';
84
import { Command } from './Command';
95

@@ -21,24 +17,12 @@ export class RemoveMigrationsCommand extends Command {
2117
if (!this.item) {
2218
return;
2319
}
24-
const migrations = dbContextsCache.get(
25-
DbContextTreeItem.getCacheId(
26-
this.item.workspaceRoot,
27-
this.item.projectFile.name,
28-
this.item.dbContext,
29-
),
30-
);
31-
const index = (migrations || []).indexOf(this.item);
32-
if (index === -1) {
33-
return;
34-
}
35-
const migrationsToRemove = migrations?.slice(index) || [];
3620
return new RemoveMigrationsAction(
3721
this.terminalProvider,
3822
this.item.workspaceRoot,
3923
this.item.dbContext,
4024
this.item.projectFile.name,
41-
migrationsToRemove,
25+
this.item,
4226
).run();
4327
}
4428
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { ResetMigrationsAction } from '../actions/ResetMigrationsAction';
2+
import type { TerminalProvider } from '../terminal/TerminalProvider';
3+
4+
import { type MigrationTreeItem } from '../treeView/MigrationTreeItem';
5+
import { Command } from './Command';
6+
7+
export class ResetMigrationsCommand extends Command {
8+
public static commandName = 'resetMigrations';
9+
10+
constructor(
11+
private readonly terminalProvider: TerminalProvider,
12+
private readonly item?: MigrationTreeItem,
13+
) {
14+
super();
15+
}
16+
17+
public async run() {
18+
if (!this.item) {
19+
return;
20+
}
21+
return new ResetMigrationsAction(
22+
this.terminalProvider,
23+
this.item.workspaceRoot,
24+
this.item.dbContext,
25+
this.item.projectFile.name,
26+
this.item,
27+
).run();
28+
}
29+
}

src/commands/RunMigrationCommand.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ export class RunMigrationCommand extends Command {
2323
this.item.dbContext,
2424
this.item.projectFile.name,
2525
this.item.migration.id,
26+
true,
2627
).run();
2728
}
2829
}

src/commands/UndoMigrationCommand.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ export class UndoMigrationCommand extends Command {
1313
constructor(
1414
private readonly terminalProvider: TerminalProvider,
1515
private readonly item?: MigrationTreeItem,
16+
private readonly refresh?: boolean,
1617
) {
1718
super();
1819
}
@@ -31,12 +32,13 @@ export class UndoMigrationCommand extends Command {
3132
const index = migrations.indexOf(this.item);
3233
const migrationId =
3334
index === 0 ? '0' : migrations[index - 1].migration.id;
34-
return new RunMigrationAction(
35+
return await new RunMigrationAction(
3536
this.terminalProvider,
3637
this.item.workspaceRoot,
3738
this.item.dbContext,
3839
this.item.projectFile.name,
3940
migrationId,
41+
this.refresh,
4042
).run();
4143
}
4244
}

src/treeView/DbContextTreeItem.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ export class DbContextTreeItem extends TreeItem {
8282
} catch (e) {
8383
const msg = `Unable to get migrations: ${(e as Error).message}`.trim();
8484
this.logger.error(msg);
85-
await vscode.window.showErrorMessage(msg, 'OK');
85+
void vscode.window.showErrorMessage(msg, 'OK');
8686
return [];
8787
}
8888
}

src/treeView/MigrationTreeItem.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,17 @@ export class MigrationTreeItem extends TreeItem {
3131

3232
function getMigrationContextValue(migration: Migration): string {
3333
const states: Array<
34-
'can-apply' | 'can-undo' | 'can-remove' | 'applied' | 'not-applied'
34+
| 'can-apply'
35+
| 'can-undo'
36+
| 'can-remove'
37+
| 'applied'
38+
| 'not-applied'
39+
| 'can-reset'
3540
> = [];
3641
if (migration.applied) {
3742
states.push('applied');
3843
states.push('can-undo');
44+
states.push('can-reset');
3945
} else {
4046
states.push('can-remove');
4147
states.push('can-apply');

src/treeView/ProjectTreeItem.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ export class ProjectTreeItem extends TreeItem {
8282
} catch (e) {
8383
const msg = `Unable to get dbContexts: ${(e as Error).message}`.trim();
8484
this.logger.error(msg);
85-
await vscode.window.showErrorMessage(msg, 'OK');
85+
void vscode.window.showErrorMessage(msg, 'OK');
8686
return [];
8787
}
8888
}

src/util/InputWizard.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ export class InputWizard {
8686
inputValues.push(inputVal);
8787
}
8888
} catch (e) {
89-
await vscode.window.showErrorMessage('Invalid input');
89+
void vscode.window.showErrorMessage('Invalid input');
9090
return [];
9191
}
9292
return inputValues;

0 commit comments

Comments
 (0)