feat(cli): improve error messaging for deploy command failures

- Extract meaningful error messages from verbose SQL update failures
- Show cleaner error messages by default (without SQL query details)
- Add --verbose flag support to show full error details when needed
- Limit default error display to first 5 failures to avoid overwhelming output
- Add helpful tips to guide users when deployment errors occur
This commit is contained in:
dal 2025-09-03 15:21:05 -06:00
parent 925cb6e269
commit 551ef41f8c
No known key found for this signature in database
GPG Key ID: 16F4B0E1E9F61122
2 changed files with 84 additions and 4 deletions

View File

@ -45,7 +45,7 @@ export async function deployHandler(options: DeployOptions): Promise<CLIDeployme
const finalResult = mergeDeploymentResults(projectResults);
// 6. Display summary
const summary = formatDeploymentSummary(finalResult);
const summary = formatDeploymentSummary(finalResult, options.verbose);
console.info(`\n${summary}`);
return finalResult;

View File

@ -80,7 +80,7 @@ export function processDeploymentResponse(
/**
* Pure function to format deployment summary for display
*/
export function formatDeploymentSummary(result: CLIDeploymentResult): string {
export function formatDeploymentSummary(result: CLIDeploymentResult, verbose = false): string {
const lines: string[] = [];
lines.push('📊 Deployment Summary');
@ -109,11 +109,31 @@ export function formatDeploymentSummary(result: CLIDeploymentResult): string {
lines.push(`❌ Failed: ${result.failures.length} models`);
lines.push('-'.repeat(40));
for (const failure of result.failures) {
// Show first 5 failures in detail, then summarize the rest
const maxDetailedFailures = verbose ? result.failures.length : 5;
const detailedFailures = result.failures.slice(0, maxDetailedFailures);
const remainingCount = result.failures.length - maxDetailedFailures;
for (const failure of detailedFailures) {
lines.push('');
lines.push(` File: ${failure.file}`);
lines.push(` Model: ${failure.modelName}`);
for (const error of failure.errors) {
lines.push(` - ${error}`);
if (verbose) {
// Show full error in verbose mode
lines.push(` • Full error: ${error}`);
} else {
// Extract meaningful error message from verbose SQL errors
const cleanedError = extractErrorMessage(error);
lines.push(`${cleanedError}`);
}
}
}
if (remainingCount > 0) {
lines.push(`...and ${remainingCount} more failures`);
if (!verbose) {
lines.push('(Run with --verbose to see all error details)');
}
}
}
@ -123,12 +143,72 @@ export function formatDeploymentSummary(result: CLIDeploymentResult): string {
if (result.failures.length === 0) {
lines.push('🎉 All models processed successfully!');
} else {
lines.push('');
lines.push('⚠️ Some models failed to deploy. Please check the errors above.');
if (!verbose && result.failures.length > 0) {
lines.push('💡 Tip: Run with --verbose flag to see full error details');
}
}
return lines.join('\n');
}
/**
* Extract meaningful error message from verbose database errors
*/
function extractErrorMessage(error: string): string {
// Handle SQL update errors with large parameter lists
if (error.includes('Failed query:')) {
// Try to extract just the error reason after the params
const paramsMatch = error.match(/params:.*?([A-Z][^,]*error[^,]*)/i);
if (paramsMatch && paramsMatch[1]) {
return paramsMatch[1].trim();
}
// Look for common database error patterns
if (error.includes('duplicate key')) {
return 'Duplicate key error - model may already exist';
}
if (error.includes('foreign key')) {
return 'Foreign key constraint violation';
}
if (error.includes('not null')) {
return 'Required field is missing (NOT NULL constraint)';
}
if (error.includes('unique constraint')) {
return 'Unique constraint violation';
}
if (error.includes('syntax error')) {
return 'SQL syntax error in model definition';
}
if (error.includes('permission denied')) {
return 'Permission denied for database operation';
}
if (error.includes('connection')) {
return 'Database connection error';
}
// If we can't extract a specific error, show a generic message
return 'Database update failed - check model definition and permissions';
}
// For non-SQL errors, truncate if too long
if (error.length > 200) {
// Try to find the actual error message part
const errorParts = error.split(':');
if (errorParts.length > 1) {
// Return the last meaningful part (often the actual error)
const lastPart = errorParts[errorParts.length - 1];
if (lastPart) {
return lastPart.trim().substring(0, 150) + '...';
}
}
return error.substring(0, 150) + '...';
}
return error;
}
/**
* Pure function to create parse failure entries
*/