Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
23 changes: 21 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,9 @@ Commands:
graphile-migrate watch Runs any un-executed committed migrations and
then runs and watches the current migration,
re-running it on any change. For development.
graphile-migrate current Runs any un-executed committed migrations, as
well as the current migration. For
development.
graphile-migrate commit Commits the current migration into the
`committed/` folder, resetting the current
migration. Resets the shadow database.
Expand Down Expand Up @@ -284,12 +287,28 @@ migration, re-running it on any change. For development.
Options:
--help Show help [boolean]
-c, --config Optional path to gmrc file [string] [default: .gmrc[.js|.cjs]]
--once Runs the current migration and then exits.
[boolean] [default: false]
--once Runs the current migration and then exits (equivalent to
`graphile-migrate current`). [boolean] [default: false]
--shadow Applies changes to shadow DB. [boolean] [default: false]
```


## graphile-migrate current

```
graphile-migrate current

Runs any un-executed committed migrations, as well as the current migration. For
development.

Options:
--help Show help [boolean]
-c, --config Optional path to gmrc file [string] [default: .gmrc[.js|.cjs]]
--shadow Apply migrations to the shadow DB (for development).
[boolean] [default: false]
```


## graphile-migrate commit

```
Expand Down
136 changes: 136 additions & 0 deletions __tests__/current.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
import "./helpers"; // Has side-effects; must come first

import mockFs from "mock-fs";

import { current } from "../src";
import { withClient } from "../src/pg";
import { ParsedSettings, parseSettings } from "../src/settings";
import { makeMigrations, resetDb, settings } from "./helpers";

beforeEach(resetDb);
beforeEach(async () => {
mockFs({ migrations: mockFs.directory() });
});
afterEach(() => {
mockFs.restore();
});
const {
MIGRATION_1_COMMITTED,
MIGRATION_ENUM_COMMITTED,
MIGRATION_NOTRX_TEXT,
MIGRATION_NOTRX_COMMITTED,
} = makeMigrations();

function getStuff(parsedSettings: ParsedSettings) {
return withClient(
parsedSettings.connectionString,
parsedSettings,
async (pgClient, _context) => {
const { rows: migrations } = await pgClient.query(
"select * from graphile_migrate.migrations",
);
const { rows: tables } = await pgClient.query(
"select * from pg_class where relnamespace = 'public'::regnamespace and relkind = 'r'",
);
const { rows: enums } = await pgClient.query(
"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'",
);
return { migrations, tables, enums };
},
);
}

it("runs migrations", async () => {
mockFs({
"migrations/current.sql": "",
});

await current(settings);
const parsedSettings = await parseSettings(settings);

{
const { migrations, tables, enums } = await getStuff(parsedSettings);
expect(migrations).toHaveLength(0);
expect(tables).toHaveLength(0);
expect(enums).toHaveLength(0);
}

mockFs({
[`migrations/committed/000001.sql`]: MIGRATION_1_COMMITTED,
[`migrations/committed/000002.sql`]: MIGRATION_ENUM_COMMITTED, // Creates enum with 1 value
"migrations/current.sql": MIGRATION_NOTRX_TEXT, // Adds a value to the enum - total = 2
});

await current(settings);

const { migrations, tables, enums } = await getStuff(parsedSettings);

expect(migrations).toHaveLength(2);
expect(migrations.map(({ date, ...rest }) => rest)).toMatchInlineSnapshot(`
[
{
"filename": "000001.sql",
"hash": "sha1:e00ec93314a423ee5cc68d1182ad52f16442d7df",
"previous_hash": null,
},
{
"filename": "000002.sql",
"hash": "sha1:bddc1ead3310dc1c42cdc7f63537ebdff2e9fd7b",
"previous_hash": "sha1:e00ec93314a423ee5cc68d1182ad52f16442d7df",
},
]
`);
expect(tables).toHaveLength(1);
expect(tables.map((t) => t.relname)).toMatchInlineSnapshot(`
[
"foo",
]
`);
expect(enums).toHaveLength(1);
expect(enums).toMatchInlineSnapshot(`
[
{
"typname": "user_role",
"value_count": "2",
},
]
`);

mockFs({
[`migrations/committed/000001.sql`]: MIGRATION_1_COMMITTED,
[`migrations/committed/000002.sql`]: MIGRATION_ENUM_COMMITTED,
[`migrations/committed/000003.sql`]: MIGRATION_NOTRX_COMMITTED,
"migrations/current.sql": "",
});

await current(settings);

const {
migrations: newMigrations,
tables: newTables,
enums: newEnums,
} = await getStuff(parsedSettings);

expect(newMigrations).toHaveLength(3);
expect(newMigrations.map(({ date, ...rest }) => rest)).toMatchInlineSnapshot(`
[
{
"filename": "000001.sql",
"hash": "sha1:e00ec93314a423ee5cc68d1182ad52f16442d7df",
"previous_hash": null,
},
{
"filename": "000002.sql",
"hash": "sha1:bddc1ead3310dc1c42cdc7f63537ebdff2e9fd7b",
"previous_hash": "sha1:e00ec93314a423ee5cc68d1182ad52f16442d7df",
},
{
"filename": "000003.sql",
"hash": "sha1:2d248344ac299ebbad2aeba5bfec2ae3c3cb0a4f",
"previous_hash": "sha1:bddc1ead3310dc1c42cdc7f63537ebdff2e9fd7b",
},
]
`);
expect(newTables).toEqual(tables);
expect(newEnums).toEqual(enums);
});
4 changes: 4 additions & 0 deletions scripts/usage
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ echo -e '## graphile-migrate watch\n\n```'
$GRAPHILE_MIGRATE watch --help
echo -e '```\n\n'

