netlify-cli 24.9.0 → 24.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands/database/connect.d.ts +7 -0
- package/dist/commands/database/connect.d.ts.map +1 -0
- package/dist/commands/database/connect.js +130 -0
- package/dist/commands/database/connect.js.map +1 -0
- package/dist/commands/database/database.d.ts.map +1 -1
- package/dist/commands/database/database.js +76 -45
- package/dist/commands/database/database.js.map +1 -1
- package/dist/commands/database/db-connection.d.ts +7 -0
- package/dist/commands/database/db-connection.d.ts.map +1 -1
- package/dist/commands/database/db-connection.js +32 -8
- package/dist/commands/database/db-connection.js.map +1 -1
- package/dist/commands/database/meta-commands.d.ts +18 -0
- package/dist/commands/database/meta-commands.d.ts.map +1 -0
- package/dist/commands/database/meta-commands.js +51 -0
- package/dist/commands/database/meta-commands.js.map +1 -0
- package/dist/commands/database/migration-pull.d.ts +8 -0
- package/dist/commands/database/migration-pull.d.ts.map +1 -0
- package/dist/commands/database/migration-pull.js +107 -0
- package/dist/commands/database/migration-pull.js.map +1 -0
- package/dist/commands/database/psql-formatter.d.ts +3 -0
- package/dist/commands/database/psql-formatter.d.ts.map +1 -0
- package/dist/commands/database/psql-formatter.js +47 -0
- package/dist/commands/database/psql-formatter.js.map +1 -0
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"connect.d.ts","sourceRoot":"","sources":["../../../src/commands/database/connect.ts"],"names":[],"mappings":"AAGA,OAAO,WAAW,MAAM,oBAAoB,CAAA;AAK5C,MAAM,WAAW,cAAc;IAC7B,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,IAAI,CAAC,EAAE,OAAO,CAAA;CACf;AAaD,eAAO,MAAM,OAAO,GAAU,SAAS,cAAc,EAAE,SAAS,WAAW,KAAG,OAAO,CAAC,IAAI,CAuHzF,CAAA"}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import readline from 'readline';
|
|
2
|
+
import { log, logJson } from '../../utils/command-helpers.js';
|
|
3
|
+
import { connectRawClient } from './db-connection.js';
|
|
4
|
+
import { executeMetaCommand } from './meta-commands.js';
|
|
5
|
+
import { formatQueryResult } from './psql-formatter.js';
|
|
6
|
+
function redactConnectionString(connectionString) {
|
|
7
|
+
try {
|
|
8
|
+
const url = new URL(connectionString);
|
|
9
|
+
url.username = '';
|
|
10
|
+
url.password = '';
|
|
11
|
+
return url.toString();
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
return 'database';
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
export const connect = async (options, command) => {
|
|
18
|
+
const buildDir = command.netlify.site.root ?? command.project.root ?? command.project.baseDirectory;
|
|
19
|
+
if (!buildDir) {
|
|
20
|
+
throw new Error('Could not determine the project root directory.');
|
|
21
|
+
}
|
|
22
|
+
const { client, connectionString, cleanup } = await connectRawClient(buildDir);
|
|
23
|
+
// --json without --query: print connection details
|
|
24
|
+
if (options.json && !options.query) {
|
|
25
|
+
logJson({ connection_string: connectionString, context: 'dev' });
|
|
26
|
+
await cleanup();
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
log(`Connected to ${redactConnectionString(connectionString)}`);
|
|
30
|
+
// --query: one-shot mode
|
|
31
|
+
if (options.query) {
|
|
32
|
+
try {
|
|
33
|
+
const result = await client.query(options.query);
|
|
34
|
+
if (options.json) {
|
|
35
|
+
logJson(result.rows);
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
log(formatQueryResult(result.fields, result.rows, result.rowCount, result.command));
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
finally {
|
|
42
|
+
await cleanup();
|
|
43
|
+
}
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
// Interactive REPL
|
|
47
|
+
const rl = readline.createInterface({
|
|
48
|
+
input: process.stdin,
|
|
49
|
+
output: process.stdout,
|
|
50
|
+
prompt: 'netlifydb=> ',
|
|
51
|
+
});
|
|
52
|
+
let buffer = '';
|
|
53
|
+
const handleCleanup = async () => {
|
|
54
|
+
rl.close();
|
|
55
|
+
await cleanup();
|
|
56
|
+
};
|
|
57
|
+
process.on('SIGINT', () => {
|
|
58
|
+
if (buffer) {
|
|
59
|
+
// Cancel current multi-line input
|
|
60
|
+
buffer = '';
|
|
61
|
+
process.stdout.write('\n');
|
|
62
|
+
rl.setPrompt('netlifydb=> ');
|
|
63
|
+
rl.prompt();
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
log('');
|
|
67
|
+
void handleCleanup();
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
rl.on('close', () => {
|
|
71
|
+
void handleCleanup();
|
|
72
|
+
});
|
|
73
|
+
rl.on('line', (line) => {
|
|
74
|
+
// Meta-commands are only recognized at the start of input (not mid-statement)
|
|
75
|
+
if (buffer === '' && line.trimStart().startsWith('\\')) {
|
|
76
|
+
rl.pause();
|
|
77
|
+
void (async () => {
|
|
78
|
+
try {
|
|
79
|
+
const result = await executeMetaCommand(line, client);
|
|
80
|
+
switch (result.type) {
|
|
81
|
+
case 'quit':
|
|
82
|
+
await handleCleanup();
|
|
83
|
+
return;
|
|
84
|
+
case 'help':
|
|
85
|
+
log(result.text);
|
|
86
|
+
break;
|
|
87
|
+
case 'unknown':
|
|
88
|
+
log(`Invalid command ${result.command}. Try \\? for help.`);
|
|
89
|
+
break;
|
|
90
|
+
case 'query':
|
|
91
|
+
log(formatQueryResult(result.fields, result.rows, result.rowCount, result.command));
|
|
92
|
+
break;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
catch (err) {
|
|
96
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
97
|
+
log(`ERROR: ${message}`);
|
|
98
|
+
}
|
|
99
|
+
rl.resume();
|
|
100
|
+
rl.prompt();
|
|
101
|
+
})();
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
buffer += (buffer ? '\n' : '') + line;
|
|
105
|
+
if (buffer.trimEnd().endsWith(';')) {
|
|
106
|
+
const sql = buffer;
|
|
107
|
+
buffer = '';
|
|
108
|
+
rl.setPrompt('netlifydb=> ');
|
|
109
|
+
rl.pause();
|
|
110
|
+
void (async () => {
|
|
111
|
+
try {
|
|
112
|
+
const result = await client.query(sql);
|
|
113
|
+
log(formatQueryResult(result.fields, result.rows, result.rowCount, result.command));
|
|
114
|
+
}
|
|
115
|
+
catch (err) {
|
|
116
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
117
|
+
log(`ERROR: ${message}`);
|
|
118
|
+
}
|
|
119
|
+
rl.resume();
|
|
120
|
+
rl.prompt();
|
|
121
|
+
})();
|
|
122
|
+
}
|
|
123
|
+
else {
|
|
124
|
+
rl.setPrompt('netlifydb-> ');
|
|
125
|
+
rl.prompt();
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
rl.prompt();
|
|
129
|
+
};
|
|
130
|
+
//# sourceMappingURL=connect.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"connect.js","sourceRoot":"","sources":["../../../src/commands/database/connect.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../../../src/commands/database/database.ts"],"names":[],"mappings":"AAEA,OAAO,WAAW,MAAM,oBAAoB,CAAA;
|
|
1
|
+
{"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../../../src/commands/database/database.ts"],"names":[],"mappings":"AAEA,OAAO,WAAW,MAAM,oBAAoB,CAAA;AAK5C,MAAM,MAAM,SAAS,GAAG;IACtB,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,WAAW,EAAE,MAAM,CAAA;IACnB,eAAe,EAAE,OAAO,CAAA;CACzB,CAAA;AAED,MAAM,MAAM,QAAQ,GAAG;IACrB,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,UAAU,EAAE,MAAM,CAAA;IAClB,SAAS,EAAE,MAAM,CAAA;IACjB,GAAG,EAAE,MAAM,CAAA;IACX,OAAO,EAAE,MAAM,CAAA;CAChB,CAAA;AAID,eAAO,MAAM,qBAAqB,GAAI,SAAS,WAAW,SAmJzD,CAAA"}
|
|
@@ -8,43 +8,43 @@ export const createDatabaseCommand = (program) => {
|
|
|
8
8
|
.description(`Provision a production ready Postgres database with a single command`)
|
|
9
9
|
.addExamples([
|
|
10
10
|
'netlify db status',
|
|
11
|
-
'netlify db init',
|
|
12
|
-
'netlify db init --help',
|
|
13
11
|
...(process.env.EXPERIMENTAL_NETLIFY_DB_ENABLED === '1'
|
|
14
|
-
? ['netlify db
|
|
15
|
-
: []),
|
|
12
|
+
? ['netlify db migrations apply', 'netlify db migrations pull', 'netlify db reset', 'netlify db migrations new']
|
|
13
|
+
: ['netlify db init', 'netlify db init --help']),
|
|
16
14
|
]);
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
15
|
+
if (process.env.EXPERIMENTAL_NETLIFY_DB_ENABLED !== '1') {
|
|
16
|
+
dbCommand
|
|
17
|
+
.command('init')
|
|
18
|
+
.description(`Initialize a new database for the current site`)
|
|
19
|
+
.option('--assume-no', 'Non-interactive setup. Does not initialize any third-party tools/boilerplate. Ideal for CI environments or AI tools.', false)
|
|
20
|
+
.addOption(new Option('--boilerplate <tool>', 'Type of boilerplate to add to your project.').choices(Array.from(supportedBoilerplates).sort()))
|
|
21
|
+
.option('--no-boilerplate', "Don't add any boilerplate to your project.")
|
|
22
|
+
.option('-o, --overwrite', 'Overwrites existing files that would be created when setting up boilerplate')
|
|
23
|
+
.action(async (_options, command) => {
|
|
24
|
+
const { init } = await import('./init.js');
|
|
25
|
+
// Only prompt for drizzle if the user did not specify a boilerplate option, and if we're in
|
|
26
|
+
// interactive mode
|
|
27
|
+
if (_options.boilerplate === undefined && !_options.assumeNo) {
|
|
28
|
+
const answers = await inquirer.prompt([
|
|
29
|
+
{
|
|
30
|
+
type: 'confirm',
|
|
31
|
+
name: 'useDrizzle',
|
|
32
|
+
message: 'Set up Drizzle boilerplate?',
|
|
33
|
+
},
|
|
34
|
+
]);
|
|
35
|
+
if (answers.useDrizzle) {
|
|
36
|
+
command.setOptionValue('boilerplate', 'drizzle');
|
|
37
|
+
}
|
|
38
38
|
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
39
|
+
const options = _options;
|
|
40
|
+
if (options.assumeNo) {
|
|
41
|
+
options.boilerplate = false;
|
|
42
|
+
options.overwrite = false;
|
|
43
|
+
}
|
|
44
|
+
await init(options, command);
|
|
45
|
+
})
|
|
46
|
+
.addExamples([`netlify db init --assume-no`, `netlify db init --boilerplate=drizzle --overwrite`]);
|
|
47
|
+
}
|
|
48
48
|
dbCommand
|
|
49
49
|
.command('status')
|
|
50
50
|
.description(`Check the status of the database`)
|
|
@@ -55,14 +55,20 @@ export const createDatabaseCommand = (program) => {
|
|
|
55
55
|
});
|
|
56
56
|
if (process.env.EXPERIMENTAL_NETLIFY_DB_ENABLED === '1') {
|
|
57
57
|
dbCommand
|
|
58
|
-
.command('
|
|
59
|
-
.description('
|
|
60
|
-
.option('--
|
|
61
|
-
.option('--json', 'Output
|
|
58
|
+
.command('connect')
|
|
59
|
+
.description('Connect to the database')
|
|
60
|
+
.option('-q, --query <sql>', 'Execute a single query and exit')
|
|
61
|
+
.option('--json', 'Output query results as JSON. When used without --query, prints the connection details as JSON instead.')
|
|
62
62
|
.action(async (options, command) => {
|
|
63
|
-
const {
|
|
64
|
-
await
|
|
65
|
-
})
|
|
63
|
+
const { connect } = await import('./connect.js');
|
|
64
|
+
await connect(options, command);
|
|
65
|
+
})
|
|
66
|
+
.addExamples([
|
|
67
|
+
'netlify db connect',
|
|
68
|
+
'netlify db connect --query "SELECT * FROM users"',
|
|
69
|
+
'netlify db connect --json --query "SELECT * FROM users"',
|
|
70
|
+
'netlify db connect --json',
|
|
71
|
+
]);
|
|
66
72
|
dbCommand
|
|
67
73
|
.command('reset')
|
|
68
74
|
.description('Reset the local development database, removing all data and tables')
|
|
@@ -71,8 +77,17 @@ export const createDatabaseCommand = (program) => {
|
|
|
71
77
|
const { reset } = await import('./reset.js');
|
|
72
78
|
await reset(options, command);
|
|
73
79
|
});
|
|
74
|
-
const
|
|
75
|
-
|
|
80
|
+
const migrationsCommand = dbCommand.command('migrations').description('Manage database migrations');
|
|
81
|
+
migrationsCommand
|
|
82
|
+
.command('apply')
|
|
83
|
+
.description('Apply database migrations to the local development database')
|
|
84
|
+
.option('--to <name>', 'Target migration name or prefix to apply up to (applies all if omitted)')
|
|
85
|
+
.option('--json', 'Output result as JSON')
|
|
86
|
+
.action(async (options, command) => {
|
|
87
|
+
const { migrate } = await import('./migrate.js');
|
|
88
|
+
await migrate(options, command);
|
|
89
|
+
});
|
|
90
|
+
migrationsCommand
|
|
76
91
|
.command('new')
|
|
77
92
|
.description('Create a new migration')
|
|
78
93
|
.option('-d, --description <description>', 'Purpose of the migration (used to generate the file name)')
|
|
@@ -86,8 +101,24 @@ export const createDatabaseCommand = (program) => {
|
|
|
86
101
|
await migrationNew(options, command);
|
|
87
102
|
})
|
|
88
103
|
.addExamples([
|
|
89
|
-
'netlify db
|
|
90
|
-
'netlify db
|
|
104
|
+
'netlify db migrations new',
|
|
105
|
+
'netlify db migrations new --description "add users table" --scheme sequential',
|
|
106
|
+
]);
|
|
107
|
+
migrationsCommand
|
|
108
|
+
.command('pull')
|
|
109
|
+
.description('Pull migrations and overwrite local migration files')
|
|
110
|
+
.option('-b, --branch [branch]', "Pull migrations for a specific branch (defaults to 'production'; pass --branch with no value to use local git branch)")
|
|
111
|
+
.option('--force', 'Skip confirmation prompt', false)
|
|
112
|
+
.option('--json', 'Output result as JSON')
|
|
113
|
+
.action(async (options, command) => {
|
|
114
|
+
const { migrationPull } = await import('./migration-pull.js');
|
|
115
|
+
await migrationPull(options, command);
|
|
116
|
+
})
|
|
117
|
+
.addExamples([
|
|
118
|
+
'netlify db migrations pull',
|
|
119
|
+
'netlify db migrations pull --branch staging',
|
|
120
|
+
'netlify db migrations pull --branch',
|
|
121
|
+
'netlify db migrations pull --force',
|
|
91
122
|
]);
|
|
92
123
|
}
|
|
93
124
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"database.js","sourceRoot":"","sources":["../../../src/commands/database/database.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,WAAW,CAAA;AAClC,OAAO,QAAQ,MAAM,UAAU,CAAA;
|
|
1
|
+
{"version":3,"file":"database.js","sourceRoot":"","sources":["../../../src/commands/database/database.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,8 +1,15 @@
|
|
|
1
|
+
import { Client } from 'pg';
|
|
1
2
|
import { type SQLExecutor } from '@netlify/dev';
|
|
2
3
|
interface DBConnection {
|
|
3
4
|
executor: SQLExecutor;
|
|
4
5
|
cleanup: () => Promise<void>;
|
|
5
6
|
}
|
|
6
7
|
export declare function connectToDatabase(buildDir: string): Promise<DBConnection>;
|
|
8
|
+
interface RawDBConnection {
|
|
9
|
+
client: Client;
|
|
10
|
+
connectionString: string;
|
|
11
|
+
cleanup: () => Promise<void>;
|
|
12
|
+
}
|
|
13
|
+
export declare function connectRawClient(buildDir: string): Promise<RawDBConnection>;
|
|
7
14
|
export {};
|
|
8
15
|
//# sourceMappingURL=db-connection.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"db-connection.d.ts","sourceRoot":"","sources":["../../../src/commands/database/db-connection.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"db-connection.d.ts","sourceRoot":"","sources":["../../../src/commands/database/db-connection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,IAAI,CAAA;AAE3B,OAAO,EAAc,KAAK,WAAW,EAAE,MAAM,cAAc,CAAA;AAK3D,UAAU,YAAY;IACpB,QAAQ,EAAE,WAAW,CAAA;IACrB,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAC7B;AAED,wBAAsB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAM/E;AAED,UAAU,eAAe;IACvB,MAAM,EAAE,MAAM,CAAA;IACd,gBAAgB,EAAE,MAAM,CAAA;IACxB,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAC7B;AAED,wBAAsB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC,CA2DjF"}
|
|
@@ -3,13 +3,31 @@ import { NetlifyDev } from '@netlify/dev';
|
|
|
3
3
|
import { LocalState } from '@netlify/dev-utils';
|
|
4
4
|
import { PgClientExecutor } from './pg-client-executor.js';
|
|
5
5
|
export async function connectToDatabase(buildDir) {
|
|
6
|
+
const { client, cleanup } = await connectRawClient(buildDir);
|
|
7
|
+
return {
|
|
8
|
+
executor: new PgClientExecutor(client),
|
|
9
|
+
cleanup,
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
export async function connectRawClient(buildDir) {
|
|
13
|
+
const envConnectionString = process.env.NETLIFY_DB_URL;
|
|
14
|
+
if (envConnectionString) {
|
|
15
|
+
const client = new Client({ connectionString: envConnectionString });
|
|
16
|
+
await client.connect();
|
|
17
|
+
return {
|
|
18
|
+
client,
|
|
19
|
+
connectionString: envConnectionString,
|
|
20
|
+
cleanup: () => client.end(),
|
|
21
|
+
};
|
|
22
|
+
}
|
|
6
23
|
const state = new LocalState(buildDir);
|
|
7
|
-
const
|
|
8
|
-
if (
|
|
9
|
-
const client = new Client({ connectionString });
|
|
24
|
+
const storedConnectionString = state.get('dbConnectionString');
|
|
25
|
+
if (storedConnectionString) {
|
|
26
|
+
const client = new Client({ connectionString: storedConnectionString });
|
|
10
27
|
await client.connect();
|
|
11
28
|
return {
|
|
12
|
-
|
|
29
|
+
client,
|
|
30
|
+
connectionString: storedConnectionString,
|
|
13
31
|
cleanup: () => client.end(),
|
|
14
32
|
};
|
|
15
33
|
}
|
|
@@ -28,14 +46,20 @@ export async function connectToDatabase(buildDir) {
|
|
|
28
46
|
serverAddress: null,
|
|
29
47
|
});
|
|
30
48
|
await netlifyDev.start();
|
|
31
|
-
const
|
|
32
|
-
if (!
|
|
49
|
+
const connectionString = state.get('dbConnectionString');
|
|
50
|
+
if (!connectionString) {
|
|
33
51
|
await netlifyDev.stop();
|
|
34
52
|
throw new Error('Local database failed to start. Set EXPERIMENTAL_NETLIFY_DB_ENABLED=1 to enable.');
|
|
35
53
|
}
|
|
54
|
+
const client = new Client({ connectionString });
|
|
55
|
+
await client.connect();
|
|
36
56
|
return {
|
|
37
|
-
|
|
38
|
-
|
|
57
|
+
client,
|
|
58
|
+
connectionString,
|
|
59
|
+
cleanup: async () => {
|
|
60
|
+
await client.end();
|
|
61
|
+
await netlifyDev.stop();
|
|
62
|
+
},
|
|
39
63
|
};
|
|
40
64
|
}
|
|
41
65
|
//# sourceMappingURL=db-connection.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"db-connection.js","sourceRoot":"","sources":["../../../src/commands/database/db-connection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,IAAI,CAAA;AAE3B,OAAO,EAAE,UAAU,EAAoB,MAAM,cAAc,CAAA;AAC3D,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAA;AAE/C,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAA;AAO1D,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,QAAgB;IACtD,MAAM,
|
|
1
|
+
{"version":3,"file":"db-connection.js","sourceRoot":"","sources":["../../../src/commands/database/db-connection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,IAAI,CAAA;AAE3B,OAAO,EAAE,UAAU,EAAoB,MAAM,cAAc,CAAA;AAC3D,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAA;AAE/C,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAA;AAO1D,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,QAAgB;IACtD,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,gBAAgB,CAAC,QAAQ,CAAC,CAAA;IAC5D,OAAO;QACL,QAAQ,EAAE,IAAI,gBAAgB,CAAC,MAAM,CAAC;QACtC,OAAO;KACR,CAAA;AACH,CAAC;AAQD,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,QAAgB;IACrD,MAAM,mBAAmB,GAAG,OAAO,CAAC,GAAG,CAAC,cAAc,CAAA;IACtD,IAAI,mBAAmB,EAAE,CAAC;QACxB,MAAM,MAAM,GAAG,IAAI,MAAM,CAAC,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,CAAC,CAAA;QACpE,MAAM,MAAM,CAAC,OAAO,EAAE,CAAA;QACtB,OAAO;YACL,MAAM;YACN,gBAAgB,EAAE,mBAAmB;YACrC,OAAO,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,GAAG,EAAE;SAC5B,CAAA;IACH,CAAC;IAED,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,QAAQ,CAAC,CAAA;IACtC,MAAM,sBAAsB,GAAG,KAAK,CAAC,GAAG,CAAC,oBAAoB,CAAC,CAAA;IAE9D,IAAI,sBAAsB,EAAE,CAAC;QAC3B,MAAM,MAAM,GAAG,IAAI,MAAM,CAAC,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,CAAC,CAAA;QACvE,MAAM,MAAM,CAAC,OAAO,EAAE,CAAA;QACtB,OAAO;YACL,MAAM;YACN,gBAAgB,EAAE,sBAAsB;YACxC,OAAO,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,GAAG,EAAE;SAC5B,CAAA;IACH,CAAC;IAED,MAAM,UAAU,GAAG,IAAI,UAAU,CAAC;QAChC,WAAW,EAAE,QAAQ;QACrB,SAAS,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE;QAC7B,KAAK,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE;QACzB,aAAa,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE;QACjC,oBAAoB,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE;QACxC,SAAS,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE;QAC7B,WAAW,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE;QAC/B,OAAO,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE;QAC3B,MAAM,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE;QAC1B,SAAS,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE;QAC7B,WAAW,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE;QAC/B,aAAa,EAAE,IAAI;KACpB,CAAC,CAAA;IAEF,MAAM,UAAU,CAAC,KAAK,EAAE,CAAA;IAExB,MAAM,gBAAgB,GAAG,KAAK,CAAC,GAAG,CAAC,oBAAoB,CAAC,CAAA;IACxD,IAAI,CAAC,gBAAgB,EAAE,CAAC;QACtB,MAAM,UAAU,CAAC,IAAI,EAAE,CAAA;QACvB,MAAM,IAAI,KAAK,CAAC,kFAAkF,CAAC,CAAA;IACrG,CAAC;IAED,MAAM,MAAM,GAAG,IAAI,MAAM,CAAC,EAAE,gBAAgB,EAAE,CAAC,CAAA;IAC/C,MAAM,MAAM,CAAC,OAAO,EAAE,CAAA;IAEtB,OAAO;QACL,MAAM;QACN,gBAAgB;QAChB,OAAO,EAAE,KAAK,IAAI,EAAE;YAClB,MAAM,MAAM,CAAC,GAAG,EAAE,CAAA;YAClB,MAAM,UAAU,CAAC,IAAI,EAAE,CAAA;QACzB,CAAC;KACF,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { Client, FieldDef } from 'pg';
|
|
2
|
+
export type MetaCommandResult = {
|
|
3
|
+
type: 'query';
|
|
4
|
+
fields: FieldDef[];
|
|
5
|
+
rows: Record<string, unknown>[];
|
|
6
|
+
rowCount: number | null;
|
|
7
|
+
command: string;
|
|
8
|
+
} | {
|
|
9
|
+
type: 'quit';
|
|
10
|
+
} | {
|
|
11
|
+
type: 'help';
|
|
12
|
+
text: string;
|
|
13
|
+
} | {
|
|
14
|
+
type: 'unknown';
|
|
15
|
+
command: string;
|
|
16
|
+
};
|
|
17
|
+
export declare const executeMetaCommand: (input: string, client: Client) => Promise<MetaCommandResult>;
|
|
18
|
+
//# sourceMappingURL=meta-commands.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"meta-commands.d.ts","sourceRoot":"","sources":["../../../src/commands/database/meta-commands.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,IAAI,CAAA;AAE1C,MAAM,MAAM,iBAAiB,GACzB;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,QAAQ,EAAE,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;IAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAChH;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAChB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAC9B;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAA;AAcxC,eAAO,MAAM,kBAAkB,GAAU,OAAO,MAAM,EAAE,QAAQ,MAAM,KAAG,OAAO,CAAC,iBAAiB,CAmDjG,CAAA"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
const HELP_TEXT = `Netlify DB interactive client. Supports a subset of psql commands.
|
|
2
|
+
|
|
3
|
+
General
|
|
4
|
+
\\q quit
|
|
5
|
+
|
|
6
|
+
Informational
|
|
7
|
+
\\d list tables
|
|
8
|
+
\\dt list tables
|
|
9
|
+
\\d NAME describe table
|
|
10
|
+
\\l list databases
|
|
11
|
+
\\? show this help`;
|
|
12
|
+
export const executeMetaCommand = async (input, client) => {
|
|
13
|
+
const trimmed = input.trim();
|
|
14
|
+
const [cmd, ...args] = trimmed.split(/\s+/);
|
|
15
|
+
if (cmd === '\\q') {
|
|
16
|
+
return { type: 'quit' };
|
|
17
|
+
}
|
|
18
|
+
if (cmd === '\\?') {
|
|
19
|
+
return { type: 'help', text: HELP_TEXT };
|
|
20
|
+
}
|
|
21
|
+
if (cmd === '\\dt' || (cmd === '\\d' && args.length === 0)) {
|
|
22
|
+
const result = await client.query(`SELECT schemaname AS "Schema", tablename AS "Name", tableowner AS "Owner"
|
|
23
|
+
FROM pg_tables
|
|
24
|
+
WHERE schemaname NOT IN ('pg_catalog', 'information_schema')
|
|
25
|
+
ORDER BY schemaname, tablename`);
|
|
26
|
+
return { type: 'query', fields: result.fields, rows: result.rows, rowCount: result.rowCount, command: 'SELECT' };
|
|
27
|
+
}
|
|
28
|
+
if (cmd === '\\d' && args.length > 0) {
|
|
29
|
+
const tableName = args[0];
|
|
30
|
+
const result = await client.query(`SELECT column_name AS "Column", data_type AS "Type",
|
|
31
|
+
CASE WHEN is_nullable = 'YES' THEN 'yes' ELSE 'no' END AS "Nullable",
|
|
32
|
+
column_default AS "Default"
|
|
33
|
+
FROM information_schema.columns
|
|
34
|
+
WHERE table_schema = 'public' AND table_name = $1
|
|
35
|
+
ORDER BY ordinal_position`, [tableName]);
|
|
36
|
+
if (result.rowCount === 0) {
|
|
37
|
+
return { type: 'query', fields: result.fields, rows: result.rows, rowCount: 0, command: 'SELECT' };
|
|
38
|
+
}
|
|
39
|
+
return { type: 'query', fields: result.fields, rows: result.rows, rowCount: result.rowCount, command: 'SELECT' };
|
|
40
|
+
}
|
|
41
|
+
if (cmd === '\\l') {
|
|
42
|
+
const result = await client.query(`SELECT datname AS "Name",
|
|
43
|
+
pg_catalog.pg_get_userbyid(datdba) AS "Owner",
|
|
44
|
+
pg_catalog.pg_encoding_to_char(encoding) AS "Encoding"
|
|
45
|
+
FROM pg_catalog.pg_database
|
|
46
|
+
ORDER BY 1`);
|
|
47
|
+
return { type: 'query', fields: result.fields, rows: result.rows, rowCount: result.rowCount, command: 'SELECT' };
|
|
48
|
+
}
|
|
49
|
+
return { type: 'unknown', command: cmd };
|
|
50
|
+
};
|
|
51
|
+
//# sourceMappingURL=meta-commands.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"meta-commands.js","sourceRoot":"","sources":["../../../src/commands/database/meta-commands.ts"],"names":[],"mappings":"AAQA,MAAM,SAAS,GAAG;;;;;;;;;;8BAUY,CAAA;AAE9B,MAAM,CAAC,MAAM,kBAAkB,GAAG,KAAK,EAAE,KAAa,EAAE,MAAc,EAA8B,EAAE;IACpG,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,EAAE,CAAA;IAC5B,MAAM,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAA;IAE3C,IAAI,GAAG,KAAK,KAAK,EAAE,CAAC;QAClB,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,CAAA;IACzB,CAAC;IAED,IAAI,GAAG,KAAK,KAAK,EAAE,CAAC;QAClB,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,CAAA;IAC1C,CAAC;IAED,IAAI,GAAG,KAAK,MAAM,IAAI,CAAC,GAAG,KAAK,KAAK,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,CAAC,EAAE,CAAC;QAC3D,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,KAAK,CAC/B;;;sCAGgC,CACjC,CAAA;QACD,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAA;IAClH,CAAC;IAED,IAAI,GAAG,KAAK,KAAK,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACrC,MAAM,SAAS,GAAG,IAAI,CAAC,CAAC,CAAC,CAAA;QACzB,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,KAAK,CAC/B;;;;;iCAK2B,EAC3B,CAAC,SAAS,CAAC,CACZ,CAAA;QACD,IAAI,MAAM,CAAC,QAAQ,KAAK,CAAC,EAAE,CAAC;YAC1B,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,QAAQ,EAAE,CAAC,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAA;QACpG,CAAC;QACD,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAA;IAClH,CAAC;IAED,IAAI,GAAG,KAAK,KAAK,EAAE,CAAC;QAClB,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,KAAK,CAC/B;;;;kBAIY,CACb,CAAA;QACD,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAA;IAClH,CAAC;IAED,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,GAAG,EAAE,CAAA;AAC1C,CAAC,CAAA"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import BaseCommand from '../base-command.js';
|
|
2
|
+
export interface MigrationPullOptions {
|
|
3
|
+
branch?: string | true;
|
|
4
|
+
force?: boolean;
|
|
5
|
+
json?: boolean;
|
|
6
|
+
}
|
|
7
|
+
export declare const migrationPull: (options: MigrationPullOptions, command: BaseCommand) => Promise<void>;
|
|
8
|
+
//# sourceMappingURL=migration-pull.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"migration-pull.d.ts","sourceRoot":"","sources":["../../../src/commands/database/migration-pull.ts"],"names":[],"mappings":"AAOA,OAAO,WAAW,MAAM,oBAAoB,CAAA;AAG5C,MAAM,WAAW,oBAAoB;IACnC,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACtB,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,IAAI,CAAC,EAAE,OAAO,CAAA;CACf;AAkED,eAAO,MAAM,aAAa,GAAU,SAAS,oBAAoB,EAAE,SAAS,WAAW,kBAkEtF,CAAA"}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { rm, mkdir, writeFile } from 'fs/promises';
|
|
2
|
+
import { dirname, resolve, isAbsolute } from 'path';
|
|
3
|
+
import inquirer from 'inquirer';
|
|
4
|
+
import { log, logJson } from '../../utils/command-helpers.js';
|
|
5
|
+
import execa from '../../utils/execa.js';
|
|
6
|
+
import { resolveMigrationsDirectory } from './migration-new.js';
|
|
7
|
+
const getLocalGitBranch = async () => {
|
|
8
|
+
const { stdout } = await execa('git', ['rev-parse', '--abbrev-ref', 'HEAD']);
|
|
9
|
+
const branch = stdout.trim();
|
|
10
|
+
if (!branch || branch === 'HEAD') {
|
|
11
|
+
throw new Error('Could not determine the current git branch. Are you in a detached HEAD state?');
|
|
12
|
+
}
|
|
13
|
+
return branch;
|
|
14
|
+
};
|
|
15
|
+
const resolveBranch = async (branchOption) => {
|
|
16
|
+
if (branchOption === undefined) {
|
|
17
|
+
return undefined;
|
|
18
|
+
}
|
|
19
|
+
if (branchOption === true) {
|
|
20
|
+
return getLocalGitBranch();
|
|
21
|
+
}
|
|
22
|
+
return branchOption;
|
|
23
|
+
};
|
|
24
|
+
const fetchMigrations = async (command, branch) => {
|
|
25
|
+
const siteId = command.siteId;
|
|
26
|
+
if (!siteId) {
|
|
27
|
+
throw new Error('The project must be linked with netlify link before pulling migrations.');
|
|
28
|
+
}
|
|
29
|
+
const accessToken = command.netlify.api.accessToken;
|
|
30
|
+
if (!accessToken) {
|
|
31
|
+
throw new Error('You must be logged in with netlify login to pull migrations.');
|
|
32
|
+
}
|
|
33
|
+
const token = accessToken.replace('Bearer ', '');
|
|
34
|
+
const basePath = command.netlify.api.basePath;
|
|
35
|
+
const url = new URL(`${basePath}/sites/${encodeURIComponent(siteId)}/database/migrations`);
|
|
36
|
+
if (branch) {
|
|
37
|
+
url.searchParams.set('branch', branch);
|
|
38
|
+
}
|
|
39
|
+
const response = await fetch(url, {
|
|
40
|
+
headers: {
|
|
41
|
+
Authorization: `Bearer ${token}`,
|
|
42
|
+
},
|
|
43
|
+
});
|
|
44
|
+
if (!response.ok) {
|
|
45
|
+
const text = await response.text();
|
|
46
|
+
throw new Error(`Failed to fetch migrations (${String(response.status)}): ${text}`);
|
|
47
|
+
}
|
|
48
|
+
const data = (await response.json());
|
|
49
|
+
return data.migrations;
|
|
50
|
+
};
|
|
51
|
+
export const migrationPull = async (options, command) => {
|
|
52
|
+
const { force, json } = options;
|
|
53
|
+
const branch = await resolveBranch(options.branch);
|
|
54
|
+
const source = branch ?? 'production';
|
|
55
|
+
const migrations = await fetchMigrations(command, branch);
|
|
56
|
+
if (migrations.length === 0) {
|
|
57
|
+
if (json) {
|
|
58
|
+
logJson({ migrations_pulled: 0, branch: source });
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
log(`No migrations found for ${source}.`);
|
|
62
|
+
}
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
const migrationsDirectory = resolveMigrationsDirectory(command);
|
|
66
|
+
if (!force) {
|
|
67
|
+
const { confirmed } = await inquirer.prompt([
|
|
68
|
+
{
|
|
69
|
+
type: 'confirm',
|
|
70
|
+
name: 'confirmed',
|
|
71
|
+
message: `This will overwrite all local migrations in ${migrationsDirectory} with ${String(migrations.length)} migration${migrations.length === 1 ? '' : 's'} from ${source}. Continue?`,
|
|
72
|
+
default: false,
|
|
73
|
+
},
|
|
74
|
+
]);
|
|
75
|
+
if (!confirmed) {
|
|
76
|
+
log('Pull cancelled.');
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
const canonicalMigrationsDir = resolve(migrationsDirectory);
|
|
81
|
+
await rm(canonicalMigrationsDir, { recursive: true, force: true });
|
|
82
|
+
for (const migration of migrations) {
|
|
83
|
+
if (isAbsolute(migration.path) || migration.path.split(/[/\\]/).includes('..')) {
|
|
84
|
+
throw new Error(`Migration path "${migration.path}" contains invalid path segments.`);
|
|
85
|
+
}
|
|
86
|
+
const filePath = resolve(canonicalMigrationsDir, migration.path);
|
|
87
|
+
if (!filePath.startsWith(canonicalMigrationsDir)) {
|
|
88
|
+
throw new Error(`Migration path "${migration.path}" resolves outside the migrations directory.`);
|
|
89
|
+
}
|
|
90
|
+
await mkdir(dirname(filePath), { recursive: true });
|
|
91
|
+
await writeFile(filePath, migration.content);
|
|
92
|
+
}
|
|
93
|
+
if (json) {
|
|
94
|
+
logJson({
|
|
95
|
+
migrations_pulled: migrations.length,
|
|
96
|
+
branch: source,
|
|
97
|
+
migrations: migrations.map((m) => m.name),
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
log(`Pulled ${String(migrations.length)} migration${migrations.length === 1 ? '' : 's'} from ${source}:`);
|
|
102
|
+
for (const migration of migrations) {
|
|
103
|
+
log(` - ${migration.name}`);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
//# sourceMappingURL=migration-pull.js.map
|