From fd71a2185d7bb15dd24c40324234bceb450841d3 Mon Sep 17 00:00:00 2001 From: Max Korp Date: Tue, 4 Jan 2022 18:06:17 -0700 Subject: [PATCH 1/9] feat: add --forceActions option to watch --- src/commands/watch.ts | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/src/commands/watch.ts b/src/commands/watch.ts index ba52642..b28a5ab 100644 --- a/src/commands/watch.ts +++ b/src/commands/watch.ts @@ -19,12 +19,14 @@ import { CommonArgv, getSettings } from "./_common"; interface WatchArgv extends CommonArgv { once: boolean; shadow: boolean; + forceActions: boolean; } export function _makeCurrentMigrationRunner( parsedSettings: ParsedSettings, _once = false, shadow = false, + forceActions = false, ): () => Promise { async function run(): Promise { const currentLocation = await getCurrentMigrationLocation(parsedSettings); @@ -85,7 +87,7 @@ export function _makeCurrentMigrationRunner( currentBodyMinified === previousBodyMinified; // 4: if different - if (!migrationsAreEquivalent) { + if (forceActions || !migrationsAreEquivalent) { await executeActions( parsedSettings, shadow, @@ -173,6 +175,7 @@ export async function _watch( parsedSettings: ParsedSettings, once = false, shadow = false, + forceActions = false, ): Promise { await _migrate(parsedSettings, shadow); @@ -185,7 +188,12 @@ export async function _watch( ); } - const run = _makeCurrentMigrationRunner(parsedSettings, once, shadow); + const run = _makeCurrentMigrationRunner( + parsedSettings, + once, + shadow, + forceActions, + ); if (once) { return run(); } else { @@ -249,9 +257,10 @@ export async function watch( settings: Settings, once = false, shadow = false, + forceActions = false, ): Promise { const parsedSettings = await parseSettings(settings, shadow); - return _watch(parsedSettings, once, shadow); + return _watch(parsedSettings, once, shadow, forceActions); } export const watchCommand: CommandModule = { @@ -270,6 +279,12 @@ export const watchCommand: CommandModule = { default: false, description: "Applies changes to shadow DB.", }, + forceActions: { + type: "boolean", + default: false, + description: + "Run beforeAllMigrations and afterAllMigrations actions even if no migration was necessary.", + }, }, handler: async argv => { await watch( From 3c048844124ce27309a90294376e8790d7ba13af Mon Sep 17 00:00:00 2001 From: Max Korp Date: Tue, 4 Jan 2022 18:06:17 -0700 Subject: [PATCH 2/9] Revert "feat: add --forceActions option to watch" This reverts commit fd71a2185d7bb15dd24c40324234bceb450841d3. --- src/commands/watch.ts | 21 +++------------------ 1 file changed, 3 insertions(+), 18 deletions(-) diff --git a/src/commands/watch.ts b/src/commands/watch.ts index b28a5ab..ba52642 100644 --- a/src/commands/watch.ts +++ b/src/commands/watch.ts @@ -19,14 +19,12 @@ import { CommonArgv, getSettings } from "./_common"; interface WatchArgv extends CommonArgv { once: boolean; shadow: boolean; - forceActions: boolean; } export function _makeCurrentMigrationRunner( parsedSettings: ParsedSettings, _once = false, shadow = false, - forceActions = false, ): () => Promise { async function run(): Promise { const currentLocation = await getCurrentMigrationLocation(parsedSettings); @@ -87,7 +85,7 @@ export function _makeCurrentMigrationRunner( currentBodyMinified === previousBodyMinified; // 4: if different - if (forceActions || !migrationsAreEquivalent) { + if (!migrationsAreEquivalent) { await executeActions( parsedSettings, shadow, @@ -175,7 +173,6 @@ export async function _watch( parsedSettings: ParsedSettings, once = false, shadow = false, - forceActions = false, ): Promise { await _migrate(parsedSettings, shadow); @@ -188,12 +185,7 @@ export async function _watch( ); } - const run = _makeCurrentMigrationRunner( - parsedSettings, - once, - shadow, - forceActions, - ); + const run = _makeCurrentMigrationRunner(parsedSettings, once, shadow); if (once) { return run(); } else { @@ -257,10 +249,9 @@ export async function watch( settings: Settings, once = false, shadow = false, - forceActions = false, ): Promise { const parsedSettings = await parseSettings(settings, shadow); - return _watch(parsedSettings, once, shadow, forceActions); + return _watch(parsedSettings, once, shadow); } export const watchCommand: CommandModule = { @@ -279,12 +270,6 @@ export const watchCommand: CommandModule = { default: false, description: "Applies changes to shadow DB.", }, - forceActions: { - type: "boolean", - default: false, - description: - "Run beforeAllMigrations and afterAllMigrations actions even if no migration was necessary.", - }, }, handler: async argv => { await watch( From b683b82a1f41f32ed0ec55fd4d1321c6d0bc4016 Mon Sep 17 00:00:00 2001 From: Max Korp Date: Mon, 7 Feb 2022 11:44:48 -0700 Subject: [PATCH 3/9] feat: add forceActions back to underlying watch function for use externally --- __tests__/watch.test.ts | 2 ++ src/commands/watch.ts | 5 +++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/__tests__/watch.test.ts b/__tests__/watch.test.ts index df5f085..17973e1 100644 --- a/__tests__/watch.test.ts +++ b/__tests__/watch.test.ts @@ -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([]); @@ -81,6 +82,7 @@ it("watches symlinked files", async () => { parsedSettings, false, false, + false, ); expect(getActionCalls()).toEqual([]); diff --git a/src/commands/watch.ts b/src/commands/watch.ts index ba52642..1e2ca36 100644 --- a/src/commands/watch.ts +++ b/src/commands/watch.ts @@ -25,6 +25,7 @@ export function _makeCurrentMigrationRunner( parsedSettings: ParsedSettings, _once = false, shadow = false, + forceActions = false, ): () => Promise { async function run(): Promise { const currentLocation = await getCurrentMigrationLocation(parsedSettings); @@ -85,7 +86,7 @@ export function _makeCurrentMigrationRunner( currentBodyMinified === previousBodyMinified; // 4: if different - if (!migrationsAreEquivalent) { + if (forceActions || !migrationsAreEquivalent) { await executeActions( parsedSettings, shadow, @@ -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 { From e297767f4a23133b7ae1892817a5b7adfbb309ca Mon Sep 17 00:00:00 2001 From: Max Korp Date: Mon, 7 Feb 2022 11:44:55 -0700 Subject: [PATCH 4/9] feat: add new `current` command --- src/cli.ts | 2 + src/commands/current.ts | 86 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 88 insertions(+) create mode 100644 src/commands/current.ts diff --git a/src/cli.ts b/src/cli.ts index 6d8f975..98c50cd 100755 --- a/src/cli.ts +++ b/src/cli.ts @@ -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"; @@ -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 diff --git a/src/commands/current.ts b/src/commands/current.ts new file mode 100644 index 0000000..5a8cec1 --- /dev/null +++ b/src/commands/current.ts @@ -0,0 +1,86 @@ +import { CommandModule } from "yargs"; + +import { executeActions } from "../actions"; +import { + getLastMigration, + getMigrationsAfter, + runCommittedMigration, +} from "../migration"; +import { _migrate } from "./migrate"; +import { _makeCurrentMigrationRunner } from "./watch"; +import { + getCurrentMigrationLocation, + readCurrentMigration, + writeCurrentMigration, +} from "../current"; + +import { withClient } from "../pg"; +import { withAdvisoryLock } from "../pgReal"; +import { ParsedSettings, parseSettings, Settings } from "../settings"; +import { CommonArgv, getSettings } from "./_common"; + +interface CurrentArgv extends CommonArgv { + shadow: boolean; + forceActions: boolean; +} + +export async function _current( + parsedSettings: ParsedSettings, + shadow = false, + forceActions = false, +): Promise { + 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 { + const parsedSettings = await parseSettings(settings, shadow); + return _current(parsedSettings, shadow, forceActions); +} + +export const currentCommand: CommandModule = { + 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, + ); + }, +}; From 179357f2dc2c48790f112650ed205c1e68b83aaa Mon Sep 17 00:00:00 2001 From: Max Korp Date: Mon, 7 Feb 2022 11:54:19 -0700 Subject: [PATCH 5/9] feat: add test to new current command --- __tests__/current.test.ts | 145 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 145 insertions(+) create mode 100644 __tests__/current.test.ts diff --git a/__tests__/current.test.ts b/__tests__/current.test.ts new file mode 100644 index 0000000..aeed182 --- /dev/null +++ b/__tests__/current.test.ts @@ -0,0 +1,145 @@ +import "./helpers"; // Has side-effects; must come first + +import * as mockFs from "mock-fs"; + +import { migrate } 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_2_TEXT, + 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 () => { + 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 migrate(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", + }, + ] + `); + 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 migrate(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", + }, +] +`); + } +}); From 370ae01d6cad8bdf1f0ace7528b0386114968800 Mon Sep 17 00:00:00 2001 From: Max Korp Date: Mon, 7 Feb 2022 12:01:06 -0700 Subject: [PATCH 6/9] chore: fix lint --- __tests__/current.test.ts | 1 - src/commands/current.ts | 19 +++---------------- 2 files changed, 3 insertions(+), 17 deletions(-) diff --git a/__tests__/current.test.ts b/__tests__/current.test.ts index aeed182..5baa644 100644 --- a/__tests__/current.test.ts +++ b/__tests__/current.test.ts @@ -16,7 +16,6 @@ afterEach(() => { }); const { MIGRATION_1_COMMITTED, - MIGRATION_2_TEXT, MIGRATION_ENUM_COMMITTED, MIGRATION_NOTRX_TEXT, MIGRATION_NOTRX_COMMITTED, diff --git a/src/commands/current.ts b/src/commands/current.ts index 5a8cec1..0197842 100644 --- a/src/commands/current.ts +++ b/src/commands/current.ts @@ -1,23 +1,10 @@ import { CommandModule } from "yargs"; -import { executeActions } from "../actions"; -import { - getLastMigration, - getMigrationsAfter, - runCommittedMigration, -} from "../migration"; -import { _migrate } from "./migrate"; -import { _makeCurrentMigrationRunner } from "./watch"; -import { - getCurrentMigrationLocation, - readCurrentMigration, - writeCurrentMigration, -} from "../current"; - -import { withClient } from "../pg"; -import { withAdvisoryLock } from "../pgReal"; +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; From ea397598660a0579fe6aa52318b4308b1790300b Mon Sep 17 00:00:00 2001 From: Max Korp Date: Mon, 7 Feb 2022 12:27:04 -0700 Subject: [PATCH 7/9] fix test --- __tests__/current.test.ts | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/__tests__/current.test.ts b/__tests__/current.test.ts index 5baa644..7b5cd7f 100644 --- a/__tests__/current.test.ts +++ b/__tests__/current.test.ts @@ -2,7 +2,7 @@ import "./helpers"; // Has side-effects; must come first import * as mockFs from "mock-fs"; -import { migrate } from "../src"; +import { current } from "../src"; import { withClient } from "../src/pg"; import { ParsedSettings, parseSettings } from "../src/settings"; import { makeMigrations, resetDb, settings } from "./helpers"; @@ -41,6 +41,11 @@ function getStuff(parsedSettings: ParsedSettings) { } it("runs migrations", async () => { + mockFs({ + "migrations/current.sql": "", + }); + + await current(settings); const parsedSettings = await parseSettings(settings); { @@ -56,12 +61,12 @@ it("runs migrations", async () => { "migrations/current.sql": MIGRATION_NOTRX_TEXT, }); - await migrate(settings); + await current(settings); { const { migrations, tables, enums } = await getStuff(parsedSettings); - expect(migrations).toHaveLength(3); + expect(migrations).toHaveLength(2); expect(migrations.map(({ date, ...rest }) => rest)).toMatchInlineSnapshot(` Array [ Object { @@ -100,7 +105,7 @@ Array [ "migrations/current.sql": "", }); - await migrate(settings); + await current(settings); { const { migrations, tables, enums } = await getStuff(parsedSettings); From b31e83dc13dc681f26815992b149ddb523fcf22a Mon Sep 17 00:00:00 2001 From: Max Korp Date: Mon, 7 Feb 2022 12:27:22 -0700 Subject: [PATCH 8/9] fix missing import/export --- src/commands/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/commands/index.ts b/src/commands/index.ts index 725c5cd..8821ec6 100644 --- a/src/commands/index.ts +++ b/src/commands/index.ts @@ -7,3 +7,4 @@ export { status } from "./status"; export { reset } from "./reset"; export { compile } from "./compile"; export { run } from "./run"; +export { current } from "./current"; From 32943842ab0b5c79736f87ff07fcc762bc4b3821 Mon Sep 17 00:00:00 2001 From: Max Korp Date: Wed, 9 Feb 2022 14:17:30 -0700 Subject: [PATCH 9/9] feat: add `current` documentation to README --- README.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/README.md b/README.md index 7223a54..8904673 100644 --- a/README.md +++ b/README.md @@ -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. @@ -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 ```