echo -e '## graphile-migrate current\n\n```'
$GRAPHILE_MIGRATE current --help
echo -e '```\n\n'

echo -e '## graphile-migrate commit\n\n```'
$GRAPHILE_MIGRATE commit --help
echo -e '```\n\n'
Expand Down
2 changes: 2 additions & 0 deletions src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import * as yargs from "yargs";

import { commitCommand } from "./commands/commit";
import { compileCommand } from "./commands/compile";
import { currentCommand } from "./commands/current";
import { initCommand } from "./commands/init";
import { migrateCommand } from "./commands/migrate";
import { resetCommand } from "./commands/reset";
Expand Down Expand Up @@ -67,6 +68,7 @@ const f = yargs
.command(wrapHandler(initCommand))
.command(wrapHandler(migrateCommand))
.command(wrapHandler(watchCommand))
.command(wrapHandler(currentCommand))
.command(wrapHandler(commitCommand))
.command(wrapHandler(uncommitCommand))
.command(wrapHandler(statusCommand))
Expand Down
57 changes: 57 additions & 0 deletions src/commands/current.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { CommandModule } from "yargs";

import { getCurrentMigrationLocation, writeCurrentMigration } from "../current";
import { makeCurrentMigrationRunner } from "../currentRunner";
import { parseSettings, Settings } from "../settings";
import type { CommonArgv } from "./_common";
import { getSettings } from "./_common";
import { _migrate } from "./migrate";

interface CurrentArgv extends CommonArgv {
shadow?: boolean;
}

export async function current(
settings: Settings,
options: Partial<CurrentArgv> = {},
): Promise<void> {
const { shadow = false } = options;
const parsedSettings = await parseSettings(settings, shadow);
await _migrate(parsedSettings, shadow);

const currentLocation = await getCurrentMigrationLocation(parsedSettings);
if (!currentLocation.exists) {
await writeCurrentMigration(
parsedSettings,
currentLocation,
parsedSettings.blankMigrationContent.trim() + "\n",
);
}

const run = makeCurrentMigrationRunner(parsedSettings, {
once: true,
shadow,
});
return run();
}

export const currentCommand: CommandModule<
Record<string, never>,
CurrentArgv
> = {
command: "current",
aliases: [],
describe:
"Runs any un-executed committed migrations, as well as the current migration. For development.",
builder: {
shadow: {
type: "boolean",
default: false,
description: "Apply migrations to the shadow DB (for development).",
},
},
handler: async (argv) => {
const settings = await getSettings({ configFile: argv.config });
await current(settings, argv);
},
};
1 change: 1 addition & 0 deletions src/commands/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export { commit } from "./commit";
export { compile } from "./compile";
export { current } from "./current";
export { init } from "./init";
export { migrate } from "./migrate";
export { reset } from "./reset";
Expand Down
10 changes: 1 addition & 9 deletions src/commands/run.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,7 @@ export async function run<T extends QueryResultRow = QueryResultRow>(
settings: Settings,
rawContent: string,
filename: string,
{
shadow = false,
root = false,
rootDatabase = false,
}: {
shadow?: boolean;
root?: boolean;
rootDatabase?: boolean;
} = {},
{ shadow = false, root = false, rootDatabase = false }: RunArgv = {},
): Promise<T[] | undefined> {
const parsedSettings = await parseSettings(settings, shadow);
const content = await compileIncludes(
Expand Down
3 changes: 2 additions & 1 deletion src/commands/watch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,8 @@ export const watchCommand: CommandModule<Record<string, never>, WatchArgv> = {
once: {
type: "boolean",
default: false,
description: "Runs the current migration and then exits.",
description:
"Runs the current migration and then exits (equivalent to `graphile-migrate current`).",
},
shadow: {
type: "boolean",
Expand Down
Loading