Skip to content

Commit b9b85ec

Browse files
authored
Compile and run will both replace includes now. (#248)
2 parents a2c4f7f + e3cf6e9 commit b9b85ec

5 files changed

Lines changed: 102 additions & 51 deletions

File tree

README.md

Lines changed: 20 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -217,22 +217,23 @@ Commands:
217217
graphile-migrate reset Drops and re-creates the database, re-running
218218
all committed migrations from the start.
219219
**HIGHLY DESTRUCTIVE**.
220-
graphile-migrate compile [file] Compiles a SQL file, inserting all the
221-
placeholders and returning the result to
222-
STDOUT
223-
graphile-migrate run [file] Compiles a SQL file, inserting all the
224-
placeholders, and then runs it against the
225-
database. Useful for seeding. If called from
226-
an action will automatically run against the
227-
same database (via GM_DBURL envvar) unless
228-
--shadow or --rootDatabase are supplied.
220+
graphile-migrate compile [file] Compiles a SQL file (resolving `--!includes`,
221+
replacing :PLACEHOLDERs, etc) and outputs the
222+
result to STDOUT
223+
graphile-migrate run [file] Compiles a SQL file (resolving `--!includes`,
224+
replacing :PLACEHOLDERs, etc) and then runs
225+
it against the database. Useful for seeding.
226+
If called from an action will automatically
227+
run against the same database (via GM_DBURL
228+
envvar) unless --shadow or --rootDatabase are
229+
supplied.
229230
graphile-migrate completion Generate shell completion script.
230231
231232
Options:
232233
--help Show help [boolean]
233234
-c, --config Optional path to gmrc file [string] [default: .gmrc[.js|.cjs]]
234235
235-
You are running graphile-migrate v2.0.0-rc.1.
236+
You are running graphile-migrate v2.0.0-rc.2.
236237
```
237238

238239

@@ -373,8 +374,8 @@ Options:
373374
```
374375
graphile-migrate compile [file]
375376
376-
Compiles a SQL file, inserting all the placeholders and returning the result to
377-
STDOUT
377+
Compiles a SQL file (resolving `--!includes`, replacing :PLACEHOLDERs, etc) and
378+
outputs the result to STDOUT
378379
379380
Options:
380381
--help Show help [boolean]
@@ -389,10 +390,10 @@ Options:
389390
```
390391
graphile-migrate run [file]
391392
392-
Compiles a SQL file, inserting all the placeholders, and then runs it against
393-
the database. Useful for seeding. If called from an action will automatically
394-
run against the same database (via GM_DBURL envvar) unless --shadow or
395-
--rootDatabase are supplied.
393+
Compiles a SQL file (resolving `--!includes`, replacing :PLACEHOLDERs, etc) and
394+
then runs it against the database. Useful for seeding. If called from an action
395+
will automatically run against the same database (via GM_DBURL envvar) unless
396+
--shadow or --rootDatabase are supplied.
396397
397398
Options:
398399
--help Show help [boolean]
@@ -767,8 +768,9 @@ drop policy if exists access_by_numbers on mytable;
767768
create policy access_by_numbers on mytable for update using (myfunction(4, 2) < 42);
768769
```
769770
770-
and when the migration is committed or watched, the contents of `myfunction.sql`
771-
will be included in the result, such that the following SQL is executed:
771+
and when the migration is committed, watched, run, or compiled, the contents of
772+
`myfunction.sql` will be included in the result, such that the following SQL is
773+
executed:
772774
773775
```sql
774776
create or replace function myfunction(a int, b int)

__tests__/compile.test.ts

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,15 @@
11
import "./helpers";
22

3-
import * as mockFs from "mock-fs";
3+
import mockFs from "mock-fs";
44

55
import { compile } from "../src";
66
let old: string | undefined;
7+
const settings = {
8+
connectionString: "postgres://dbowner:dbpassword@dbhost:1221/dbname",
9+
placeholders: {
10+
":DATABASE_AUTHENTICATOR": "!ENV",
11+
},
12+
};
713
beforeAll(() => {
814
old = process.env.DATABASE_AUTHENTICATOR;
915
process.env.DATABASE_AUTHENTICATOR = "dbauth";
@@ -19,12 +25,7 @@ afterEach(() => {
1925
it("compiles SQL with settings", async () => {
2026
expect(
2127
await compile(
22-
{
23-
connectionString: "postgres://dbowner:dbpassword@dbhost:1221/dbname",
24-
placeholders: {
25-
":DATABASE_AUTHENTICATOR": "!ENV",
26-
},
27-
},
28+
settings,
2829
`\
2930
BEGIN;
3031
GRANT CONNECT ON DATABASE :DATABASE_NAME TO :DATABASE_OWNER;
@@ -53,3 +54,24 @@ CREATE EXTENSION IF NOT EXISTS pgcrypto WITH SCHEMA public;
5354
COMMIT;
5455
`);
5556
});
57+
58+
it("will compile included files", async () => {
59+
mockFs({
60+
"migrations/fixtures/foo.sql": "select * from foo;",
61+
});
62+
expect(
63+
await compile(
64+
settings,
65+
`\
66+
select 1;
67+
--!include foo.sql
68+
select 2;
69+
`,
70+
{ filename: `${process.cwd()}/migrations/current.sql` },
71+
),
72+
).toEqual(`\
73+
select 1;
74+
select * from foo;
75+
select 2;
76+
`);
77+
});

src/commands/_common.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,23 @@ export async function getSettings(options: Options = {}): Promise<Settings> {
119119
}
120120
}
121121

122+
export async function readFileOrStdin(file: unknown): Promise<{
123+
filename: string;
124+
content: string;
125+
}> {
126+
if (file != null) {
127+
if (typeof file === "string") {
128+
const filename = resolve(file);
129+
const content = await fsp.readFile(filename, "utf8");
130+
return { filename, content };
131+
} else {
132+
throw new Error(`Unexpected value for "file" flag`);
133+
}
134+
} else {
135+
return { filename: "stdin", content: await readStdin() };
136+
}
137+
}
138+
122139
export function readStdin(): Promise<string> {
123140
return new Promise((resolve, reject) => {
124141
let data = "";

src/commands/compile.ts

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,34 @@
1-
import * as fsp from "fs/promises";
21
import { CommandModule } from "yargs";
32

4-
import { compilePlaceholders } from "../migration";
3+
import { compileIncludes, compilePlaceholders } from "../migration";
54
import { parseSettings, Settings } from "../settings";
6-
import { CommonArgv, getSettings, readStdin } from "./_common";
5+
import { CommonArgv, getSettings, readFileOrStdin } from "./_common";
76

87
interface CompileArgv extends CommonArgv {
98
shadow?: boolean;
109
}
1110

11+
interface CompileOptions {
12+
shadow?: boolean;
13+
filename?: string;
14+
}
15+
16+
function resolveOptions(options: CompileOptions | boolean) {
17+
return typeof options === "boolean" ? { shadow: options } : options;
18+
}
19+
1220
export async function compile(
1321
settings: Settings,
14-
content: string,
15-
shadow = false,
22+
rawContent: string,
23+
options: boolean | CompileOptions = false,
1624
): Promise<string> {
25+
const { shadow = false, filename = "unknown" } = resolveOptions(options);
1726
const parsedSettings = await parseSettings(settings, shadow);
27+
const content = await compileIncludes(
28+
parsedSettings,
29+
rawContent,
30+
new Set([filename]),
31+
);
1832
return compilePlaceholders(parsedSettings, content, shadow);
1933
}
2034

@@ -25,7 +39,7 @@ export const compileCommand: CommandModule<
2539
command: "compile [file]",
2640
aliases: [],
2741
describe: `\
28-
Compiles a SQL file, inserting all the placeholders and returning the result to STDOUT`,
42+
Compiles a SQL file (resolving \`--!includes\`, replacing :PLACEHOLDERs, etc) and outputs the result to STDOUT`,
2943
builder: {
3044
shadow: {
3145
type: "boolean",
@@ -35,13 +49,11 @@ Compiles a SQL file, inserting all the placeholders and returning the result to
3549
},
3650
handler: async (argv) => {
3751
const settings = await getSettings({ configFile: argv.config });
38-
const content =
39-
typeof argv.file === "string"
40-
? await fsp.readFile(argv.file, "utf8")
41-
: await readStdin();
42-
43-
const compiled = await compile(settings, content, argv.shadow);
44-
52+
const { content, filename } = await readFileOrStdin(argv.file);
53+
const compiled = await compile(settings, content, {
54+
shadow: argv.shadow,
55+
filename,
56+
});
4557
// eslint-disable-next-line no-console
4658
console.log(compiled);
4759
},

src/commands/run.ts

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
1-
import * as fsp from "fs/promises";
21
import { QueryResultRow } from "pg";
32
import { CommandModule } from "yargs";
43

54
import { DO_NOT_USE_DATABASE_URL } from "../actions";
65
import { runQueryWithErrorInstrumentation } from "../instrumentation";
7-
import { compilePlaceholders } from "../migration";
6+
import { compileIncludes, compilePlaceholders } from "../migration";
87
import { withClient } from "../pgReal";
98
import {
109
makeRootDatabaseConnectionString,
1110
parseSettings,
1211
Settings,
1312
} from "../settings";
14-
import { CommonArgv, getDatabaseName, getSettings, readStdin } from "./_common";
13+
import type { CommonArgv } from "./_common";
14+
import { getDatabaseName, getSettings, readFileOrStdin } from "./_common";
1515

1616
interface RunArgv extends CommonArgv {
1717
shadow?: boolean;
@@ -22,7 +22,7 @@ interface RunArgv extends CommonArgv {
2222
// eslint-disable-next-line @typescript-eslint/no-explicit-any
2323
export async function run<T extends QueryResultRow = QueryResultRow>(
2424
settings: Settings,
25-
content: string,
25+
rawContent: string,
2626
filename: string,
2727
{
2828
shadow = false,
@@ -35,6 +35,11 @@ export async function run<T extends QueryResultRow = QueryResultRow>(
3535
} = {},
3636
): Promise<T[] | undefined> {
3737
const parsedSettings = await parseSettings(settings, shadow);
38+
const content = await compileIncludes(
39+
parsedSettings,
40+
rawContent,
41+
new Set([filename]),
42+
);
3843
const sql = compilePlaceholders(parsedSettings, content, shadow);
3944
const baseConnectionString = rootDatabase
4045
? parsedSettings.rootConnectionString
@@ -62,7 +67,7 @@ export const runCommand: CommandModule<Record<string, never>, RunArgv> = {
6267
command: "run [file]",
6368
aliases: [],
6469
describe: `\
65-
Compiles a SQL file, inserting all the placeholders, and then runs it against the database. Useful for seeding. If called from an action will automatically run against the same database (via GM_DBURL envvar) unless --shadow or --rootDatabase are supplied.`,
70+
Compiles a SQL file (resolving \`--!includes\`, replacing :PLACEHOLDERs, etc) and then runs it against the database. Useful for seeding. If called from an action will automatically run against the same database (via GM_DBURL envvar) unless --shadow or --rootDatabase are supplied.`,
6671
builder: {
6772
shadow: {
6873
type: "boolean",
@@ -102,14 +107,7 @@ Compiles a SQL file, inserting all the placeholders, and then runs it against th
102107
connectionString: process.env.GM_DBURL,
103108
};
104109

105-
const { content, filename } =
106-
typeof argv.file === "string"
107-
? {
108-
filename: argv.file,
109-
content: await fsp.readFile(argv.file, "utf8"),
110-
}
111-
: { filename: "stdin", content: await readStdin() };
112-
110+
const { content, filename } = await readFileOrStdin(argv.file);
113111
const rows = await run(settings, content, filename, argv);
114112

115113
if (rows) {

0 commit comments

Comments
 (0)