Skip to content

Commit 28cea06

Browse files
authored
Add current command (equivalent to watch --once) (#254)
2 parents f8439a5 + d0c6e3d commit 28cea06

8 files changed

Lines changed: 224 additions & 12 deletions

File tree

README.md

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,9 @@ Commands:
180180
graphile-migrate watch Runs any un-executed committed migrations and
181181
then runs and watches the current migration,
182182
re-running it on any change. For development.
183+
graphile-migrate current Runs any un-executed committed migrations, as
184+
well as the current migration. For
185+
development.
183186
graphile-migrate commit Commits the current migration into the
184187
`committed/` folder, resetting the current
185188
migration. Resets the shadow database.
@@ -284,12 +287,28 @@ migration, re-running it on any change. For development.
284287
Options:
285288
--help Show help [boolean]
286289
-c, --config Optional path to gmrc file [string] [default: .gmrc[.js|.cjs]]
287-
--once Runs the current migration and then exits.
288-
[boolean] [default: false]
290+
--once Runs the current migration and then exits (equivalent to
291+
`graphile-migrate current`). [boolean] [default: false]
289292
--shadow Applies changes to shadow DB. [boolean] [default: false]
290293
```
291294

292295

296+
## graphile-migrate current
297+
298+
```
299+
graphile-migrate current
300+
301+
Runs any un-executed committed migrations, as well as the current migration. For
302+
development.
303+
304+
Options:
305+
--help Show help [boolean]
306+
-c, --config Optional path to gmrc file [string] [default: .gmrc[.js|.cjs]]
307+
--shadow Apply migrations to the shadow DB (for development).
308+
[boolean] [default: false]
309+
```
310+
311+
293312
## graphile-migrate commit
294313

295314
```

__tests__/current.test.ts

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
import "./helpers"; // Has side-effects; must come first
2+
3+
import mockFs from "mock-fs";
4+
5+
import { current } from "../src";
6+
import { withClient } from "../src/pg";
7+
import { ParsedSettings, parseSettings } from "../src/settings";
8+
import { makeMigrations, resetDb, settings } from "./helpers";
9+
10+
beforeEach(resetDb);
11+
beforeEach(async () => {
12+
mockFs({ migrations: mockFs.directory() });
13+
});
14+
afterEach(() => {
15+
mockFs.restore();
16+
});
17+
const {
18+
MIGRATION_1_COMMITTED,
19+
MIGRATION_ENUM_COMMITTED,
20+
MIGRATION_NOTRX_TEXT,
21+
MIGRATION_NOTRX_COMMITTED,
22+
} = makeMigrations();
23+
24+
function getStuff(parsedSettings: ParsedSettings) {
25+
return withClient(
26+
parsedSettings.connectionString,
27+
parsedSettings,
28+
async (pgClient, _context) => {
29+
const { rows: migrations } = await pgClient.query(
30+
"select * from graphile_migrate.migrations",
31+
);
32+
const { rows: tables } = await pgClient.query(
33+
"select * from pg_class where relnamespace = 'public'::regnamespace and relkind = 'r'",
34+
);
35+
const { rows: enums } = await pgClient.query(
36+
"select typname, (select count(*) from pg_enum where enumtypid = pg_type.oid) as value_count from pg_type where typnamespace = 'public'::regnamespace and typtype = 'e'",
37+
);
38+
return { migrations, tables, enums };
39+
},
40+
);
41+
}
42+
43+
it("runs migrations", async () => {
44+
mockFs({
45+
"migrations/current.sql": "",
46+
});
47+
48+
await current(settings);
49+
const parsedSettings = await parseSettings(settings);
50+
51+
{
52+
const { migrations, tables, enums } = await getStuff(parsedSettings);
53+
expect(migrations).toHaveLength(0);
54+
expect(tables).toHaveLength(0);
55+
expect(enums).toHaveLength(0);
56+
}
57+
58+
mockFs({
59+
[`migrations/committed/000001.sql`]: MIGRATION_1_COMMITTED,
60+
[`migrations/committed/000002.sql`]: MIGRATION_ENUM_COMMITTED, // Creates enum with 1 value
61+
"migrations/current.sql": MIGRATION_NOTRX_TEXT, // Adds a value to the enum - total = 2
62+
});
63+
64+
await current(settings);
65+
66+
const { migrations, tables, enums } = await getStuff(parsedSettings);
67+
68+
expect(migrations).toHaveLength(2);
69+
expect(migrations.map(({ date, ...rest }) => rest)).toMatchInlineSnapshot(`
70+
[
71+
{
72+
"filename": "000001.sql",
73+
"hash": "sha1:e00ec93314a423ee5cc68d1182ad52f16442d7df",
74+
"previous_hash": null,
75+
},
76+
{
77+
"filename": "000002.sql",
78+
"hash": "sha1:bddc1ead3310dc1c42cdc7f63537ebdff2e9fd7b",
79+
"previous_hash": "sha1:e00ec93314a423ee5cc68d1182ad52f16442d7df",
80+
},
81+
]
82+
`);
83+
expect(tables).toHaveLength(1);
84+
expect(tables.map((t) => t.relname)).toMatchInlineSnapshot(`
85+
[
86+
"foo",
87+
]
88+
`);
89+
expect(enums).toHaveLength(1);
90+
expect(enums).toMatchInlineSnapshot(`
91+
[
92+
{
93+
"typname": "user_role",
94+
"value_count": "2",
95+
},
96+
]
97+
`);
98+
99+
mockFs({
100+
[`migrations/committed/000001.sql`]: MIGRATION_1_COMMITTED,
101+
[`migrations/committed/000002.sql`]: MIGRATION_ENUM_COMMITTED,
102+
[`migrations/committed/000003.sql`]: MIGRATION_NOTRX_COMMITTED,
103+
"migrations/current.sql": "",
104+
});
105+
106+
await current(settings);
107+
108+
const {
109+
migrations: newMigrations,
110+
tables: newTables,
111+
enums: newEnums,
112+
} = await getStuff(parsedSettings);
113+
114+
expect(newMigrations).toHaveLength(3);
115+
expect(newMigrations.map(({ date, ...rest }) => rest)).toMatchInlineSnapshot(`
116+
[
117+
{
118+
"filename": "000001.sql",
119+
"hash": "sha1:e00ec93314a423ee5cc68d1182ad52f16442d7df",
120+
"previous_hash": null,
121+
},
122+
{
123+
"filename": "000002.sql",
124+
"hash": "sha1:bddc1ead3310dc1c42cdc7f63537ebdff2e9fd7b",
125+
"previous_hash": "sha1:e00ec93314a423ee5cc68d1182ad52f16442d7df",
126+
},
127+
{
128+
"filename": "000003.sql",
129+
"hash": "sha1:2d248344ac299ebbad2aeba5bfec2ae3c3cb0a4f",
130+
"previous_hash": "sha1:bddc1ead3310dc1c42cdc7f63537ebdff2e9fd7b",
131+
},
132+
]
133+
`);
134+
expect(newTables).toEqual(tables);
135+
expect(newEnums).toEqual(enums);
136+
});

scripts/usage

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ echo -e '## graphile-migrate watch\n\n```'
2121
$GRAPHILE_MIGRATE watch --help
2222
echo -e '```\n\n'
2323

24+
echo -e '## graphile-migrate current\n\n```'
25+
$GRAPHILE_MIGRATE current --help
26+
echo -e '```\n\n'
27+
2428
echo -e '## graphile-migrate commit\n\n```'
2529
$GRAPHILE_MIGRATE commit --help
2630
echo -e '```\n\n'

src/cli.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import * as yargs from "yargs";
33

44
import { commitCommand } from "./commands/commit";
55
import { compileCommand } from "./commands/compile";
6+
import { currentCommand } from "./commands/current";
67
import { initCommand } from "./commands/init";
78
import { migrateCommand } from "./commands/migrate";
89
import { resetCommand } from "./commands/reset";
@@ -67,6 +68,7 @@ const f = yargs
6768
.command(wrapHandler(initCommand))
6869
.command(wrapHandler(migrateCommand))
6970
.command(wrapHandler(watchCommand))
71+
.command(wrapHandler(currentCommand))
7072
.command(wrapHandler(commitCommand))
7173
.command(wrapHandler(uncommitCommand))
7274
.command(wrapHandler(statusCommand))

src/commands/current.ts

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import { CommandModule } from "yargs";
2+
3+
import { getCurrentMigrationLocation, writeCurrentMigration } from "../current";
4+
import { makeCurrentMigrationRunner } from "../currentRunner";
5+
import { parseSettings, Settings } from "../settings";
6+
import type { CommonArgv } from "./_common";
7+
import { getSettings } from "./_common";
8+
import { _migrate } from "./migrate";
9+
10+
interface CurrentArgv extends CommonArgv {
11+
shadow?: boolean;
12+
}
13+
14+
export async function current(
15+
settings: Settings,
16+
options: Partial<CurrentArgv> = {},
17+
): Promise<void> {
18+
const { shadow = false } = options;
19+
const parsedSettings = await parseSettings(settings, shadow);
20+
await _migrate(parsedSettings, shadow);
21+
22+
const currentLocation = await getCurrentMigrationLocation(parsedSettings);
23+
if (!currentLocation.exists) {
24+
await writeCurrentMigration(
25+
parsedSettings,
26+
currentLocation,
27+
parsedSettings.blankMigrationContent.trim() + "\n",
28+
);
29+
}
30+
31+
const run = makeCurrentMigrationRunner(parsedSettings, {
32+
once: true,
33+
shadow,
34+
});
35+
return run();
36+
}
37+
38+
export const currentCommand: CommandModule<
39+
Record<string, never>,
40+
CurrentArgv
41+
> = {
42+
command: "current",
43+
aliases: [],
44+
describe:
45+
"Runs any un-executed committed migrations, as well as the current migration. For development.",
46+
builder: {
47+
shadow: {
48+
type: "boolean",
49+
default: false,
50+
description: "Apply migrations to the shadow DB (for development).",
51+
},
52+
},
53+
handler: async (argv) => {
54+
const settings = await getSettings({ configFile: argv.config });
55+
await current(settings, argv);
56+
},
57+
};

src/commands/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
export { commit } from "./commit";
22
export { compile } from "./compile";
3+
export { current } from "./current";
34
export { init } from "./init";
45
export { migrate } from "./migrate";
56
export { reset } from "./reset";

src/commands/run.ts

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -24,15 +24,7 @@ export async function run<T extends QueryResultRow = QueryResultRow>(
2424
settings: Settings,
2525
rawContent: string,
2626
filename: string,
27-
{
28-
shadow = false,
29-
root = false,
30-
rootDatabase = false,
31-
}: {
32-
shadow?: boolean;
33-
root?: boolean;
34-
rootDatabase?: boolean;
35-
} = {},
27+
{ shadow = false, root = false, rootDatabase = false }: RunArgv = {},
3628
): Promise<T[] | undefined> {
3729
const parsedSettings = await parseSettings(settings, shadow);
3830
const content = await compileIncludes(

src/commands/watch.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,8 @@ export const watchCommand: CommandModule<Record<string, never>, WatchArgv> = {
110110
once: {
111111
type: "boolean",
112112
default: false,
113-
description: "Runs the current migration and then exits.",
113+
description:
114+
"Runs the current migration and then exits (equivalent to `graphile-migrate current`).",
114115
},
115116
shadow: {
116117
type: "boolean",

0 commit comments

Comments
 (0)