Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add --forceActions option to watch #148

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
Open
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
21 changes: 21 additions & 0 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 and
then runs 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 @@ -287,6 +290,24 @@ Options:
```


## graphile-migrate current

```
graphile-migrate current

Runs any un-executed committed migrations and then runs the current migration.
For development.

Options:
--help Show help [boolean]
--config, -c Optional path to gmrc file string] [default: .gmrc[.js]]
--shadow Applies changes to shadow DB. [boolean] [default: false]
--forceActions Run beforeAllMigrations, afterAllMigrations, beforeCurrent,
and afterCurrent actions even if no migration was necessary.
[boolean] [default: false]
```


## graphile-migrate commit

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

import * as 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,
"migrations/current.sql": MIGRATION_NOTRX_TEXT,
});

await current(settings);

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

expect(migrations).toHaveLength(2);
expect(migrations.map(({ date, ...rest }) => rest)).toMatchInlineSnapshot(`
Array [
Object {
"filename": "000001.sql",
"hash": "sha1:e00ec93314a423ee5cc68d1182ad52f16442d7df",
"previous_hash": null,
},
Object {
"filename": "000002.sql",
"hash": "sha1:bddc1ead3310dc1c42cdc7f63537ebdff2e9fd7b",
"previous_hash": "sha1:e00ec93314a423ee5cc68d1182ad52f16442d7df",
},
]
`);
expect(tables).toHaveLength(1);
expect(tables.map(t => t.relname)).toMatchInlineSnapshot(`
Array [
"foo",
]
`);
expect(enums).toHaveLength(1);
expect(enums).toMatchInlineSnapshot(`
Array [
Object {
"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, tables, enums } = await getStuff(parsedSettings);

expect(migrations).toHaveLength(3);
expect(migrations.map(({ date, ...rest }) => rest)).toMatchInlineSnapshot(`
Array [
Object {
"filename": "000001.sql",
"hash": "sha1:e00ec93314a423ee5cc68d1182ad52f16442d7df",
"previous_hash": null,
},
Object {
"filename": "000002.sql",
"hash": "sha1:bddc1ead3310dc1c42cdc7f63537ebdff2e9fd7b",
"previous_hash": "sha1:e00ec93314a423ee5cc68d1182ad52f16442d7df",
},
Object {
"filename": "000003.sql",
"hash": "sha1:2d248344ac299ebbad2aeba5bfec2ae3c3cb0a4f",
"previous_hash": "sha1:bddc1ead3310dc1c42cdc7f63537ebdff2e9fd7b",
},
]
`);
expect(tables).toHaveLength(1);
expect(tables.map(t => t.relname)).toMatchInlineSnapshot(`
Array [
"foo",
]
`);
expect(enums).toHaveLength(1);
expect(enums).toMatchInlineSnapshot(`
Array [
Object {
"typname": "user_role",
"value_count": "2",
},
]
`);
}
});
2 changes: 2 additions & 0 deletions __tests__/watch.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ it("doesn't run current.sql if it's already up to date", async () => {
parsedSettings,
false,
false,
false,
);

expect(getActionCalls()).toEqual([]);
Expand Down Expand Up @@ -81,6 +82,7 @@ it("watches symlinked files", async () => {
parsedSettings,
false,
false,
false,
);

expect(getActionCalls()).toEqual([]);
Expand Down
2 changes: 2 additions & 0 deletions src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import * as yargs from "yargs";
import { version } from "../package.json";
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 @@ -76,6 +77,7 @@ yargs
.command(wrapHandler(statusCommand))
.command(wrapHandler(resetCommand))
.command(wrapHandler(compileCommand))
.command(wrapHandler(currentCommand))
.command(wrapHandler(runCommand))

// Make sure options added here are represented in CommonArgv
Expand Down
73 changes: 73 additions & 0 deletions src/commands/current.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { CommandModule } from "yargs";

import { getCurrentMigrationLocation, writeCurrentMigration } from "../current";
import { ParsedSettings, parseSettings, Settings } from "../settings";
import { CommonArgv, getSettings } from "./_common";
import { _migrate } from "./migrate";
import { _makeCurrentMigrationRunner } from "./watch";

interface CurrentArgv extends CommonArgv {
shadow: boolean;
forceActions: boolean;
}

export async function _current(
parsedSettings: ParsedSettings,
shadow = false,
forceActions = false,
): Promise<void> {
await _migrate(parsedSettings, shadow);

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

const run = _makeCurrentMigrationRunner(
parsedSettings,
false,
shadow,
forceActions,
);
return run();
}

export async function current(
settings: Settings,
shadow = false,
forceActions = false,
): Promise<void> {
const parsedSettings = await parseSettings(settings, shadow);
return _current(parsedSettings, shadow, forceActions);
}

export const currentCommand: CommandModule<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).",
},
forceActions: {
type: "boolean",
default: false,
description:
"Run beforeAllMigrations and afterAllMigrations actions even if no migration was necessary.",
},
},
handler: async argv => {
await current(
await getSettings({ configFile: argv.config }),
argv.shadow,
argv.forceActions,
);
},
};
1 change: 1 addition & 0 deletions src/commands/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ export { status } from "./status";
export { reset } from "./reset";
export { compile } from "./compile";
export { run } from "./run";
export { current } from "./current";
5 changes: 3 additions & 2 deletions src/commands/watch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export function _makeCurrentMigrationRunner(
parsedSettings: ParsedSettings,
_once = false,
shadow = false,
forceActions = false,
): () => Promise<void> {
async function run(): Promise<void> {
const currentLocation = await getCurrentMigrationLocation(parsedSettings);
Expand Down Expand Up @@ -85,7 +86,7 @@ export function _makeCurrentMigrationRunner(
currentBodyMinified === previousBodyMinified;

// 4: if different
if (!migrationsAreEquivalent) {
if (forceActions || !migrationsAreEquivalent) {
await executeActions(
parsedSettings,
shadow,
Expand Down Expand Up @@ -185,7 +186,7 @@ export async function _watch(
);
}

const run = _makeCurrentMigrationRunner(parsedSettings, once, shadow);
const run = _makeCurrentMigrationRunner(parsedSettings, once, shadow, false);
if (once) {
return run();
} else {
Expand Down