popeye-cli 1.6.0 → 1.8.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/README.md +240 -32
- package/cheatsheet.md +407 -0
- package/dist/cli/commands/db.d.ts +10 -0
- package/dist/cli/commands/db.d.ts.map +1 -0
- package/dist/cli/commands/db.js +240 -0
- package/dist/cli/commands/db.js.map +1 -0
- package/dist/cli/commands/doctor.d.ts +18 -0
- package/dist/cli/commands/doctor.d.ts.map +1 -0
- package/dist/cli/commands/doctor.js +255 -0
- package/dist/cli/commands/doctor.js.map +1 -0
- package/dist/cli/commands/index.d.ts +2 -0
- package/dist/cli/commands/index.d.ts.map +1 -1
- package/dist/cli/commands/index.js +2 -0
- package/dist/cli/commands/index.js.map +1 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +3 -1
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/interactive.d.ts.map +1 -1
- package/dist/cli/interactive.js +96 -0
- package/dist/cli/interactive.js.map +1 -1
- package/dist/generators/admin-wizard.d.ts +25 -0
- package/dist/generators/admin-wizard.d.ts.map +1 -0
- package/dist/generators/admin-wizard.js +123 -0
- package/dist/generators/admin-wizard.js.map +1 -0
- package/dist/generators/all.d.ts.map +1 -1
- package/dist/generators/all.js +10 -3
- package/dist/generators/all.js.map +1 -1
- package/dist/generators/database.d.ts +58 -0
- package/dist/generators/database.d.ts.map +1 -0
- package/dist/generators/database.js +229 -0
- package/dist/generators/database.js.map +1 -0
- package/dist/generators/fullstack.d.ts.map +1 -1
- package/dist/generators/fullstack.js +23 -7
- package/dist/generators/fullstack.js.map +1 -1
- package/dist/generators/index.d.ts +2 -0
- package/dist/generators/index.d.ts.map +1 -1
- package/dist/generators/index.js +2 -0
- package/dist/generators/index.js.map +1 -1
- package/dist/generators/templates/admin-wizard-python.d.ts +32 -0
- package/dist/generators/templates/admin-wizard-python.d.ts.map +1 -0
- package/dist/generators/templates/admin-wizard-python.js +425 -0
- package/dist/generators/templates/admin-wizard-python.js.map +1 -0
- package/dist/generators/templates/admin-wizard-react.d.ts +48 -0
- package/dist/generators/templates/admin-wizard-react.d.ts.map +1 -0
- package/dist/generators/templates/admin-wizard-react.js +554 -0
- package/dist/generators/templates/admin-wizard-react.js.map +1 -0
- package/dist/generators/templates/database-docker.d.ts +23 -0
- package/dist/generators/templates/database-docker.d.ts.map +1 -0
- package/dist/generators/templates/database-docker.js +221 -0
- package/dist/generators/templates/database-docker.js.map +1 -0
- package/dist/generators/templates/database-python.d.ts +54 -0
- package/dist/generators/templates/database-python.d.ts.map +1 -0
- package/dist/generators/templates/database-python.js +723 -0
- package/dist/generators/templates/database-python.js.map +1 -0
- package/dist/generators/templates/database-typescript.d.ts +34 -0
- package/dist/generators/templates/database-typescript.d.ts.map +1 -0
- package/dist/generators/templates/database-typescript.js +232 -0
- package/dist/generators/templates/database-typescript.js.map +1 -0
- package/dist/generators/templates/fullstack.d.ts.map +1 -1
- package/dist/generators/templates/fullstack.js +29 -0
- package/dist/generators/templates/fullstack.js.map +1 -1
- package/dist/generators/templates/index.d.ts +5 -0
- package/dist/generators/templates/index.d.ts.map +1 -1
- package/dist/generators/templates/index.js +5 -0
- package/dist/generators/templates/index.js.map +1 -1
- package/dist/state/index.d.ts +10 -0
- package/dist/state/index.d.ts.map +1 -1
- package/dist/state/index.js +22 -0
- package/dist/state/index.js.map +1 -1
- package/dist/types/consensus.d.ts +3 -0
- package/dist/types/consensus.d.ts.map +1 -1
- package/dist/types/consensus.js +1 -0
- package/dist/types/consensus.js.map +1 -1
- package/dist/types/database-runtime.d.ts +86 -0
- package/dist/types/database-runtime.d.ts.map +1 -0
- package/dist/types/database-runtime.js +61 -0
- package/dist/types/database-runtime.js.map +1 -0
- package/dist/types/database.d.ts +85 -0
- package/dist/types/database.d.ts.map +1 -0
- package/dist/types/database.js +71 -0
- package/dist/types/database.js.map +1 -0
- package/dist/types/index.d.ts +3 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +6 -0
- package/dist/types/index.js.map +1 -1
- package/dist/types/tester.d.ts +138 -0
- package/dist/types/tester.d.ts.map +1 -0
- package/dist/types/tester.js +110 -0
- package/dist/types/tester.js.map +1 -0
- package/dist/types/workflow.d.ts +166 -0
- package/dist/types/workflow.d.ts.map +1 -1
- package/dist/types/workflow.js +14 -0
- package/dist/types/workflow.js.map +1 -1
- package/dist/workflow/db-setup-runner.d.ts +63 -0
- package/dist/workflow/db-setup-runner.d.ts.map +1 -0
- package/dist/workflow/db-setup-runner.js +336 -0
- package/dist/workflow/db-setup-runner.js.map +1 -0
- package/dist/workflow/db-state-machine.d.ts +30 -0
- package/dist/workflow/db-state-machine.d.ts.map +1 -0
- package/dist/workflow/db-state-machine.js +51 -0
- package/dist/workflow/db-state-machine.js.map +1 -0
- package/dist/workflow/execution-mode.js +2 -2
- package/dist/workflow/execution-mode.js.map +1 -1
- package/dist/workflow/index.d.ts +3 -0
- package/dist/workflow/index.d.ts.map +1 -1
- package/dist/workflow/index.js +3 -0
- package/dist/workflow/index.js.map +1 -1
- package/dist/workflow/task-workflow.d.ts +5 -0
- package/dist/workflow/task-workflow.d.ts.map +1 -1
- package/dist/workflow/task-workflow.js +172 -6
- package/dist/workflow/task-workflow.js.map +1 -1
- package/dist/workflow/tester.d.ts +120 -0
- package/dist/workflow/tester.d.ts.map +1 -0
- package/dist/workflow/tester.js +589 -0
- package/dist/workflow/tester.js.map +1 -0
- package/dist/workflow/workflow-logger.d.ts +1 -1
- package/dist/workflow/workflow-logger.d.ts.map +1 -1
- package/dist/workflow/workflow-logger.js.map +1 -1
- package/package.json +1 -1
- package/src/cli/commands/db.ts +281 -0
- package/src/cli/commands/doctor.ts +273 -0
- package/src/cli/commands/index.ts +2 -0
- package/src/cli/index.ts +4 -0
- package/src/cli/interactive.ts +102 -0
- package/src/generators/admin-wizard.ts +146 -0
- package/src/generators/all.ts +10 -3
- package/src/generators/database.ts +286 -0
- package/src/generators/fullstack.ts +26 -9
- package/src/generators/index.ts +12 -0
- package/src/generators/templates/admin-wizard-python.ts +431 -0
- package/src/generators/templates/admin-wizard-react.ts +560 -0
- package/src/generators/templates/database-docker.ts +227 -0
- package/src/generators/templates/database-python.ts +734 -0
- package/src/generators/templates/database-typescript.ts +238 -0
- package/src/generators/templates/fullstack.ts +29 -0
- package/src/generators/templates/index.ts +5 -0
- package/src/state/index.ts +29 -0
- package/src/types/consensus.ts +3 -0
- package/src/types/database-runtime.ts +69 -0
- package/src/types/database.ts +84 -0
- package/src/types/index.ts +50 -0
- package/src/types/tester.ts +136 -0
- package/src/types/workflow.ts +31 -0
- package/src/workflow/db-setup-runner.ts +391 -0
- package/src/workflow/db-state-machine.ts +58 -0
- package/src/workflow/execution-mode.ts +2 -2
- package/src/workflow/index.ts +3 -0
- package/src/workflow/task-workflow.ts +227 -5
- package/src/workflow/tester.ts +723 -0
- package/src/workflow/workflow-logger.ts +2 -0
- package/tests/generators/admin-wizard-orchestrator.test.ts +64 -0
- package/tests/generators/admin-wizard-templates.test.ts +366 -0
- package/tests/generators/cross-phase-integration.test.ts +383 -0
- package/tests/generators/database.test.ts +456 -0
- package/tests/generators/fe-be-db-integration.test.ts +613 -0
- package/tests/types/database-runtime.test.ts +158 -0
- package/tests/types/database.test.ts +187 -0
- package/tests/types/tester.test.ts +174 -0
- package/tests/workflow/db-setup-runner.test.ts +211 -0
- package/tests/workflow/db-state-machine.test.ts +117 -0
- package/tests/workflow/tester.test.ts +401 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workflow-logger.js","sourceRoot":"","sources":["../../src/workflow/workflow-logger.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,QAAQ,IAAI,EAAE,EAAE,MAAM,SAAS,CAAC;AACzC,OAAO,IAAI,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"workflow-logger.js","sourceRoot":"","sources":["../../src/workflow/workflow-logger.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Database CLI command
|
|
3
|
+
* Provides subcommands: status, configure, apply
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { Command } from 'commander';
|
|
7
|
+
import path from 'node:path';
|
|
8
|
+
import { promises as fsPromises } from 'node:fs';
|
|
9
|
+
import { createInterface } from 'node:readline';
|
|
10
|
+
import { loadProject, updateState } from '../../state/index.js';
|
|
11
|
+
import { DEFAULT_DB_CONFIG } from '../../types/database.js';
|
|
12
|
+
import type { DbConfig, DbMode } from '../../types/database.js';
|
|
13
|
+
import { transitionDbStatus } from '../../workflow/db-state-machine.js';
|
|
14
|
+
import {
|
|
15
|
+
runDbSetupPipeline,
|
|
16
|
+
resolveBackendDir,
|
|
17
|
+
} from '../../workflow/db-setup-runner.js';
|
|
18
|
+
import {
|
|
19
|
+
printHeader,
|
|
20
|
+
printSection,
|
|
21
|
+
printSuccess,
|
|
22
|
+
printError,
|
|
23
|
+
printWarning,
|
|
24
|
+
printInfo,
|
|
25
|
+
printKeyValue,
|
|
26
|
+
startSpinner,
|
|
27
|
+
succeedSpinner,
|
|
28
|
+
failSpinner,
|
|
29
|
+
} from '../output.js';
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Prompt the user for a line of input
|
|
33
|
+
*/
|
|
34
|
+
function promptLine(question: string): Promise<string> {
|
|
35
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
36
|
+
return new Promise((resolve) => {
|
|
37
|
+
rl.question(question, (answer) => {
|
|
38
|
+
rl.close();
|
|
39
|
+
resolve(answer.trim());
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Display DB status from project state
|
|
46
|
+
*/
|
|
47
|
+
async function handleDbStatus(directory: string): Promise<void> {
|
|
48
|
+
const projectDir = path.resolve(directory);
|
|
49
|
+
|
|
50
|
+
try {
|
|
51
|
+
const state = await loadProject(projectDir);
|
|
52
|
+
const dbConfig: DbConfig = state.dbConfig || { ...DEFAULT_DB_CONFIG, designed: false };
|
|
53
|
+
|
|
54
|
+
printHeader('Database Status');
|
|
55
|
+
|
|
56
|
+
printKeyValue('Designed', dbConfig.designed ? 'Yes' : 'No');
|
|
57
|
+
printKeyValue('Status', dbConfig.status);
|
|
58
|
+
printKeyValue('Mode', dbConfig.mode || 'not set');
|
|
59
|
+
printKeyValue('Vector Required', dbConfig.vectorRequired ? 'Yes' : 'No');
|
|
60
|
+
printKeyValue('Migrations Applied', String(dbConfig.migrationsApplied));
|
|
61
|
+
|
|
62
|
+
if (dbConfig.lastError) {
|
|
63
|
+
printSection('Last Error');
|
|
64
|
+
printError(dbConfig.lastError);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (dbConfig.readinessCheckedAt) {
|
|
68
|
+
printKeyValue('Last Readiness Check', dbConfig.readinessCheckedAt);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Show next steps based on status
|
|
72
|
+
console.log();
|
|
73
|
+
switch (dbConfig.status) {
|
|
74
|
+
case 'unconfigured':
|
|
75
|
+
printInfo('Run "popeye db configure" to set up database connection.');
|
|
76
|
+
break;
|
|
77
|
+
case 'configured':
|
|
78
|
+
printInfo('Run "popeye db apply" to apply migrations and finalize setup.');
|
|
79
|
+
break;
|
|
80
|
+
case 'error':
|
|
81
|
+
printWarning('Database setup failed. Run "popeye db apply" to retry.');
|
|
82
|
+
break;
|
|
83
|
+
case 'ready':
|
|
84
|
+
printSuccess('Database is ready.');
|
|
85
|
+
break;
|
|
86
|
+
case 'applying':
|
|
87
|
+
printInfo('Database setup is in progress...');
|
|
88
|
+
break;
|
|
89
|
+
}
|
|
90
|
+
} catch (error) {
|
|
91
|
+
printError(error instanceof Error ? error.message : 'Failed to load project');
|
|
92
|
+
process.exit(1);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Configure database mode and connection URL
|
|
98
|
+
*/
|
|
99
|
+
async function handleDbConfigure(directory: string): Promise<void> {
|
|
100
|
+
const projectDir = path.resolve(directory);
|
|
101
|
+
|
|
102
|
+
try {
|
|
103
|
+
const state = await loadProject(projectDir);
|
|
104
|
+
const dbConfig: DbConfig = state.dbConfig || { ...DEFAULT_DB_CONFIG };
|
|
105
|
+
|
|
106
|
+
printHeader('Database Configuration');
|
|
107
|
+
|
|
108
|
+
// Prompt for mode
|
|
109
|
+
console.log();
|
|
110
|
+
console.log(' Choose database mode:');
|
|
111
|
+
console.log(' 1. local_docker - PostgreSQL via Docker Compose (recommended for dev)');
|
|
112
|
+
console.log(' 2. managed - External managed database (Neon, Supabase, etc.)');
|
|
113
|
+
console.log();
|
|
114
|
+
|
|
115
|
+
const modeChoice = await promptLine(' Enter choice [1-2]: ');
|
|
116
|
+
let mode: DbMode;
|
|
117
|
+
if (modeChoice === '2' || modeChoice === 'managed') {
|
|
118
|
+
mode = 'managed';
|
|
119
|
+
} else {
|
|
120
|
+
mode = 'local_docker';
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
printKeyValue('Mode', mode);
|
|
124
|
+
|
|
125
|
+
// For managed mode, prompt for DATABASE_URL
|
|
126
|
+
if (mode === 'managed') {
|
|
127
|
+
const dbUrl = await promptLine(' Enter DATABASE_URL: ');
|
|
128
|
+
|
|
129
|
+
if (!dbUrl) {
|
|
130
|
+
printError('DATABASE_URL is required for managed mode.');
|
|
131
|
+
process.exit(1);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Write DATABASE_URL to apps/backend/.env
|
|
135
|
+
const backendDir = resolveBackendDir(projectDir);
|
|
136
|
+
const envPath = path.join(backendDir, '.env');
|
|
137
|
+
|
|
138
|
+
let envContent = '';
|
|
139
|
+
try {
|
|
140
|
+
envContent = await fsPromises.readFile(envPath, 'utf-8');
|
|
141
|
+
} catch {
|
|
142
|
+
// File doesn't exist yet
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Replace or add DATABASE_URL
|
|
146
|
+
if (envContent.includes('DATABASE_URL=')) {
|
|
147
|
+
envContent = envContent.replace(/DATABASE_URL=.*/, `DATABASE_URL=${dbUrl}`);
|
|
148
|
+
} else {
|
|
149
|
+
envContent += `\nDATABASE_URL=${dbUrl}\n`;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
await fsPromises.writeFile(envPath, envContent, 'utf-8');
|
|
153
|
+
printSuccess(`DATABASE_URL written to ${envPath}`);
|
|
154
|
+
} else {
|
|
155
|
+
printInfo('Local Docker mode: PostgreSQL starts with "docker-compose up".');
|
|
156
|
+
printInfo('DATABASE_URL is set automatically in docker-compose.yml.');
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Update state: transition to configured
|
|
160
|
+
const newStatus = transitionDbStatus(dbConfig.status, 'configured');
|
|
161
|
+
await updateState(projectDir, {
|
|
162
|
+
dbConfig: {
|
|
163
|
+
...dbConfig,
|
|
164
|
+
mode,
|
|
165
|
+
status: newStatus,
|
|
166
|
+
},
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
printSuccess('Database configured successfully.');
|
|
170
|
+
printInfo('Run "popeye db apply" to apply migrations.');
|
|
171
|
+
} catch (error) {
|
|
172
|
+
printError(error instanceof Error ? error.message : 'Configuration failed');
|
|
173
|
+
process.exit(1);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Apply database setup (migrations, extensions, readiness)
|
|
179
|
+
*/
|
|
180
|
+
async function handleDbApply(directory: string, options: { skipSeed?: boolean }): Promise<void> {
|
|
181
|
+
const projectDir = path.resolve(directory);
|
|
182
|
+
|
|
183
|
+
try {
|
|
184
|
+
const state = await loadProject(projectDir);
|
|
185
|
+
const dbConfig: DbConfig = state.dbConfig || { ...DEFAULT_DB_CONFIG };
|
|
186
|
+
|
|
187
|
+
printHeader('Database Setup');
|
|
188
|
+
|
|
189
|
+
// Transition to applying
|
|
190
|
+
let currentStatus = dbConfig.status;
|
|
191
|
+
if (currentStatus === 'unconfigured') {
|
|
192
|
+
printError('Database not configured. Run "popeye db configure" first.');
|
|
193
|
+
process.exit(1);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
startSpinner('Running database setup pipeline...');
|
|
197
|
+
|
|
198
|
+
const result = await runDbSetupPipeline(projectDir, {
|
|
199
|
+
skipSeed: options.skipSeed,
|
|
200
|
+
onStep: (step, status, message) => {
|
|
201
|
+
if (status === 'start') {
|
|
202
|
+
startSpinner(`[${step}] ${message}`);
|
|
203
|
+
} else if (status === 'success') {
|
|
204
|
+
succeedSpinner(`[${step}] ${message}`);
|
|
205
|
+
} else {
|
|
206
|
+
failSpinner(`[${step}] ${message}`);
|
|
207
|
+
}
|
|
208
|
+
},
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
// Print summary
|
|
212
|
+
console.log();
|
|
213
|
+
printSection('Setup Summary');
|
|
214
|
+
for (const step of result.steps) {
|
|
215
|
+
const icon = step.success ? ' [PASS]' : ' [FAIL]';
|
|
216
|
+
const duration = `(${step.durationMs}ms)`;
|
|
217
|
+
if (step.success) {
|
|
218
|
+
printSuccess(`${icon} ${step.step} ${duration}`);
|
|
219
|
+
} else {
|
|
220
|
+
printError(`${icon} ${step.step} ${duration}`);
|
|
221
|
+
if (step.error) {
|
|
222
|
+
printError(` ${step.error}`);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
printKeyValue('Total Duration', `${result.totalDurationMs}ms`);
|
|
228
|
+
|
|
229
|
+
// Update state with result
|
|
230
|
+
const newStatus = result.success ? 'ready' : 'error';
|
|
231
|
+
const now = new Date().toISOString();
|
|
232
|
+
await updateState(projectDir, {
|
|
233
|
+
dbConfig: {
|
|
234
|
+
...dbConfig,
|
|
235
|
+
status: newStatus as DbConfig['status'],
|
|
236
|
+
lastError: result.error,
|
|
237
|
+
readinessCheckedAt: result.success ? now : dbConfig.readinessCheckedAt,
|
|
238
|
+
},
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
if (result.success) {
|
|
242
|
+
console.log();
|
|
243
|
+
printSuccess('Database setup complete. Status: READY');
|
|
244
|
+
} else {
|
|
245
|
+
console.log();
|
|
246
|
+
printError(`Database setup failed: ${result.error}`);
|
|
247
|
+
printInfo('Fix the issue and run "popeye db apply" to retry.');
|
|
248
|
+
process.exit(1);
|
|
249
|
+
}
|
|
250
|
+
} catch (error) {
|
|
251
|
+
failSpinner('Setup failed');
|
|
252
|
+
printError(error instanceof Error ? error.message : 'Unknown error');
|
|
253
|
+
process.exit(1);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Create the db command with subcommands
|
|
259
|
+
*/
|
|
260
|
+
export function createDbCommand(): Command {
|
|
261
|
+
const db = new Command('db')
|
|
262
|
+
.description('Database management commands');
|
|
263
|
+
|
|
264
|
+
db.command('status')
|
|
265
|
+
.description('Show database configuration status')
|
|
266
|
+
.argument('[directory]', 'Project directory', '.')
|
|
267
|
+
.action(handleDbStatus);
|
|
268
|
+
|
|
269
|
+
db.command('configure')
|
|
270
|
+
.description('Configure database mode and connection')
|
|
271
|
+
.argument('[directory]', 'Project directory', '.')
|
|
272
|
+
.action(handleDbConfigure);
|
|
273
|
+
|
|
274
|
+
db.command('apply')
|
|
275
|
+
.description('Apply database setup (migrations, extensions, readiness)')
|
|
276
|
+
.argument('[directory]', 'Project directory', '.')
|
|
277
|
+
.option('--skip-seed', 'Skip seed step')
|
|
278
|
+
.action(handleDbApply);
|
|
279
|
+
|
|
280
|
+
return db;
|
|
281
|
+
}
|
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Doctor command
|
|
3
|
+
* Runs comprehensive readiness checks on database and project health
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { Command } from 'commander';
|
|
7
|
+
import path from 'node:path';
|
|
8
|
+
import { promises as fsPromises } from 'node:fs';
|
|
9
|
+
import { loadProject } from '../../state/index.js';
|
|
10
|
+
import { DEFAULT_DB_CONFIG } from '../../types/database.js';
|
|
11
|
+
import type { ReadinessCheck, ReadinessResult } from '../../types/database-runtime.js';
|
|
12
|
+
import {
|
|
13
|
+
readEnvFile,
|
|
14
|
+
resolveBackendDir,
|
|
15
|
+
} from '../../workflow/db-setup-runner.js';
|
|
16
|
+
import {
|
|
17
|
+
printHeader,
|
|
18
|
+
printSuccess,
|
|
19
|
+
printError,
|
|
20
|
+
printWarning,
|
|
21
|
+
printInfo,
|
|
22
|
+
} from '../output.js';
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Run all readiness checks and return structured results
|
|
26
|
+
*
|
|
27
|
+
* @param projectDir - Project root directory
|
|
28
|
+
* @returns ReadinessResult with all check outcomes
|
|
29
|
+
*/
|
|
30
|
+
export async function runDoctorChecks(projectDir: string): Promise<ReadinessResult> {
|
|
31
|
+
const checks: ReadinessCheck[] = [];
|
|
32
|
+
|
|
33
|
+
// Check 1: Project state exists
|
|
34
|
+
try {
|
|
35
|
+
await loadProject(projectDir);
|
|
36
|
+
checks.push({
|
|
37
|
+
name: 'Project State',
|
|
38
|
+
passed: true,
|
|
39
|
+
message: 'Project state loaded successfully',
|
|
40
|
+
severity: 'critical',
|
|
41
|
+
});
|
|
42
|
+
} catch {
|
|
43
|
+
checks.push({
|
|
44
|
+
name: 'Project State',
|
|
45
|
+
passed: false,
|
|
46
|
+
message: 'No valid project state found at this directory',
|
|
47
|
+
severity: 'critical',
|
|
48
|
+
});
|
|
49
|
+
return { healthy: false, checks, timestamp: new Date().toISOString() };
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const state = await loadProject(projectDir);
|
|
53
|
+
const dbConfig = state.dbConfig || { ...DEFAULT_DB_CONFIG, designed: false };
|
|
54
|
+
const backendDir = resolveBackendDir(projectDir);
|
|
55
|
+
|
|
56
|
+
// Check 2: DB layer generated
|
|
57
|
+
checks.push({
|
|
58
|
+
name: 'DB Layer Generated',
|
|
59
|
+
passed: dbConfig.designed === true,
|
|
60
|
+
message: dbConfig.designed
|
|
61
|
+
? 'Database layer files are present'
|
|
62
|
+
: 'Database layer not generated (dbConfig.designed = false)',
|
|
63
|
+
severity: 'critical',
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
// Check 3: Docker compose includes postgres service
|
|
67
|
+
let composeHasPostgres = false;
|
|
68
|
+
try {
|
|
69
|
+
const composePath = path.join(projectDir, 'docker-compose.yml');
|
|
70
|
+
const composeContent = await fsPromises.readFile(composePath, 'utf-8');
|
|
71
|
+
composeHasPostgres = composeContent.includes('postgres:') && composeContent.includes('pg_isready');
|
|
72
|
+
} catch {
|
|
73
|
+
// File doesn't exist
|
|
74
|
+
}
|
|
75
|
+
checks.push({
|
|
76
|
+
name: 'Docker Compose Postgres',
|
|
77
|
+
passed: composeHasPostgres,
|
|
78
|
+
message: composeHasPostgres
|
|
79
|
+
? 'docker-compose.yml includes postgres service with healthcheck'
|
|
80
|
+
: 'docker-compose.yml missing or does not include postgres service',
|
|
81
|
+
severity: 'warning',
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
// Check 4: .env has DATABASE_URL (not placeholder)
|
|
85
|
+
const env = await readEnvFile(path.join(backendDir, '.env'));
|
|
86
|
+
const dbUrl = env['DATABASE_URL'] || '';
|
|
87
|
+
const hasRealDbUrl = dbUrl.length > 0 && !dbUrl.includes('sqlite');
|
|
88
|
+
checks.push({
|
|
89
|
+
name: 'DATABASE_URL Configured',
|
|
90
|
+
passed: hasRealDbUrl,
|
|
91
|
+
message: hasRealDbUrl
|
|
92
|
+
? 'DATABASE_URL is set to a PostgreSQL connection string'
|
|
93
|
+
: 'DATABASE_URL is missing or still using SQLite placeholder',
|
|
94
|
+
severity: 'critical',
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
// Check 5: DB connection reachable (only if URL is set)
|
|
98
|
+
if (hasRealDbUrl) {
|
|
99
|
+
try {
|
|
100
|
+
const { exec } = await import('node:child_process');
|
|
101
|
+
const { promisify } = await import('node:util');
|
|
102
|
+
const execAsync = promisify(exec);
|
|
103
|
+
const cmd = `cd "${backendDir}" && python3 -c "
|
|
104
|
+
import asyncio, asyncpg
|
|
105
|
+
async def check():
|
|
106
|
+
conn = await asyncpg.connect('${dbUrl.replace(/'/g, "\\'")}')
|
|
107
|
+
await conn.execute('SELECT 1')
|
|
108
|
+
await conn.close()
|
|
109
|
+
asyncio.run(check())
|
|
110
|
+
"`;
|
|
111
|
+
await execAsync(cmd, { timeout: 10000 });
|
|
112
|
+
checks.push({
|
|
113
|
+
name: 'DB Connection',
|
|
114
|
+
passed: true,
|
|
115
|
+
message: 'Database is reachable',
|
|
116
|
+
severity: 'critical',
|
|
117
|
+
});
|
|
118
|
+
} catch {
|
|
119
|
+
checks.push({
|
|
120
|
+
name: 'DB Connection',
|
|
121
|
+
passed: false,
|
|
122
|
+
message: 'Cannot connect to database - check DATABASE_URL and server status',
|
|
123
|
+
severity: 'critical',
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
} else {
|
|
127
|
+
checks.push({
|
|
128
|
+
name: 'DB Connection',
|
|
129
|
+
passed: false,
|
|
130
|
+
message: 'Skipped - DATABASE_URL not configured',
|
|
131
|
+
severity: 'info',
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Check 6: pgvector extension available (only if connected)
|
|
136
|
+
if (hasRealDbUrl && checks.find((c) => c.name === 'DB Connection')?.passed) {
|
|
137
|
+
try {
|
|
138
|
+
const { exec } = await import('node:child_process');
|
|
139
|
+
const { promisify } = await import('node:util');
|
|
140
|
+
const execAsync = promisify(exec);
|
|
141
|
+
const cmd = `cd "${backendDir}" && python3 -c "
|
|
142
|
+
import asyncio, asyncpg
|
|
143
|
+
async def check():
|
|
144
|
+
conn = await asyncpg.connect('${dbUrl.replace(/'/g, "\\'")}')
|
|
145
|
+
row = await conn.fetchrow(\"SELECT extname FROM pg_extension WHERE extname = 'vector'\")
|
|
146
|
+
await conn.close()
|
|
147
|
+
if row is None:
|
|
148
|
+
raise Exception('pgvector extension not found')
|
|
149
|
+
asyncio.run(check())
|
|
150
|
+
"`;
|
|
151
|
+
await execAsync(cmd, { timeout: 10000 });
|
|
152
|
+
checks.push({
|
|
153
|
+
name: 'pgvector Extension',
|
|
154
|
+
passed: true,
|
|
155
|
+
message: 'pgvector extension is installed',
|
|
156
|
+
severity: 'warning',
|
|
157
|
+
});
|
|
158
|
+
} catch {
|
|
159
|
+
checks.push({
|
|
160
|
+
name: 'pgvector Extension',
|
|
161
|
+
passed: false,
|
|
162
|
+
message: 'pgvector extension not available - vector features will be disabled',
|
|
163
|
+
severity: 'warning',
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Check 7: Migrations applied
|
|
169
|
+
if (hasRealDbUrl && checks.find((c) => c.name === 'DB Connection')?.passed) {
|
|
170
|
+
try {
|
|
171
|
+
const { exec } = await import('node:child_process');
|
|
172
|
+
const { promisify } = await import('node:util');
|
|
173
|
+
const execAsync = promisify(exec);
|
|
174
|
+
const cmd = `cd "${backendDir}" && python3 -c "
|
|
175
|
+
import asyncio, asyncpg
|
|
176
|
+
async def check():
|
|
177
|
+
conn = await asyncpg.connect('${dbUrl.replace(/'/g, "\\'")}')
|
|
178
|
+
row = await conn.fetchrow('SELECT version_num FROM alembic_version LIMIT 1')
|
|
179
|
+
await conn.close()
|
|
180
|
+
if row is None:
|
|
181
|
+
raise Exception('No migrations applied')
|
|
182
|
+
asyncio.run(check())
|
|
183
|
+
"`;
|
|
184
|
+
await execAsync(cmd, { timeout: 10000 });
|
|
185
|
+
checks.push({
|
|
186
|
+
name: 'Migrations Applied',
|
|
187
|
+
passed: true,
|
|
188
|
+
message: 'Alembic migrations have been applied',
|
|
189
|
+
severity: 'critical',
|
|
190
|
+
});
|
|
191
|
+
} catch {
|
|
192
|
+
checks.push({
|
|
193
|
+
name: 'Migrations Applied',
|
|
194
|
+
passed: false,
|
|
195
|
+
message: 'No migrations applied - run "popeye db apply" or "alembic upgrade head"',
|
|
196
|
+
severity: 'critical',
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// Check 8: /health/db endpoint (optional - only if server is running)
|
|
202
|
+
try {
|
|
203
|
+
const response = await fetch('http://localhost:8000/health/db', {
|
|
204
|
+
signal: AbortSignal.timeout(3000),
|
|
205
|
+
});
|
|
206
|
+
checks.push({
|
|
207
|
+
name: 'Health Endpoint',
|
|
208
|
+
passed: response.status === 200,
|
|
209
|
+
message: response.status === 200
|
|
210
|
+
? '/health/db returns 200 OK'
|
|
211
|
+
: `/health/db returns ${response.status}`,
|
|
212
|
+
severity: 'info',
|
|
213
|
+
});
|
|
214
|
+
} catch {
|
|
215
|
+
checks.push({
|
|
216
|
+
name: 'Health Endpoint',
|
|
217
|
+
passed: false,
|
|
218
|
+
message: 'Backend server not running (optional check)',
|
|
219
|
+
severity: 'info',
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
const healthy = checks
|
|
224
|
+
.filter((c) => c.severity === 'critical')
|
|
225
|
+
.every((c) => c.passed);
|
|
226
|
+
|
|
227
|
+
return {
|
|
228
|
+
healthy,
|
|
229
|
+
checks,
|
|
230
|
+
timestamp: new Date().toISOString(),
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Create the doctor command
|
|
236
|
+
*/
|
|
237
|
+
export function createDoctorCommand(): Command {
|
|
238
|
+
const doctor = new Command('doctor')
|
|
239
|
+
.description('Run comprehensive database and project readiness checks')
|
|
240
|
+
.argument('[directory]', 'Project directory', '.')
|
|
241
|
+
.action(async (directory: string) => {
|
|
242
|
+
const projectDir = path.resolve(directory);
|
|
243
|
+
|
|
244
|
+
printHeader('Popeye Doctor');
|
|
245
|
+
console.log();
|
|
246
|
+
|
|
247
|
+
const result = await runDoctorChecks(projectDir);
|
|
248
|
+
|
|
249
|
+
for (const check of result.checks) {
|
|
250
|
+
const statusLabel = check.passed ? '[PASS]' : check.severity === 'info' ? '[SKIP]' : '[FAIL]';
|
|
251
|
+
|
|
252
|
+
if (check.passed) {
|
|
253
|
+
printSuccess(` ${statusLabel} ${check.name}: ${check.message}`);
|
|
254
|
+
} else if (check.severity === 'info') {
|
|
255
|
+
printInfo(` ${statusLabel} ${check.name}: ${check.message}`);
|
|
256
|
+
} else if (check.severity === 'warning') {
|
|
257
|
+
printWarning(` ${statusLabel} ${check.name}: ${check.message}`);
|
|
258
|
+
} else {
|
|
259
|
+
printError(` ${statusLabel} ${check.name}: ${check.message}`);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
console.log();
|
|
264
|
+
if (result.healthy) {
|
|
265
|
+
printSuccess('All critical checks passed. Database is healthy.');
|
|
266
|
+
} else {
|
|
267
|
+
printError('Some critical checks failed. Fix the issues above and re-run.');
|
|
268
|
+
process.exit(1);
|
|
269
|
+
}
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
return doctor;
|
|
273
|
+
}
|
|
@@ -8,3 +8,5 @@ export { createCreateCommand } from './create.js';
|
|
|
8
8
|
export { createStatusCommand, createValidateCommand, createSummaryCommand } from './status.js';
|
|
9
9
|
export { createResumeCommand, createResetCommand, createCancelCommand } from './resume.js';
|
|
10
10
|
export { createConfigCommand } from './config.js';
|
|
11
|
+
export { createDbCommand } from './db.js';
|
|
12
|
+
export { createDoctorCommand } from './doctor.js';
|
package/src/cli/index.ts
CHANGED
|
@@ -15,6 +15,8 @@ import {
|
|
|
15
15
|
createResetCommand,
|
|
16
16
|
createCancelCommand,
|
|
17
17
|
createConfigCommand,
|
|
18
|
+
createDbCommand,
|
|
19
|
+
createDoctorCommand,
|
|
18
20
|
} from './commands/index.js';
|
|
19
21
|
import { startInteractiveMode } from './interactive.js';
|
|
20
22
|
import { printError } from './output.js';
|
|
@@ -54,6 +56,8 @@ export function createProgram(): Command {
|
|
|
54
56
|
program.addCommand(createResetCommand());
|
|
55
57
|
program.addCommand(createCancelCommand());
|
|
56
58
|
program.addCommand(createConfigCommand());
|
|
59
|
+
program.addCommand(createDbCommand());
|
|
60
|
+
program.addCommand(createDoctorCommand());
|
|
57
61
|
|
|
58
62
|
// Interactive mode command
|
|
59
63
|
program
|