Skip to content

Commit 45df433

Browse files
committed
simplify CLI install command output formatting and show full paths (#439)
1 parent ecc7e47 commit 45df433

File tree

6 files changed

+91
-174
lines changed

6 files changed

+91
-174
lines changed

pkgs/cli/src/commands/install/copy-migrations.ts

Lines changed: 18 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import fs from 'fs';
22
import path from 'path';
33
import { createRequire } from 'module';
44
import { fileURLToPath } from 'url';
5-
import { log, confirm, note } from '@clack/prompts';
5+
import { log, confirm } from '@clack/prompts';
66
import chalk from 'chalk';
77

88
// Get the directory name in ES modules
@@ -231,38 +231,9 @@ export async function copyMigrations({
231231
}
232232
}
233233

234-
// If no files to copy, show message with details and return false (no changes made)
234+
// If no files to copy, show message and return false (no changes made)
235235
if (filesToCopy.length === 0) {
236-
// Show success message
237-
log.success('All pgflow migrations are already in place');
238-
239-
// Show details of already installed migrations
240-
if (skippedFiles.length > 0) {
241-
const detailedMsg = [
242-
'Already installed migrations:',
243-
...skippedFiles.map((file) => {
244-
// Find the matching existing file to show how it was installed
245-
const matchingFile = existingFiles.find((existingFile) =>
246-
existingFile.includes(file)
247-
);
248-
249-
if (matchingFile === file) {
250-
// Installed with old direct method
251-
return ` ${chalk.dim('•')} ${chalk.bold(file)}`;
252-
} else {
253-
// Installed with new timestamped method
254-
const timestampPart =
255-
matchingFile?.substring(0, matchingFile.indexOf(file) - 1) || '';
256-
return ` ${chalk.dim('•')} ${chalk.dim(
257-
timestampPart + '_'
258-
)}${chalk.bold(file)}`;
259-
}
260-
}),
261-
].join('\n');
262-
263-
note(detailedMsg, 'Existing pgflow Migrations');
264-
}
265-
236+
log.success(`All ${skippedFiles.length} pgflow migrations are already in place`);
266237
return false;
267238
}
268239

@@ -276,48 +247,24 @@ export async function copyMigrations({
276247
file.destination = `${baseTimestamp}_${file.source}`;
277248
});
278249

279-
log.info(
280-
`Found ${filesToCopy.length} migration${
281-
filesToCopy.length !== 1 ? 's' : ''
282-
} to install`
283-
);
284-
285-
// Prepare summary message with colored output
286-
const summaryParts = [];
287-
288-
if (filesToCopy.length > 0) {
289-
summaryParts.push(
290-
`${chalk.green('New migrations to install:')}\n${filesToCopy
291-
.map((file) => {
292-
// Extract the timestamp part from the new filename
293-
const newTimestamp = file.destination.substring(0, 14);
294-
// Format: dim timestamp + bright original name
295-
return `${chalk.green('+')} ${file.source}${chalk.dim(
296-
newTimestamp + '_'
297-
)}${chalk.bold(file.source)}`;
298-
})
299-
.join('\n')}`
300-
);
301-
}
250+
// Build summary message with explanation - show all migrations
251+
const migrationLines = filesToCopy.map((file) => {
252+
return ` ${chalk.bold(file.source)}`;
253+
});
302254

303-
if (skippedFiles.length > 0) {
304-
summaryParts.push(
305-
`${chalk.yellow('Already installed:')}\n${skippedFiles
306-
.map((file) => `${chalk.yellow('•')} ${file}`)
307-
.join('\n')}`
308-
);
309-
}
255+
const summaryMsg = [
256+
`Add to ${chalk.cyan('migrations/')} ${chalk.dim('(database schema for workflow engine)')}:`,
257+
'',
258+
...migrationLines,
259+
].join('\n');
310260

311-
// Show summary and ask for confirmation if not auto-confirming
312-
note(summaryParts.join('\n\n'), 'pgflow Migrations');
261+
log.info(summaryMsg);
313262

314263
let shouldContinue = autoConfirm;
315264

316265
if (!autoConfirm) {
317266
const confirmResult = await confirm({
318-
message: `Install ${filesToCopy.length} new migration${
319-
filesToCopy.length !== 1 ? 's' : ''
320-
}?`,
267+
message: `Add ${filesToCopy.length} migration${filesToCopy.length !== 1 ? 's' : ''}?`,
321268
});
322269

323270
shouldContinue = confirmResult === true;
@@ -336,18 +283,12 @@ export async function copyMigrations({
336283
fs.copyFileSync(sourcePath1, destinationPath);
337284
}
338285

339-
// Show detailed success message with styled filenames
340-
const detailedSuccessMsg = [
341-
`Installed ${filesToCopy.length} migration${
342-
filesToCopy.length !== 1 ? 's' : ''
343-
} to your Supabase project:`,
344-
...filesToCopy.map((file) => {
345-
const newTimestamp = file.destination.substring(0, 14);
346-
return ` ${chalk.dim(newTimestamp + '_')}${chalk.bold(file.source)}`;
347-
}),
286+
const successMsg = [
287+
`Installed ${filesToCopy.length} migration${filesToCopy.length !== 1 ? 's' : ''}`,
288+
` ${chalk.dim('Learn more:')} ${chalk.blue.underline('https://pgflow.dev/concepts/data-model/')}`,
348289
].join('\n');
349290

350-
log.success(detailedSuccessMsg);
291+
log.success(successMsg);
351292

352293
return true; // Return true to indicate migrations were copied
353294
}

pkgs/cli/src/commands/install/create-edge-function.ts

Lines changed: 26 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import fs from 'fs';
22
import path from 'path';
3-
import { log, confirm, note } from '@clack/prompts';
3+
import { log, confirm } from '@clack/prompts';
44
import chalk from 'chalk';
55
import { getVersion } from '../../utils/get-version.js';
66

@@ -41,54 +41,57 @@ export async function createEdgeFunction({
4141
const indexPath = path.join(pgflowFunctionDir, 'index.ts');
4242
const denoJsonPath = path.join(pgflowFunctionDir, 'deno.json');
4343

44+
// Relative paths for display
45+
const relativeFunctionDir = 'supabase/functions/pgflow';
46+
const relativeIndexPath = `${relativeFunctionDir}/index.ts`;
47+
const relativeDenoJsonPath = `${relativeFunctionDir}/deno.json`;
48+
4449
// Check what needs to be created
45-
const filesToCreate: Array<{ path: string; name: string }> = [];
50+
const filesToCreate: Array<{ path: string; relativePath: string }> = [];
4651

4752
if (!fs.existsSync(indexPath)) {
48-
filesToCreate.push({ path: indexPath, name: 'index.ts' });
53+
filesToCreate.push({ path: indexPath, relativePath: relativeIndexPath });
4954
}
5055

5156
if (!fs.existsSync(denoJsonPath)) {
52-
filesToCreate.push({ path: denoJsonPath, name: 'deno.json' });
57+
filesToCreate.push({ path: denoJsonPath, relativePath: relativeDenoJsonPath });
5358
}
5459

5560
// If all files exist, return success
5661
if (filesToCreate.length === 0) {
57-
log.success('ControlPlane edge function files are already in place');
58-
5962
const detailedMsg = [
60-
'Existing files:',
61-
` ${chalk.dim('•')} ${chalk.bold('supabase/functions/pgflow/index.ts')}`,
62-
` ${chalk.dim('•')} ${chalk.bold('supabase/functions/pgflow/deno.json')}`,
63+
'ControlPlane edge function files are already in place:',
64+
` ${chalk.bold(relativeIndexPath)}`,
65+
` ${chalk.bold(relativeDenoJsonPath)}`,
6366
].join('\n');
6467

65-
note(detailedMsg, 'ControlPlane Edge Function');
68+
log.success(detailedMsg);
6669

6770
return false;
6871
}
6972

70-
// Show what will be created
71-
log.info(`Found ${filesToCreate.length} file${filesToCreate.length !== 1 ? 's' : ''} to create`);
72-
73-
const summaryParts = [`${chalk.green('Files to create:')}\n${filesToCreate
74-
.map((file) => `${chalk.green('+')} ${file.name}`)
75-
.join('\n')}`];
73+
// Show what will be created with explanation
74+
const summaryMsg = [
75+
`Create ${chalk.cyan('functions/pgflow/')} ${chalk.dim('(Control Plane for flow registration and compilation)')}:`,
76+
'',
77+
...filesToCreate.map((file) => ` ${chalk.bold(path.basename(file.relativePath))}`),
78+
].join('\n');
7679

77-
note(summaryParts.join('\n'), 'ControlPlane Edge Function');
80+
log.info(summaryMsg);
7881

7982
// Get confirmation
8083
let shouldContinue = autoConfirm;
8184

8285
if (!autoConfirm) {
8386
const confirmResult = await confirm({
84-
message: `Create ${filesToCreate.length} file${filesToCreate.length !== 1 ? 's' : ''}?`,
87+
message: `Create functions/pgflow/?`,
8588
});
8689

8790
shouldContinue = confirmResult === true;
8891
}
8992

9093
if (!shouldContinue) {
91-
log.warn('Edge function setup skipped');
94+
log.warn('Control Plane installation skipped');
9295
return false;
9396
}
9497

@@ -106,13 +109,12 @@ export async function createEdgeFunction({
106109
fs.writeFileSync(denoJsonPath, DENO_JSON_TEMPLATE(getVersion()));
107110
}
108111

109-
// Show success message
110-
const detailedSuccessMsg = [
111-
`Created ${filesToCreate.length} file${filesToCreate.length !== 1 ? 's' : ''}:`,
112-
...filesToCreate.map((file) => ` ${chalk.bold(file.name)}`),
112+
const successMsg = [
113+
`Control Plane installed`,
114+
` ${chalk.dim('Learn more:')} ${chalk.blue.underline('https://pgflow.dev/concepts/compilation/')}`,
113115
].join('\n');
114116

115-
log.success(detailedSuccessMsg);
117+
log.success(successMsg);
116118

117119
return true;
118120
}

pkgs/cli/src/commands/install/index.ts

Lines changed: 12 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -86,46 +86,39 @@ export default (program: Command) => {
8686
}
8787

8888
// Show completion message
89-
const outroMessages = [];
89+
const outroMessages: string[] = [];
9090

9191
// Always start with a bolded acknowledgement
9292
if (migrations || configUpdate || edgeFunction || envFile) {
93-
outroMessages.push(chalk.bold('pgflow setup completed successfully!'));
93+
outroMessages.push(chalk.bold('Installation complete!'));
9494
} else {
9595
outroMessages.push(
96-
chalk.bold(
97-
'pgflow is already properly configured - no changes needed!'
98-
)
96+
chalk.bold('pgflow is already installed - no changes needed!')
9997
);
10098
}
10199

102-
// Add a newline after the acknowledgement
100+
// Add numbered next steps
103101
outroMessages.push('');
102+
outroMessages.push('Next steps:');
103+
104+
let stepNumber = 1;
104105

105-
// Add specific next steps if changes were made
106106
if (configUpdate || envFile) {
107107
outroMessages.push(
108-
`- Restart your Supabase instance for configuration changes to take effect`
108+
` ${stepNumber}. Restart Supabase: ${chalk.cyan('supabase stop && supabase start')}`
109109
);
110+
stepNumber++;
110111
}
111112

112113
if (migrations) {
113114
outroMessages.push(
114-
`- Apply the migrations with: ${chalk.cyan('supabase migrations up')}`
115+
` ${stepNumber}. Apply migrations: ${chalk.cyan('supabase migrations up')}`
115116
);
116-
}
117-
118-
// Always add documentation link with consistent formatting
119-
if (outroMessages.length > 2) {
120-
// If we have specific steps, add another newline
121-
outroMessages.push('');
117+
stepNumber++;
122118
}
123119

124120
outroMessages.push(
125-
chalk.bold('Continue the setup:'),
126-
chalk.blue.underline(
127-
'https://pgflow.dev/getting-started/create-first-flow/'
128-
)
121+
` ${stepNumber}. Create your first flow: ${chalk.blue.underline('https://pgflow.dev/getting-started/create-first-flow/')}`
129122
);
130123

131124
// Single outro for all paths

pkgs/cli/src/commands/install/supabase-path-prompt.ts

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,6 @@ export async function supabasePathPrompt(options?: { supabasePath?: string }) {
2828
}
2929
}
3030

31-
// Always prompt for detected paths - don't skip
32-
if (detectedPath) {
33-
log.info(`Found Supabase project at: ${detectedPath}`);
34-
}
35-
3631
const promptMessage = 'Where is your Supabase project located?';
3732

3833
const supabasePath = await text({

pkgs/cli/src/commands/install/update-config-toml.ts

Lines changed: 22 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import fs from 'fs';
22
import path from 'path';
3-
import { log, confirm, note } from '@clack/prompts';
3+
import { log, confirm } from '@clack/prompts';
44
import * as TOML from '@decimalturn/toml-patch';
55
import chalk from 'chalk';
66

@@ -77,33 +77,40 @@ export async function updateConfigToml({
7777
return false;
7878
}
7979

80-
const changes = [];
80+
const changes: string[] = [];
8181

82+
// Connection pooler changes
83+
const poolerChanges: string[] = [];
8284
if (currentSettings.poolerEnabled !== true) {
83-
changes.push(`${chalk.bold('Enable connection pooler:')}
84-
${chalk.red(`- enabled = ${currentSettings.poolerEnabled}`)}
85-
${chalk.green('+ enabled = true')}`);
85+
poolerChanges.push(`enabled = ${currentSettings.poolerEnabled} ${chalk.dim('->')} ${chalk.green('true')}`);
8686
}
87-
8887
if (currentSettings.poolMode !== 'transaction') {
89-
changes.push(`${chalk.bold('Set pool mode to transaction:')}
90-
${chalk.red(`- pool_mode = "${currentSettings.poolMode}"`)}
91-
${chalk.green('+ pool_mode = "transaction"')}`);
88+
poolerChanges.push(`pool_mode = "${currentSettings.poolMode}" ${chalk.dim('->')} ${chalk.green('"transaction"')}`);
89+
}
90+
if (poolerChanges.length > 0) {
91+
changes.push(` ${chalk.bold('[db.pooler]')} ${chalk.dim('(required for pgflow worker)')}`);
92+
poolerChanges.forEach(change => changes.push(` ${change}`));
9293
}
9394

95+
// Edge runtime changes
9496
if (currentSettings.edgeRuntimePolicy !== 'per_worker') {
95-
changes.push(`${chalk.bold('Set edge runtime policy:')}
96-
${chalk.red(`- policy = "${currentSettings.edgeRuntimePolicy}"`)}
97-
${chalk.green('+ policy = "per_worker"')}`);
97+
changes.push(` ${chalk.bold('[edge_runtime]')} ${chalk.dim('(required for long-running tasks)')}`);
98+
changes.push(` policy = "${currentSettings.edgeRuntimePolicy}" ${chalk.dim('->')} ${chalk.green('"per_worker"')}`);
9899
}
99100

100-
note(changes.join('\n\n'), 'Required Configuration Changes');
101+
const summaryMsg = [
102+
`Update ${chalk.cyan('config.toml')}:`,
103+
'',
104+
...changes,
105+
].join('\n');
106+
107+
log.info(summaryMsg);
101108

102109
let shouldContinue = autoConfirm;
103110

104111
if (!autoConfirm) {
105112
const confirmResult = await confirm({
106-
message: `Update Supabase configuration? (a backup will be created)`,
113+
message: `Update config.toml? (backup will be created)`,
107114
});
108115

109116
shouldContinue = confirmResult === true;
@@ -149,7 +156,7 @@ ${chalk.green('+ policy = "per_worker"')}`);
149156
throw new Error(`Failed to write ${configPath}: ${errorMsg}`);
150157
}
151158

152-
log.success('Supabase configuration updated successfully');
159+
log.success('Configuration updated');
153160
return true;
154161
} catch (error) {
155162
log.error(

0 commit comments

Comments
 (0)