@friggframework/devtools 2.0.0--canary.549.70ef06a.0 → 2.0.0--canary.545.ccb5010.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/frigg-cli/README.md +1 -1
- package/frigg-cli/__tests__/application/use-cases/AddApiModuleToIntegrationUseCase.test.js +326 -0
- package/frigg-cli/__tests__/application/use-cases/CreateApiModuleUseCase.test.js +337 -0
- package/frigg-cli/__tests__/domain/entities/ApiModule.test.js +373 -0
- package/frigg-cli/__tests__/domain/entities/AppDefinition.test.js +313 -0
- package/frigg-cli/__tests__/domain/services/IntegrationValidator.test.js +269 -0
- package/frigg-cli/__tests__/domain/value-objects/IntegrationName.test.js +82 -0
- package/frigg-cli/__tests__/infrastructure/adapters/IntegrationJsUpdater.test.js +408 -0
- package/frigg-cli/__tests__/infrastructure/repositories/FileSystemApiModuleRepository.test.js +583 -0
- package/frigg-cli/__tests__/infrastructure/repositories/FileSystemAppDefinitionRepository.test.js +314 -0
- package/frigg-cli/__tests__/infrastructure/repositories/FileSystemIntegrationRepository.test.js +383 -0
- package/frigg-cli/__tests__/unit/commands/build.test.js +1 -1
- package/frigg-cli/__tests__/unit/commands/doctor.test.js +0 -2
- package/frigg-cli/__tests__/unit/commands/init.test.js +406 -0
- package/frigg-cli/__tests__/unit/commands/install.test.js +23 -19
- package/frigg-cli/__tests__/unit/commands/provider-dispatch.test.js +383 -0
- package/frigg-cli/__tests__/unit/commands/repair.test.js +275 -0
- package/frigg-cli/__tests__/unit/dependencies.test.js +2 -2
- package/frigg-cli/__tests__/unit/start-command/application/RunPreflightChecksUseCase.test.js +411 -0
- package/frigg-cli/__tests__/unit/start-command/infrastructure/DatabaseAdapter.test.js +405 -0
- package/frigg-cli/__tests__/unit/start-command/infrastructure/DockerAdapter.test.js +496 -0
- package/frigg-cli/__tests__/unit/start-command/presentation/InteractivePromptAdapter.test.js +474 -0
- package/frigg-cli/__tests__/unit/utils/output.test.js +196 -0
- package/frigg-cli/application/use-cases/AddApiModuleToIntegrationUseCase.js +93 -0
- package/frigg-cli/application/use-cases/CreateApiModuleUseCase.js +93 -0
- package/frigg-cli/application/use-cases/CreateIntegrationUseCase.js +103 -0
- package/frigg-cli/build-command/index.js +123 -11
- package/frigg-cli/container.js +172 -0
- package/frigg-cli/deploy-command/index.js +83 -1
- package/frigg-cli/docs/OUTPUT_MIGRATION_GUIDE.md +286 -0
- package/frigg-cli/doctor-command/index.js +37 -16
- package/frigg-cli/domain/entities/ApiModule.js +272 -0
- package/frigg-cli/domain/entities/AppDefinition.js +227 -0
- package/frigg-cli/domain/entities/Integration.js +198 -0
- package/frigg-cli/domain/exceptions/DomainException.js +24 -0
- package/frigg-cli/domain/ports/IApiModuleRepository.js +53 -0
- package/frigg-cli/domain/ports/IAppDefinitionRepository.js +43 -0
- package/frigg-cli/domain/ports/IIntegrationRepository.js +61 -0
- package/frigg-cli/domain/services/IntegrationValidator.js +185 -0
- package/frigg-cli/domain/value-objects/IntegrationId.js +42 -0
- package/frigg-cli/domain/value-objects/IntegrationName.js +60 -0
- package/frigg-cli/domain/value-objects/SemanticVersion.js +70 -0
- package/frigg-cli/generate-iam-command.js +21 -1
- package/frigg-cli/index.js +21 -6
- package/frigg-cli/index.test.js +7 -2
- package/frigg-cli/infrastructure/UnitOfWork.js +46 -0
- package/frigg-cli/infrastructure/adapters/BackendJsUpdater.js +197 -0
- package/frigg-cli/infrastructure/adapters/FileSystemAdapter.js +224 -0
- package/frigg-cli/infrastructure/adapters/IntegrationJsUpdater.js +249 -0
- package/frigg-cli/infrastructure/adapters/SchemaValidator.js +92 -0
- package/frigg-cli/infrastructure/repositories/FileSystemApiModuleRepository.js +373 -0
- package/frigg-cli/infrastructure/repositories/FileSystemAppDefinitionRepository.js +116 -0
- package/frigg-cli/infrastructure/repositories/FileSystemIntegrationRepository.js +277 -0
- package/frigg-cli/init-command/backend-first-handler.js +124 -42
- package/frigg-cli/init-command/index.js +2 -1
- package/frigg-cli/init-command/template-handler.js +13 -3
- package/frigg-cli/install-command/backend-js.js +3 -3
- package/frigg-cli/install-command/environment-variables.js +16 -19
- package/frigg-cli/install-command/environment-variables.test.js +12 -13
- package/frigg-cli/install-command/index.js +14 -9
- package/frigg-cli/install-command/integration-file.js +3 -3
- package/frigg-cli/install-command/validate-package.js +5 -9
- package/frigg-cli/jest.config.js +4 -1
- package/frigg-cli/package-lock.json +16226 -0
- package/frigg-cli/repair-command/index.js +121 -128
- package/frigg-cli/start-command/application/RunPreflightChecksUseCase.js +376 -0
- package/frigg-cli/start-command/index.js +324 -2
- package/frigg-cli/start-command/infrastructure/DatabaseAdapter.js +591 -0
- package/frigg-cli/start-command/infrastructure/DockerAdapter.js +306 -0
- package/frigg-cli/start-command/presentation/InteractivePromptAdapter.js +329 -0
- package/frigg-cli/templates/backend/.env.example +62 -0
- package/frigg-cli/templates/backend/.eslintrc.json +12 -0
- package/frigg-cli/templates/backend/.prettierrc +6 -0
- package/frigg-cli/templates/backend/docker-compose.yml +22 -0
- package/frigg-cli/templates/backend/index.js +96 -0
- package/frigg-cli/templates/backend/infrastructure.js +12 -0
- package/frigg-cli/templates/backend/jest.config.js +17 -0
- package/frigg-cli/templates/backend/package.json +50 -0
- package/frigg-cli/templates/backend/src/api-modules/.gitkeep +10 -0
- package/frigg-cli/templates/backend/src/base/.gitkeep +7 -0
- package/frigg-cli/templates/backend/src/integrations/.gitkeep +10 -0
- package/frigg-cli/templates/backend/src/integrations/ExampleIntegration.js +65 -0
- package/frigg-cli/templates/backend/src/utils/.gitkeep +7 -0
- package/frigg-cli/templates/backend/test/setup.js +30 -0
- package/frigg-cli/templates/backend/ui-extensions/.gitkeep +0 -0
- package/frigg-cli/templates/backend/ui-extensions/README.md +77 -0
- package/frigg-cli/ui-command/index.js +58 -36
- package/frigg-cli/utils/__tests__/provider-helper.test.js +55 -0
- package/frigg-cli/utils/__tests__/repo-detection.test.js +436 -0
- package/frigg-cli/utils/output.js +382 -0
- package/frigg-cli/utils/provider-helper.js +75 -0
- package/frigg-cli/utils/repo-detection.js +85 -37
- package/frigg-cli/validate-command/__tests__/adapters/validate-command.test.js +205 -0
- package/frigg-cli/validate-command/__tests__/application/validate-app-use-case.test.js +104 -0
- package/frigg-cli/validate-command/__tests__/domain/fix-suggestion.test.js +153 -0
- package/frigg-cli/validate-command/__tests__/domain/validation-error.test.js +162 -0
- package/frigg-cli/validate-command/__tests__/domain/validation-result.test.js +152 -0
- package/frigg-cli/validate-command/__tests__/infrastructure/api-module-validator.test.js +332 -0
- package/frigg-cli/validate-command/__tests__/infrastructure/app-definition-validator.test.js +191 -0
- package/frigg-cli/validate-command/__tests__/infrastructure/integration-class-validator.test.js +146 -0
- package/frigg-cli/validate-command/__tests__/infrastructure/template-validation.test.js +155 -0
- package/frigg-cli/validate-command/adapters/cli/validate-command.js +199 -0
- package/frigg-cli/validate-command/application/use-cases/validate-app-use-case.js +35 -0
- package/frigg-cli/validate-command/domain/entities/validation-result.js +74 -0
- package/frigg-cli/validate-command/domain/value-objects/fix-suggestion.js +74 -0
- package/frigg-cli/validate-command/domain/value-objects/validation-error.js +68 -0
- package/frigg-cli/validate-command/infrastructure/validators/api-module-validator.js +181 -0
- package/frigg-cli/validate-command/infrastructure/validators/app-definition-validator.js +145 -0
- package/frigg-cli/validate-command/infrastructure/validators/integration-class-validator.js +113 -0
- package/infrastructure/create-frigg-infrastructure.js +93 -0
- package/infrastructure/docs/iam-policy-templates.md +1 -1
- package/infrastructure/domains/admin-scripts/admin-script-builder.js +200 -0
- package/infrastructure/domains/admin-scripts/admin-script-builder.test.js +499 -0
- package/infrastructure/domains/admin-scripts/index.js +5 -0
- package/infrastructure/domains/networking/vpc-builder.test.js +2 -4
- package/infrastructure/domains/networking/vpc-resolver.test.js +1 -1
- package/infrastructure/domains/shared/resource-discovery.js +5 -5
- package/infrastructure/domains/shared/types/app-definition.js +35 -0
- package/infrastructure/domains/shared/types/discovery-result.test.js +1 -1
- package/infrastructure/domains/shared/utilities/base-definition-factory.js +10 -1
- package/infrastructure/domains/shared/utilities/base-definition-factory.test.js +2 -2
- package/infrastructure/infrastructure-composer.js +2 -0
- package/infrastructure/infrastructure-composer.test.js +2 -2
- package/infrastructure/jest.config.js +16 -0
- package/management-ui/README.md +245 -109
- package/package.json +8 -7
- package/frigg-cli/install-command/logger.js +0 -12
|
@@ -268,9 +268,17 @@ async function runPostDeploymentHealthCheck(stackName, options) {
|
|
|
268
268
|
}
|
|
269
269
|
|
|
270
270
|
async function deployCommand(options) {
|
|
271
|
+
const appDefinition = loadAppDefinition();
|
|
272
|
+
|
|
273
|
+
// Check if the app uses a non-AWS provider
|
|
274
|
+
const providerResult = loadProviderIfConfigured(appDefinition);
|
|
275
|
+
if (providerResult) {
|
|
276
|
+
return deployWithProvider(providerResult, options);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// Default: AWS deployment via serverless framework
|
|
271
280
|
console.log('Deploying the serverless application...');
|
|
272
281
|
|
|
273
|
-
const appDefinition = loadAppDefinition();
|
|
274
282
|
const environment = validateAndBuildEnvironment(appDefinition, options);
|
|
275
283
|
|
|
276
284
|
// Execute deployment
|
|
@@ -302,4 +310,78 @@ async function deployCommand(options) {
|
|
|
302
310
|
}
|
|
303
311
|
}
|
|
304
312
|
|
|
313
|
+
/**
|
|
314
|
+
* Check if the appDefinition specifies a non-AWS provider and resolve it.
|
|
315
|
+
* Returns null for AWS (default) so the caller falls through to existing behavior.
|
|
316
|
+
*
|
|
317
|
+
* @param {Object|null} appDefinition
|
|
318
|
+
* @returns {{ provider: Object, appDefinition: Object, providerName: string } | null}
|
|
319
|
+
*/
|
|
320
|
+
function loadProviderIfConfigured(appDefinition) {
|
|
321
|
+
const providerName = appDefinition?.provider;
|
|
322
|
+
if (!providerName || providerName === 'aws') {
|
|
323
|
+
return null;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
try {
|
|
327
|
+
const { resolveProvider } = require('@friggframework/core/providers/resolve-provider');
|
|
328
|
+
const provider = resolveProvider(appDefinition);
|
|
329
|
+
return { provider, appDefinition, providerName };
|
|
330
|
+
} catch (error) {
|
|
331
|
+
console.error(`Failed to load provider '${providerName}': ${error.message}`);
|
|
332
|
+
process.exit(1);
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* Deploy using a provider plugin (Netlify, etc.).
|
|
338
|
+
* Delegates entirely to the provider's deploy lifecycle:
|
|
339
|
+
* 1. validate() — check appDefinition for provider-specific issues
|
|
340
|
+
* 2. preflightCheck() — verify prerequisites (CLI tools, credentials)
|
|
341
|
+
* 3. deploy() — execute the deployment
|
|
342
|
+
*/
|
|
343
|
+
async function deployWithProvider({ provider, appDefinition, providerName }, options) {
|
|
344
|
+
console.log(`Deploying with ${providerName} provider...`);
|
|
345
|
+
|
|
346
|
+
// 1. Validate appDefinition for this provider
|
|
347
|
+
if (typeof provider.validate === 'function') {
|
|
348
|
+
const validation = provider.validate(appDefinition);
|
|
349
|
+
if (validation.errors?.length > 0) {
|
|
350
|
+
console.error(`\nValidation errors for ${providerName}:`);
|
|
351
|
+
for (const error of validation.errors) {
|
|
352
|
+
console.error(` - ${error}`);
|
|
353
|
+
}
|
|
354
|
+
process.exit(1);
|
|
355
|
+
}
|
|
356
|
+
if (validation.warnings?.length > 0) {
|
|
357
|
+
for (const warning of validation.warnings) {
|
|
358
|
+
console.warn(` Warning: ${warning}`);
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
// 2. Deploy via provider
|
|
364
|
+
try {
|
|
365
|
+
const result = await provider.deploy(appDefinition, {
|
|
366
|
+
stage: options.stage,
|
|
367
|
+
prod: options.stage === 'production' || options.stage === 'prod',
|
|
368
|
+
dryRun: options.dryRun || false,
|
|
369
|
+
});
|
|
370
|
+
|
|
371
|
+
console.log(`\n✓ Deployment completed successfully!`);
|
|
372
|
+
if (result.url) {
|
|
373
|
+
console.log(` URL: ${result.url}`);
|
|
374
|
+
}
|
|
375
|
+
} catch (error) {
|
|
376
|
+
console.error(`\n✗ Deployment failed: ${error.message}`);
|
|
377
|
+
if (error.missing) {
|
|
378
|
+
console.error(' Missing prerequisites:');
|
|
379
|
+
for (const item of error.missing) {
|
|
380
|
+
console.error(` - ${item}`);
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
process.exit(1);
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
|
|
305
387
|
module.exports = { deployCommand };
|
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
# Output Class Migration Guide
|
|
2
|
+
|
|
3
|
+
This guide shows how to migrate existing CLI commands to use the new unified `Output` class.
|
|
4
|
+
|
|
5
|
+
## Why Migrate?
|
|
6
|
+
|
|
7
|
+
The unified `Output` class provides:
|
|
8
|
+
- **Consistency**: Same UI patterns across all commands
|
|
9
|
+
- **Better UX**: Spinners, progress bars, formatted tables
|
|
10
|
+
- **Maintainability**: Single place to update UI behavior
|
|
11
|
+
- **Testing**: Easier to mock and test
|
|
12
|
+
|
|
13
|
+
## Before vs After
|
|
14
|
+
|
|
15
|
+
### Before (Inconsistent)
|
|
16
|
+
|
|
17
|
+
```javascript
|
|
18
|
+
// install-command/index.js (OLD - plain console.log)
|
|
19
|
+
function logError(message, error) {
|
|
20
|
+
console.error(message, error);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function logSuccess(message) {
|
|
24
|
+
console.log(message);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Some other command (OLD - using chalk directly)
|
|
28
|
+
const chalk = require('chalk');
|
|
29
|
+
console.log(chalk.green('✓ Success'));
|
|
30
|
+
console.error(chalk.red('✗ Failed'));
|
|
31
|
+
|
|
32
|
+
// repair-command (OLD - using readline)
|
|
33
|
+
const readline = require('readline');
|
|
34
|
+
const rl = readline.createInterface({
|
|
35
|
+
input: process.stdin,
|
|
36
|
+
output: process.stdout
|
|
37
|
+
});
|
|
38
|
+
rl.question('Select option: ', (answer) => {
|
|
39
|
+
// ...
|
|
40
|
+
});
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### After (Consistent)
|
|
44
|
+
|
|
45
|
+
```javascript
|
|
46
|
+
// All commands use the same output utility
|
|
47
|
+
const output = require('./utils/output');
|
|
48
|
+
|
|
49
|
+
output.success('Operation completed');
|
|
50
|
+
output.error('Operation failed', error);
|
|
51
|
+
|
|
52
|
+
const spinner = output.spinner('Installing...');
|
|
53
|
+
// do work
|
|
54
|
+
spinner.succeed('Installed successfully');
|
|
55
|
+
|
|
56
|
+
const answer = await output.confirm('Continue with installation?');
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Migration Steps
|
|
60
|
+
|
|
61
|
+
### Step 1: Import Output
|
|
62
|
+
|
|
63
|
+
Replace all imports of `chalk`, `console`, and `readline`:
|
|
64
|
+
|
|
65
|
+
```diff
|
|
66
|
+
- const chalk = require('chalk');
|
|
67
|
+
- const readline = require('readline');
|
|
68
|
+
+ const output = require('../utils/output');
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Step 2: Replace Console Methods
|
|
72
|
+
|
|
73
|
+
| Old | New |
|
|
74
|
+
|-----|-----|
|
|
75
|
+
| `console.log('Success')` | `output.success('Success')` |
|
|
76
|
+
| `console.error('Error')` | `output.error('Error')` |
|
|
77
|
+
| `console.info('Info')` | `output.info('Info')` |
|
|
78
|
+
| `console.warn('Warning')` | `output.warn('Warning')` |
|
|
79
|
+
| `console.log(chalk.green('✓ Done'))` | `output.success('Done')` |
|
|
80
|
+
| `console.error(chalk.red('✗ Failed'))` | `output.error('Failed')` |
|
|
81
|
+
|
|
82
|
+
### Step 3: Replace Chalk Usage
|
|
83
|
+
|
|
84
|
+
```diff
|
|
85
|
+
- console.log(chalk.blue('Starting...'));
|
|
86
|
+
+ output.info('Starting...');
|
|
87
|
+
|
|
88
|
+
- console.log(chalk.bold('=== Header ==='));
|
|
89
|
+
+ output.header('Header');
|
|
90
|
+
|
|
91
|
+
- console.log(chalk.yellow('⚠ Warning'));
|
|
92
|
+
+ output.warn('Warning');
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### Step 4: Replace Inquirer Prompts
|
|
96
|
+
|
|
97
|
+
```diff
|
|
98
|
+
- const { confirm } = require('@inquirer/prompts');
|
|
99
|
+
- const answer = await confirm({ message: 'Continue?' });
|
|
100
|
+
+ const answer = await output.confirm('Continue?');
|
|
101
|
+
|
|
102
|
+
- const { select } = require('@inquirer/prompts');
|
|
103
|
+
- const choice = await select({
|
|
104
|
+
- message: 'Select option',
|
|
105
|
+
- choices: ['Option 1', 'Option 2']
|
|
106
|
+
- });
|
|
107
|
+
+ const choice = await output.select('Select option', ['Option 1', 'Option 2']);
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### Step 5: Replace Readline (repair-command)
|
|
111
|
+
|
|
112
|
+
```diff
|
|
113
|
+
- const readline = require('readline');
|
|
114
|
+
- const rl = readline.createInterface({
|
|
115
|
+
- input: process.stdin,
|
|
116
|
+
- output: process.stdout
|
|
117
|
+
- });
|
|
118
|
+
- rl.question('Select option: ', (answer) => {
|
|
119
|
+
- // handle answer
|
|
120
|
+
- rl.close();
|
|
121
|
+
- });
|
|
122
|
+
+ const answer = await output.input('Select option:');
|
|
123
|
+
+ // handle answer (no need to close)
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### Step 6: Add Spinners for Long Operations
|
|
127
|
+
|
|
128
|
+
```diff
|
|
129
|
+
- console.log('Installing dependencies...');
|
|
130
|
+
- await installDependencies();
|
|
131
|
+
- console.log('Done');
|
|
132
|
+
+ const spinner = output.spinner('Installing dependencies...');
|
|
133
|
+
+ await installDependencies();
|
|
134
|
+
+ spinner.succeed('Dependencies installed');
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### Step 7: Add Progress Bars
|
|
138
|
+
|
|
139
|
+
```diff
|
|
140
|
+
- console.log(`Progress: ${i}/${total}`);
|
|
141
|
+
+ output.progress(i, total, 'Processing files...');
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### Step 8: Display Tables
|
|
145
|
+
|
|
146
|
+
```diff
|
|
147
|
+
- modules.forEach(mod => {
|
|
148
|
+
- console.log(`${mod.name}\t${mod.version}\t${mod.status}`);
|
|
149
|
+
- });
|
|
150
|
+
+ output.table(modules, ['name', 'version', 'status']);
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
## Real Example: install-command
|
|
154
|
+
|
|
155
|
+
### Before
|
|
156
|
+
|
|
157
|
+
```javascript
|
|
158
|
+
// install-command/logger.js
|
|
159
|
+
function logError(message, error) {
|
|
160
|
+
console.error(message, error);
|
|
161
|
+
if (error && error.stack) {
|
|
162
|
+
console.error(error.stack);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function logSuccess(message) {
|
|
167
|
+
console.log(message);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function logInfo(message) {
|
|
171
|
+
console.log(message);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
module.exports = {
|
|
175
|
+
logError,
|
|
176
|
+
logSuccess,
|
|
177
|
+
logInfo
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
// install-command/index.js
|
|
181
|
+
const logger = require('./logger');
|
|
182
|
+
|
|
183
|
+
logger.logInfo('Starting installation...');
|
|
184
|
+
// ...
|
|
185
|
+
logger.logSuccess('Module installed successfully');
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
### After
|
|
189
|
+
|
|
190
|
+
```javascript
|
|
191
|
+
// install-command/index.js
|
|
192
|
+
const output = require('../utils/output');
|
|
193
|
+
|
|
194
|
+
const spinner = output.spinner('Installing module...');
|
|
195
|
+
try {
|
|
196
|
+
// ... do installation
|
|
197
|
+
spinner.succeed('Module installed successfully');
|
|
198
|
+
} catch (error) {
|
|
199
|
+
spinner.fail('Installation failed');
|
|
200
|
+
output.error('Failed to install module', error);
|
|
201
|
+
process.exit(1);
|
|
202
|
+
}
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
**Result**: Delete `install-command/logger.js` (no longer needed!)
|
|
206
|
+
|
|
207
|
+
## Commands to Migrate
|
|
208
|
+
|
|
209
|
+
Priority order based on usage and inconsistency:
|
|
210
|
+
|
|
211
|
+
1. **install-command** (HIGH) - Uses plain console.log, has trivial logger wrapper
|
|
212
|
+
2. **repair-command** (HIGH) - Uses readline instead of inquirer
|
|
213
|
+
3. **doctor-command** (MEDIUM) - Long-running, needs spinners
|
|
214
|
+
4. **deploy-command** (MEDIUM) - Long-running, needs progress indication
|
|
215
|
+
5. **start-command** (LOW) - Already uses chalk consistently
|
|
216
|
+
6. **generate-command** (LOW) - Uses inquirer, but inconsistent colors
|
|
217
|
+
7. **build-command** (LOW) - Simple command, less output
|
|
218
|
+
|
|
219
|
+
## Testing Your Migration
|
|
220
|
+
|
|
221
|
+
After migrating a command:
|
|
222
|
+
|
|
223
|
+
1. **Run the command** manually to verify output looks correct
|
|
224
|
+
2. **Update tests** to mock `output` instead of `console`/`chalk`/`inquirer`
|
|
225
|
+
3. **Check for** color consistency, spinner behavior, error messages
|
|
226
|
+
|
|
227
|
+
### Test Example
|
|
228
|
+
|
|
229
|
+
```javascript
|
|
230
|
+
// Before
|
|
231
|
+
jest.spyOn(console, 'log');
|
|
232
|
+
await myCommand();
|
|
233
|
+
expect(console.log).toHaveBeenCalledWith(expect.stringContaining('Success'));
|
|
234
|
+
|
|
235
|
+
// After
|
|
236
|
+
const output = require('../utils/output');
|
|
237
|
+
jest.spyOn(output, 'success');
|
|
238
|
+
await myCommand();
|
|
239
|
+
expect(output.success).toHaveBeenCalledWith('Operation completed');
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
## Output API Reference
|
|
243
|
+
|
|
244
|
+
### Messages
|
|
245
|
+
- `output.success(message)` - Green checkmark + message
|
|
246
|
+
- `output.error(message, error?)` - Red X + message (+ stack trace if DEBUG=1)
|
|
247
|
+
- `output.info(message)` - Blue info icon + message
|
|
248
|
+
- `output.warn(message)` - Yellow warning icon + message
|
|
249
|
+
- `output.debug(message)` - Gray message (only if DEBUG=1)
|
|
250
|
+
|
|
251
|
+
### Formatting
|
|
252
|
+
- `output.header(title)` - Bold cyan title with underline
|
|
253
|
+
- `output.separator()` - Gray horizontal line
|
|
254
|
+
- `output.newline()` - Blank line
|
|
255
|
+
- `output.table(data, columns?)` - Formatted table
|
|
256
|
+
- `output.keyValue(object)` - Key-value pairs
|
|
257
|
+
- `output.json(data, indent?)` - Syntax-highlighted JSON
|
|
258
|
+
|
|
259
|
+
### Interactive
|
|
260
|
+
- `output.confirm(message, default?)` - Yes/no question
|
|
261
|
+
- `output.input(message, default?, validate?)` - Text input
|
|
262
|
+
- `output.select(message, choices, default?)` - Single selection
|
|
263
|
+
- `output.checkbox(message, choices)` - Multiple selection
|
|
264
|
+
- `output.password(message, validate?)` - Password input
|
|
265
|
+
|
|
266
|
+
### Progress
|
|
267
|
+
- `output.spinner(text)` - Returns {update, succeed, fail, stop}
|
|
268
|
+
- `output.progress(current, total, message?)` - Progress bar
|
|
269
|
+
|
|
270
|
+
### Compatibility
|
|
271
|
+
- `output.log(...args)` - Raw console.log (for gradual migration)
|
|
272
|
+
|
|
273
|
+
## Benefits After Migration
|
|
274
|
+
|
|
275
|
+
- ✅ **32% less code** - Remove logger wrappers and boilerplate
|
|
276
|
+
- ✅ **100% consistency** - All commands look and feel the same
|
|
277
|
+
- ✅ **Better UX** - Spinners, progress bars, formatted tables
|
|
278
|
+
- ✅ **Easier testing** - Mock one module instead of many
|
|
279
|
+
- ✅ **Maintainable** - Update UI in one place
|
|
280
|
+
|
|
281
|
+
## Questions?
|
|
282
|
+
|
|
283
|
+
See:
|
|
284
|
+
- `utils/output.js` - Full implementation
|
|
285
|
+
- `__tests__/unit/utils/output.test.js` - Test examples
|
|
286
|
+
- `FRIGG_CLI_ANALYSIS_REPORT.md` - Why we created this
|
|
@@ -12,8 +12,9 @@
|
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
14
|
const path = require('path');
|
|
15
|
+
const output = require('../utils/output');
|
|
15
16
|
const fs = require('fs');
|
|
16
|
-
|
|
17
|
+
// Using output.select from unified Output class (wraps @inquirer/prompts)
|
|
17
18
|
const { CloudFormationClient, ListStacksCommand } = require('@aws-sdk/client-cloudformation');
|
|
18
19
|
|
|
19
20
|
// Domain and Application Layer
|
|
@@ -160,9 +161,9 @@ function formatJsonOutput(report) {
|
|
|
160
161
|
function writeOutputFile(content, filePath) {
|
|
161
162
|
try {
|
|
162
163
|
fs.writeFileSync(filePath, content, 'utf8');
|
|
163
|
-
|
|
164
|
+
output.success(` Report saved to: ${filePath}`);
|
|
164
165
|
} catch (error) {
|
|
165
|
-
|
|
166
|
+
output.error(` Failed to write output file: ${error.message}`);
|
|
166
167
|
process.exit(1);
|
|
167
168
|
}
|
|
168
169
|
}
|
|
@@ -204,17 +205,17 @@ async function listStacks(region) {
|
|
|
204
205
|
* @returns {Promise<string>} Selected stack name
|
|
205
206
|
*/
|
|
206
207
|
async function promptForStackSelection(region) {
|
|
207
|
-
|
|
208
|
+
output.info(`🔍 Fetching CloudFormation stacks in ${region}...`);
|
|
208
209
|
|
|
209
210
|
const stacks = await listStacks(region);
|
|
210
211
|
|
|
211
212
|
if (stacks.length === 0) {
|
|
212
|
-
|
|
213
|
-
|
|
213
|
+
output.error(` No CloudFormation stacks found in ${region}`);
|
|
214
|
+
output.log(' Make sure you have stacks deployed and the correct AWS credentials configured.');
|
|
214
215
|
process.exit(1);
|
|
215
216
|
}
|
|
216
217
|
|
|
217
|
-
|
|
218
|
+
output.success(` Found ${stacks.length} stack(s)\n`);
|
|
218
219
|
|
|
219
220
|
// Create choices with stack name and metadata
|
|
220
221
|
const choices = stacks.map(stack => {
|
|
@@ -230,7 +231,7 @@ async function promptForStackSelection(region) {
|
|
|
230
231
|
};
|
|
231
232
|
});
|
|
232
233
|
|
|
233
|
-
const selectedStack = await select({
|
|
234
|
+
const selectedStack = await output.select({
|
|
234
235
|
message: 'Select a stack to run health check:',
|
|
235
236
|
choices,
|
|
236
237
|
pageSize: 15,
|
|
@@ -246,6 +247,13 @@ async function promptForStackSelection(region) {
|
|
|
246
247
|
*/
|
|
247
248
|
async function doctorCommand(stackName, options = {}) {
|
|
248
249
|
try {
|
|
250
|
+
// Guard: doctor only works with AWS (CloudFormation stacks)
|
|
251
|
+
if (isNonAwsProvider()) {
|
|
252
|
+
output.error('The doctor command is only available for AWS deployments.');
|
|
253
|
+
output.log('Your appDefinition uses a non-AWS provider.');
|
|
254
|
+
process.exit(1);
|
|
255
|
+
}
|
|
256
|
+
|
|
249
257
|
// Extract options with defaults
|
|
250
258
|
const region = options.region || process.env.AWS_REGION || 'us-east-1';
|
|
251
259
|
const format = options.format || 'console';
|
|
@@ -257,7 +265,7 @@ async function doctorCommand(stackName, options = {}) {
|
|
|
257
265
|
}
|
|
258
266
|
|
|
259
267
|
// Show progress to user (always, not just verbose mode)
|
|
260
|
-
|
|
268
|
+
output.info(`🏥 Running health check on stack: ${stackName} (${region})\n`);
|
|
261
269
|
|
|
262
270
|
// 1. Create stack identifier
|
|
263
271
|
const stackIdentifier = new StackIdentifier({ stackName, region });
|
|
@@ -281,9 +289,9 @@ async function doctorCommand(stackName, options = {}) {
|
|
|
281
289
|
// Progress callback to show execution status
|
|
282
290
|
const progressCallback = (step, message) => {
|
|
283
291
|
if (verbose) {
|
|
284
|
-
|
|
292
|
+
output.log(` ${message}`);
|
|
285
293
|
} else {
|
|
286
|
-
|
|
294
|
+
output.log(`${step} ${message}`);
|
|
287
295
|
}
|
|
288
296
|
};
|
|
289
297
|
|
|
@@ -292,7 +300,7 @@ async function doctorCommand(stackName, options = {}) {
|
|
|
292
300
|
onProgress: progressCallback
|
|
293
301
|
});
|
|
294
302
|
|
|
295
|
-
|
|
303
|
+
output.success(' Health check complete!\n');
|
|
296
304
|
|
|
297
305
|
// 5. Format and output results
|
|
298
306
|
if (format === 'json') {
|
|
@@ -301,11 +309,11 @@ async function doctorCommand(stackName, options = {}) {
|
|
|
301
309
|
if (options.output) {
|
|
302
310
|
writeOutputFile(jsonOutput, options.output);
|
|
303
311
|
} else {
|
|
304
|
-
|
|
312
|
+
output.log(jsonOutput);
|
|
305
313
|
}
|
|
306
314
|
} else {
|
|
307
315
|
const consoleOutput = formatConsoleOutput(report, options);
|
|
308
|
-
|
|
316
|
+
output.log(consoleOutput);
|
|
309
317
|
|
|
310
318
|
if (options.output) {
|
|
311
319
|
writeOutputFile(consoleOutput, options.output);
|
|
@@ -322,14 +330,27 @@ async function doctorCommand(stackName, options = {}) {
|
|
|
322
330
|
process.exit(0);
|
|
323
331
|
}
|
|
324
332
|
} catch (error) {
|
|
325
|
-
|
|
333
|
+
output.error(` Health check failed: ${error.message}`);
|
|
326
334
|
|
|
327
335
|
if (options.verbose && error.stack) {
|
|
328
|
-
|
|
336
|
+
output.error(`\nStack trace:\n${error.stack}`);
|
|
329
337
|
}
|
|
330
338
|
|
|
331
339
|
process.exit(1);
|
|
332
340
|
}
|
|
333
341
|
}
|
|
334
342
|
|
|
343
|
+
/**
|
|
344
|
+
* Check if the current appDefinition uses a non-AWS provider.
|
|
345
|
+
*/
|
|
346
|
+
function isNonAwsProvider() {
|
|
347
|
+
try {
|
|
348
|
+
const { loadProviderForCli } = require('../utils/provider-helper');
|
|
349
|
+
const result = loadProviderForCli();
|
|
350
|
+
return result && result.providerName !== 'aws';
|
|
351
|
+
} catch {
|
|
352
|
+
return false;
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
|
|
335
356
|
module.exports = { doctorCommand };
|