@friggframework/devtools 2.0.0--canary.545.e256e95.0 ā 2.0.0--canary.553.dc5f898.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__/unit/commands/build.test.js +1 -1
- package/frigg-cli/__tests__/unit/commands/doctor.test.js +2 -0
- package/frigg-cli/__tests__/unit/commands/install.test.js +19 -23
- package/frigg-cli/__tests__/unit/dependencies.test.js +2 -2
- package/frigg-cli/build-command/index.js +11 -123
- package/frigg-cli/deploy-command/index.js +1 -83
- package/frigg-cli/doctor-command/index.js +16 -37
- package/frigg-cli/generate-iam-command.js +1 -21
- package/frigg-cli/index.js +6 -21
- package/frigg-cli/index.test.js +2 -7
- package/frigg-cli/init-command/backend-first-handler.js +42 -124
- package/frigg-cli/init-command/index.js +1 -2
- package/frigg-cli/init-command/template-handler.js +3 -13
- package/frigg-cli/install-command/backend-js.js +3 -3
- package/frigg-cli/install-command/environment-variables.js +19 -16
- package/frigg-cli/install-command/environment-variables.test.js +13 -12
- package/frigg-cli/install-command/index.js +9 -14
- package/frigg-cli/install-command/integration-file.js +3 -3
- package/frigg-cli/install-command/logger.js +12 -0
- package/frigg-cli/install-command/validate-package.js +9 -5
- package/frigg-cli/jest.config.js +1 -4
- package/frigg-cli/repair-command/index.js +128 -121
- package/frigg-cli/start-command/index.js +2 -324
- package/frigg-cli/ui-command/index.js +36 -58
- package/frigg-cli/utils/repo-detection.js +37 -85
- package/infrastructure/create-frigg-infrastructure.js +0 -93
- package/infrastructure/docs/iam-policy-templates.md +1 -1
- package/infrastructure/domains/integration/integration-builder.js +3 -2
- package/infrastructure/domains/integration/integration-builder.test.js +54 -2
- package/infrastructure/domains/networking/vpc-builder.test.js +4 -2
- 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 +0 -35
- package/infrastructure/domains/shared/types/discovery-result.test.js +1 -1
- package/infrastructure/domains/shared/utilities/base-definition-factory.js +1 -10
- package/infrastructure/domains/shared/utilities/base-definition-factory.test.js +2 -2
- package/infrastructure/infrastructure-composer.js +0 -2
- package/infrastructure/infrastructure-composer.test.js +6 -5
- package/management-ui/README.md +109 -245
- package/package.json +7 -8
- package/frigg-cli/__tests__/application/use-cases/AddApiModuleToIntegrationUseCase.test.js +0 -326
- package/frigg-cli/__tests__/application/use-cases/CreateApiModuleUseCase.test.js +0 -337
- package/frigg-cli/__tests__/domain/entities/ApiModule.test.js +0 -373
- package/frigg-cli/__tests__/domain/entities/AppDefinition.test.js +0 -313
- package/frigg-cli/__tests__/domain/services/IntegrationValidator.test.js +0 -269
- package/frigg-cli/__tests__/domain/value-objects/IntegrationName.test.js +0 -82
- package/frigg-cli/__tests__/infrastructure/adapters/IntegrationJsUpdater.test.js +0 -408
- package/frigg-cli/__tests__/infrastructure/repositories/FileSystemApiModuleRepository.test.js +0 -583
- package/frigg-cli/__tests__/infrastructure/repositories/FileSystemAppDefinitionRepository.test.js +0 -314
- package/frigg-cli/__tests__/infrastructure/repositories/FileSystemIntegrationRepository.test.js +0 -383
- package/frigg-cli/__tests__/unit/commands/init.test.js +0 -406
- package/frigg-cli/__tests__/unit/commands/provider-dispatch.test.js +0 -383
- package/frigg-cli/__tests__/unit/commands/repair.test.js +0 -275
- package/frigg-cli/__tests__/unit/start-command/application/RunPreflightChecksUseCase.test.js +0 -411
- package/frigg-cli/__tests__/unit/start-command/infrastructure/DatabaseAdapter.test.js +0 -405
- package/frigg-cli/__tests__/unit/start-command/infrastructure/DockerAdapter.test.js +0 -496
- package/frigg-cli/__tests__/unit/start-command/presentation/InteractivePromptAdapter.test.js +0 -474
- package/frigg-cli/__tests__/unit/utils/output.test.js +0 -196
- package/frigg-cli/application/use-cases/AddApiModuleToIntegrationUseCase.js +0 -93
- package/frigg-cli/application/use-cases/CreateApiModuleUseCase.js +0 -93
- package/frigg-cli/application/use-cases/CreateIntegrationUseCase.js +0 -103
- package/frigg-cli/container.js +0 -172
- package/frigg-cli/docs/OUTPUT_MIGRATION_GUIDE.md +0 -286
- package/frigg-cli/domain/entities/ApiModule.js +0 -272
- package/frigg-cli/domain/entities/AppDefinition.js +0 -227
- package/frigg-cli/domain/entities/Integration.js +0 -198
- package/frigg-cli/domain/exceptions/DomainException.js +0 -24
- package/frigg-cli/domain/ports/IApiModuleRepository.js +0 -53
- package/frigg-cli/domain/ports/IAppDefinitionRepository.js +0 -43
- package/frigg-cli/domain/ports/IIntegrationRepository.js +0 -61
- package/frigg-cli/domain/services/IntegrationValidator.js +0 -185
- package/frigg-cli/domain/value-objects/IntegrationId.js +0 -42
- package/frigg-cli/domain/value-objects/IntegrationName.js +0 -60
- package/frigg-cli/domain/value-objects/SemanticVersion.js +0 -70
- package/frigg-cli/infrastructure/UnitOfWork.js +0 -46
- package/frigg-cli/infrastructure/adapters/BackendJsUpdater.js +0 -197
- package/frigg-cli/infrastructure/adapters/FileSystemAdapter.js +0 -224
- package/frigg-cli/infrastructure/adapters/IntegrationJsUpdater.js +0 -249
- package/frigg-cli/infrastructure/adapters/SchemaValidator.js +0 -92
- package/frigg-cli/infrastructure/repositories/FileSystemApiModuleRepository.js +0 -373
- package/frigg-cli/infrastructure/repositories/FileSystemAppDefinitionRepository.js +0 -116
- package/frigg-cli/infrastructure/repositories/FileSystemIntegrationRepository.js +0 -277
- package/frigg-cli/package-lock.json +0 -16226
- package/frigg-cli/start-command/application/RunPreflightChecksUseCase.js +0 -376
- package/frigg-cli/start-command/infrastructure/DatabaseAdapter.js +0 -591
- package/frigg-cli/start-command/infrastructure/DockerAdapter.js +0 -306
- package/frigg-cli/start-command/presentation/InteractivePromptAdapter.js +0 -329
- package/frigg-cli/templates/backend/.env.example +0 -62
- package/frigg-cli/templates/backend/.eslintrc.json +0 -12
- package/frigg-cli/templates/backend/.prettierrc +0 -6
- package/frigg-cli/templates/backend/docker-compose.yml +0 -22
- package/frigg-cli/templates/backend/index.js +0 -96
- package/frigg-cli/templates/backend/infrastructure.js +0 -12
- package/frigg-cli/templates/backend/jest.config.js +0 -17
- package/frigg-cli/templates/backend/package.json +0 -50
- package/frigg-cli/templates/backend/src/api-modules/.gitkeep +0 -10
- package/frigg-cli/templates/backend/src/base/.gitkeep +0 -7
- package/frigg-cli/templates/backend/src/integrations/.gitkeep +0 -10
- package/frigg-cli/templates/backend/src/integrations/ExampleIntegration.js +0 -65
- package/frigg-cli/templates/backend/src/utils/.gitkeep +0 -7
- package/frigg-cli/templates/backend/test/setup.js +0 -30
- package/frigg-cli/templates/backend/ui-extensions/.gitkeep +0 -0
- package/frigg-cli/templates/backend/ui-extensions/README.md +0 -77
- package/frigg-cli/utils/__tests__/provider-helper.test.js +0 -55
- package/frigg-cli/utils/__tests__/repo-detection.test.js +0 -436
- package/frigg-cli/utils/output.js +0 -382
- package/frigg-cli/utils/provider-helper.js +0 -75
- package/frigg-cli/validate-command/__tests__/adapters/validate-command.test.js +0 -205
- package/frigg-cli/validate-command/__tests__/application/validate-app-use-case.test.js +0 -104
- package/frigg-cli/validate-command/__tests__/domain/fix-suggestion.test.js +0 -153
- package/frigg-cli/validate-command/__tests__/domain/validation-error.test.js +0 -162
- package/frigg-cli/validate-command/__tests__/domain/validation-result.test.js +0 -152
- package/frigg-cli/validate-command/__tests__/infrastructure/api-module-validator.test.js +0 -332
- package/frigg-cli/validate-command/__tests__/infrastructure/app-definition-validator.test.js +0 -191
- package/frigg-cli/validate-command/__tests__/infrastructure/integration-class-validator.test.js +0 -146
- package/frigg-cli/validate-command/__tests__/infrastructure/template-validation.test.js +0 -155
- package/frigg-cli/validate-command/adapters/cli/validate-command.js +0 -199
- package/frigg-cli/validate-command/application/use-cases/validate-app-use-case.js +0 -35
- package/frigg-cli/validate-command/domain/entities/validation-result.js +0 -74
- package/frigg-cli/validate-command/domain/value-objects/fix-suggestion.js +0 -74
- package/frigg-cli/validate-command/domain/value-objects/validation-error.js +0 -68
- package/frigg-cli/validate-command/infrastructure/validators/api-module-validator.js +0 -181
- package/frigg-cli/validate-command/infrastructure/validators/app-definition-validator.js +0 -145
- package/frigg-cli/validate-command/infrastructure/validators/integration-class-validator.js +0 -113
- package/infrastructure/domains/admin-scripts/admin-script-builder.js +0 -200
- package/infrastructure/domains/admin-scripts/admin-script-builder.test.js +0 -499
- package/infrastructure/domains/admin-scripts/index.js +0 -5
- package/infrastructure/jest.config.js +0 -16
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
14
|
const path = require('path');
|
|
15
|
-
const
|
|
15
|
+
const readline = require('readline');
|
|
16
16
|
|
|
17
17
|
// Domain and Application Layer
|
|
18
18
|
const StackIdentifier = require('../../infrastructure/domains/health/domain/value-objects/stack-identifier');
|
|
@@ -34,6 +34,33 @@ const { TemplateParser } = require('../../infrastructure/domains/health/domain/s
|
|
|
34
34
|
const { ImportTemplateGenerator } = require('../../infrastructure/domains/health/domain/services/import-template-generator');
|
|
35
35
|
const { ImportProgressMonitor } = require('../../infrastructure/domains/health/domain/services/import-progress-monitor');
|
|
36
36
|
|
|
37
|
+
/**
|
|
38
|
+
* Create readline interface for user prompts
|
|
39
|
+
* @returns {readline.Interface}
|
|
40
|
+
*/
|
|
41
|
+
function createReadlineInterface() {
|
|
42
|
+
return readline.createInterface({
|
|
43
|
+
input: process.stdin,
|
|
44
|
+
output: process.stdout,
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Prompt user for confirmation
|
|
50
|
+
* @param {string} question - Question to ask
|
|
51
|
+
* @returns {Promise<boolean>} User confirmed
|
|
52
|
+
*/
|
|
53
|
+
function confirm(question) {
|
|
54
|
+
const rl = createReadlineInterface();
|
|
55
|
+
|
|
56
|
+
return new Promise((resolve) => {
|
|
57
|
+
rl.question(`${question} (y/N): `, (answer) => {
|
|
58
|
+
rl.close();
|
|
59
|
+
resolve(answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes');
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
37
64
|
/**
|
|
38
65
|
* Handle import repair operation using template comparison
|
|
39
66
|
* @param {StackIdentifier} stackIdentifier - Stack identifier
|
|
@@ -44,13 +71,13 @@ async function handleImportRepair(stackIdentifier, report, options) {
|
|
|
44
71
|
const orphanedResources = report.getOrphanedResources();
|
|
45
72
|
|
|
46
73
|
if (orphanedResources.length === 0) {
|
|
47
|
-
|
|
74
|
+
console.log('\nā No orphaned resources to import');
|
|
48
75
|
return { imported: 0, failed: 0 };
|
|
49
76
|
}
|
|
50
77
|
|
|
51
|
-
|
|
78
|
+
console.log(`\nš¦ Found ${orphanedResources.length} orphaned resource(s) to import:`);
|
|
52
79
|
orphanedResources.forEach((resource, idx) => {
|
|
53
|
-
|
|
80
|
+
console.log(` ${idx + 1}. ${resource.resourceType} - ${resource.physicalId}`);
|
|
54
81
|
});
|
|
55
82
|
|
|
56
83
|
// Check for build template
|
|
@@ -58,12 +85,12 @@ async function handleImportRepair(stackIdentifier, report, options) {
|
|
|
58
85
|
const buildTemplateExists = TemplateParser.buildTemplateExists();
|
|
59
86
|
|
|
60
87
|
if (!buildTemplateExists) {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
88
|
+
console.log('\nā ļø Build template not found. Generating sequential logical IDs (not recommended).');
|
|
89
|
+
console.log(` Run one of the following to generate build template:`);
|
|
90
|
+
console.log(` ⢠serverless package`);
|
|
91
|
+
console.log(` ⢠frigg build`);
|
|
92
|
+
console.log(` ⢠frigg deploy --stage dev`);
|
|
93
|
+
console.log(` Then run 'frigg repair --import ${stackIdentifier.stackName}' again for correct logical IDs.\n`);
|
|
67
94
|
|
|
68
95
|
// Fallback to sequential IDs (old behavior)
|
|
69
96
|
const resourcesToImport = orphanedResources.map((resource, idx) => ({
|
|
@@ -73,9 +100,9 @@ async function handleImportRepair(stackIdentifier, report, options) {
|
|
|
73
100
|
}));
|
|
74
101
|
|
|
75
102
|
if (!options.yes) {
|
|
76
|
-
const confirmed = await
|
|
103
|
+
const confirmed = await confirm(`\nImport ${orphanedResources.length} orphaned resource(s) with sequential IDs?`);
|
|
77
104
|
if (!confirmed) {
|
|
78
|
-
|
|
105
|
+
console.log('Import cancelled by user');
|
|
79
106
|
return { imported: 0, failed: 0, cancelled: true };
|
|
80
107
|
}
|
|
81
108
|
}
|
|
@@ -84,20 +111,20 @@ async function handleImportRepair(stackIdentifier, report, options) {
|
|
|
84
111
|
const resourceImporter = new AWSResourceImporter({ region: stackIdentifier.region });
|
|
85
112
|
const repairUseCase = new RepairViaImportUseCase({ resourceDetector, resourceImporter });
|
|
86
113
|
|
|
87
|
-
|
|
114
|
+
console.log('\nš§ Importing resources with sequential IDs...');
|
|
88
115
|
const importResult = await repairUseCase.importMultipleResources({
|
|
89
116
|
stackIdentifier,
|
|
90
117
|
resources: resourcesToImport,
|
|
91
118
|
});
|
|
92
119
|
|
|
93
120
|
if (importResult.success) {
|
|
94
|
-
|
|
121
|
+
console.log(`\nā Successfully imported ${importResult.importedCount} resource(s)`);
|
|
95
122
|
} else {
|
|
96
|
-
|
|
123
|
+
console.log(`\nā Import failed: ${importResult.message}`);
|
|
97
124
|
if (importResult.validationErrors && importResult.validationErrors.length > 0) {
|
|
98
|
-
|
|
125
|
+
console.log('\nValidation errors:');
|
|
99
126
|
importResult.validationErrors.forEach((error) => {
|
|
100
|
-
|
|
127
|
+
console.log(` ⢠${error.logicalId}: ${error.reason}`);
|
|
101
128
|
});
|
|
102
129
|
}
|
|
103
130
|
}
|
|
@@ -110,9 +137,9 @@ async function handleImportRepair(stackIdentifier, report, options) {
|
|
|
110
137
|
}
|
|
111
138
|
|
|
112
139
|
// Use template comparison to find correct logical IDs
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
140
|
+
console.log(`\nš Analyzing templates to map orphaned resources to correct logical IDs...`);
|
|
141
|
+
console.log(` Build template: ${buildTemplatePath}`);
|
|
142
|
+
console.log(` Deployed template: CloudFormation (via AWS API)`);
|
|
116
143
|
|
|
117
144
|
// Wire up use case with template comparison
|
|
118
145
|
const stackRepository = new AWSStackRepository({ region: stackIdentifier.region });
|
|
@@ -132,31 +159,31 @@ async function handleImportRepair(stackIdentifier, report, options) {
|
|
|
132
159
|
});
|
|
133
160
|
|
|
134
161
|
if (!mappingResult.success) {
|
|
135
|
-
|
|
162
|
+
console.log(`\nā Mapping failed: ${mappingResult.message}`);
|
|
136
163
|
return { imported: 0, failed: 0, success: false };
|
|
137
164
|
}
|
|
138
165
|
|
|
139
166
|
// Display mapping results
|
|
140
|
-
|
|
167
|
+
console.log(`\nā
Successfully mapped ${mappingResult.mappedCount} resource(s) to logical IDs:`);
|
|
141
168
|
mappingResult.mappings.forEach((mapping) => {
|
|
142
|
-
|
|
169
|
+
console.log(` ⢠${mapping.logicalId} ā ${mapping.physicalId} (${mapping.matchMethod}, ${mapping.confidence} confidence)`);
|
|
143
170
|
});
|
|
144
171
|
|
|
145
172
|
if (mappingResult.unmappedCount > 0) {
|
|
146
|
-
|
|
173
|
+
console.log(`\nā ļø Could not map ${mappingResult.unmappedCount} resource(s):`);
|
|
147
174
|
mappingResult.unmappedResources.forEach((resource) => {
|
|
148
|
-
|
|
175
|
+
console.log(` ⢠${resource.resourceType} - ${resource.physicalId}`);
|
|
149
176
|
});
|
|
150
177
|
}
|
|
151
178
|
|
|
152
179
|
// Display warnings for multiple resources of same type
|
|
153
180
|
if (mappingResult.warnings && mappingResult.warnings.length > 0) {
|
|
154
|
-
|
|
181
|
+
console.log(`\nā ļø Warnings:`);
|
|
155
182
|
mappingResult.warnings.forEach((warning) => {
|
|
156
|
-
|
|
183
|
+
console.log(` ⢠${warning.message}`);
|
|
157
184
|
if (warning.type === 'MULTIPLE_RESOURCES') {
|
|
158
185
|
warning.resources.forEach((res) => {
|
|
159
|
-
|
|
186
|
+
console.log(` - ${res.logicalId} ā ${res.physicalId} (${res.matchMethod}, ${res.confidence})`);
|
|
160
187
|
});
|
|
161
188
|
}
|
|
162
189
|
});
|
|
@@ -164,20 +191,20 @@ async function handleImportRepair(stackIdentifier, report, options) {
|
|
|
164
191
|
|
|
165
192
|
// Confirm with user (unless --yes flag)
|
|
166
193
|
if (!options.yes) {
|
|
167
|
-
|
|
194
|
+
console.log(`\nš The following will be imported into CloudFormation:`);
|
|
168
195
|
mappingResult.resourcesToImport.forEach((resource) => {
|
|
169
|
-
|
|
196
|
+
console.log(` ⢠${resource.LogicalResourceId} (${resource.ResourceType})`);
|
|
170
197
|
});
|
|
171
198
|
|
|
172
|
-
const confirmed = await
|
|
199
|
+
const confirmed = await confirm(`\nProceed with import of ${mappingResult.mappedCount} resource(s)?`);
|
|
173
200
|
if (!confirmed) {
|
|
174
|
-
|
|
201
|
+
console.log('Import cancelled by user');
|
|
175
202
|
return { imported: 0, failed: 0, cancelled: true };
|
|
176
203
|
}
|
|
177
204
|
}
|
|
178
205
|
|
|
179
206
|
// Execute actual CloudFormation import operation
|
|
180
|
-
|
|
207
|
+
console.log(`\nš§ Preparing CloudFormation import operation...`);
|
|
181
208
|
|
|
182
209
|
// Wire up ExecuteResourceImportUseCase
|
|
183
210
|
const templateParser = new TemplateParser();
|
|
@@ -210,38 +237,38 @@ async function handleImportRepair(stackIdentifier, report, options) {
|
|
|
210
237
|
buildTemplatePath,
|
|
211
238
|
onProgress: (progress) => {
|
|
212
239
|
if (progress.step === 'generate_template' && progress.status === 'in_progress') {
|
|
213
|
-
|
|
240
|
+
console.log(' ⢠Generating import template...');
|
|
214
241
|
} else if (progress.step === 'generate_template' && progress.status === 'complete') {
|
|
215
|
-
|
|
242
|
+
console.log(' ā Template generated');
|
|
216
243
|
} else if (progress.step === 'create_change_set' && progress.status === 'in_progress') {
|
|
217
|
-
|
|
244
|
+
console.log(' ⢠Creating CloudFormation change set...');
|
|
218
245
|
} else if (progress.step === 'create_change_set' && progress.status === 'complete') {
|
|
219
|
-
|
|
246
|
+
console.log(` ā Change set created: ${progress.changeSetName}`);
|
|
220
247
|
} else if (progress.step === 'wait_change_set' && progress.status === 'in_progress') {
|
|
221
|
-
|
|
248
|
+
console.log(' ⢠Waiting for change set...');
|
|
222
249
|
} else if (progress.step === 'wait_change_set' && progress.status === 'complete') {
|
|
223
|
-
|
|
250
|
+
console.log(' ā Change set ready');
|
|
224
251
|
} else if (progress.step === 'execute_import' && progress.status === 'in_progress') {
|
|
225
252
|
if (progress.resourceProgress) {
|
|
226
253
|
const { logicalId, status, progress: resourceProgress, total } = progress.resourceProgress;
|
|
227
|
-
|
|
254
|
+
console.log(` ⢠Importing resource ${resourceProgress}/${total}: ${logicalId} (${status})`);
|
|
228
255
|
} else {
|
|
229
|
-
|
|
256
|
+
console.log(' ⢠Executing import operation...');
|
|
230
257
|
}
|
|
231
258
|
} else if (progress.step === 'execute_import' && progress.status === 'complete') {
|
|
232
|
-
|
|
259
|
+
console.log(' ā Import operation complete');
|
|
233
260
|
} else if (progress.step === 'verify' && progress.status === 'in_progress') {
|
|
234
|
-
|
|
261
|
+
console.log(' ⢠Verifying imported resources...');
|
|
235
262
|
} else if (progress.step === 'verify' && progress.status === 'complete') {
|
|
236
|
-
|
|
263
|
+
console.log(' ā Verification complete');
|
|
237
264
|
}
|
|
238
265
|
},
|
|
239
266
|
});
|
|
240
267
|
|
|
241
268
|
if (importResult.success) {
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
269
|
+
console.log(`\nā
Successfully imported ${importResult.importedCount} resource(s) into CloudFormation!`);
|
|
270
|
+
console.log(` Stack status: ${importResult.stackStatus}`);
|
|
271
|
+
console.log(` Change set: ${importResult.changeSetName}`);
|
|
245
272
|
|
|
246
273
|
return {
|
|
247
274
|
imported: importResult.importedCount,
|
|
@@ -249,8 +276,8 @@ async function handleImportRepair(stackIdentifier, report, options) {
|
|
|
249
276
|
success: true,
|
|
250
277
|
};
|
|
251
278
|
} else {
|
|
252
|
-
|
|
253
|
-
|
|
279
|
+
console.error(`\nā Import operation failed: ${importResult.error}`);
|
|
280
|
+
console.error(` Failed at step: ${importResult.step}`);
|
|
254
281
|
|
|
255
282
|
return {
|
|
256
283
|
imported: 0,
|
|
@@ -271,7 +298,7 @@ async function handleReconcileRepair(stackIdentifier, report, options) {
|
|
|
271
298
|
const driftedResources = report.getDriftedResources();
|
|
272
299
|
|
|
273
300
|
if (driftedResources.length === 0) {
|
|
274
|
-
|
|
301
|
+
console.log('\nā No property drift to reconcile');
|
|
275
302
|
return { reconciled: 0, failed: 0 };
|
|
276
303
|
}
|
|
277
304
|
|
|
@@ -284,12 +311,12 @@ async function handleReconcileRepair(stackIdentifier, report, options) {
|
|
|
284
311
|
totalMismatches += issues.length;
|
|
285
312
|
});
|
|
286
313
|
|
|
287
|
-
|
|
314
|
+
console.log(`\nš§ Found ${driftedResources.length} drifted resource(s) with ${totalMismatches} property mismatch(es):`);
|
|
288
315
|
driftedResources.forEach((resource) => {
|
|
289
316
|
const issues = report.issues.filter(
|
|
290
317
|
(issue) => issue.type === 'PROPERTY_MISMATCH' && issue.resourceId === resource.physicalId
|
|
291
318
|
);
|
|
292
|
-
|
|
319
|
+
console.log(` ⢠${resource.logicalId} (${resource.resourceType}): ${issues.length} mismatch(es)`);
|
|
293
320
|
});
|
|
294
321
|
|
|
295
322
|
// Determine mode (template or resource)
|
|
@@ -298,14 +325,14 @@ async function handleReconcileRepair(stackIdentifier, report, options) {
|
|
|
298
325
|
? 'Update CloudFormation template to match actual resource state'
|
|
299
326
|
: 'Update cloud resources to match CloudFormation template';
|
|
300
327
|
|
|
301
|
-
|
|
302
|
-
|
|
328
|
+
console.log(`\nReconciliation mode: ${mode}`);
|
|
329
|
+
console.log(` ${modeDescription}`);
|
|
303
330
|
|
|
304
331
|
// Confirm with user (unless --yes flag)
|
|
305
332
|
if (!options.yes) {
|
|
306
|
-
const confirmed = await
|
|
333
|
+
const confirmed = await confirm(`\nReconcile ${totalMismatches} property mismatch(es) in ${mode} mode?`);
|
|
307
334
|
if (!confirmed) {
|
|
308
|
-
|
|
335
|
+
console.log('Reconciliation cancelled by user');
|
|
309
336
|
return { reconciled: 0, failed: 0, cancelled: true };
|
|
310
337
|
}
|
|
311
338
|
}
|
|
@@ -319,7 +346,7 @@ async function handleReconcileRepair(stackIdentifier, report, options) {
|
|
|
319
346
|
const reconcileUseCase = new ReconcilePropertiesUseCase({ propertyReconciler });
|
|
320
347
|
|
|
321
348
|
// Execute reconciliation for each drifted resource
|
|
322
|
-
|
|
349
|
+
console.log('\nš§ Reconciling property drift...');
|
|
323
350
|
let reconciledCount = 0;
|
|
324
351
|
let failedCount = 0;
|
|
325
352
|
let skippedImmutableCount = 0;
|
|
@@ -364,56 +391,56 @@ async function handleReconcileRepair(stackIdentifier, report, options) {
|
|
|
364
391
|
});
|
|
365
392
|
}
|
|
366
393
|
|
|
367
|
-
|
|
394
|
+
console.log(` ā ${resource.logicalId}: Reconciled ${result.reconciledCount} property(ies)`);
|
|
368
395
|
if (result.skippedCount > 0) {
|
|
369
|
-
|
|
396
|
+
console.log(` ā Skipped ${result.skippedCount} immutable property(ies) - requires manual intervention`);
|
|
370
397
|
}
|
|
371
398
|
|
|
372
399
|
// Debug: Log full result if reconciledCount is 0 but we expected properties
|
|
373
400
|
if (process.env.DEBUG_RECONCILE && result.reconciledCount === 0 && mismatches.length > 0) {
|
|
374
|
-
|
|
401
|
+
console.log(` [DEBUG] Expected ${mismatches.length} mismatches, got result:`, JSON.stringify(result, null, 2));
|
|
375
402
|
}
|
|
376
403
|
} catch (error) {
|
|
377
404
|
// Count failed properties, not just the resource
|
|
378
405
|
failedCount += mismatches.length;
|
|
379
|
-
|
|
406
|
+
console.log(` ā ${resource.logicalId}: ${error.message}`);
|
|
380
407
|
|
|
381
408
|
// Debug: Log full error
|
|
382
409
|
if (process.env.DEBUG_RECONCILE) {
|
|
383
|
-
|
|
410
|
+
console.log(` [DEBUG] Error stack:`, error.stack);
|
|
384
411
|
}
|
|
385
412
|
}
|
|
386
413
|
}
|
|
387
414
|
|
|
388
415
|
// Report results
|
|
389
|
-
|
|
416
|
+
console.log(''); // Blank line before summary
|
|
390
417
|
|
|
391
418
|
if (reconciledCount > 0) {
|
|
392
|
-
|
|
419
|
+
console.log(`ā
Reconciled ${reconciledCount} property(ies)`);
|
|
393
420
|
}
|
|
394
421
|
|
|
395
422
|
if (skippedImmutableCount > 0) {
|
|
396
|
-
|
|
423
|
+
console.log(`\nā ${skippedImmutableCount} immutable property(ies) require manual intervention:`);
|
|
397
424
|
immutableProperties.forEach(prop => {
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
425
|
+
console.log(` ⢠${prop.logicalId}.${prop.propertyPath}`);
|
|
426
|
+
console.log(` Template: ${JSON.stringify(prop.expectedValue)}`);
|
|
427
|
+
console.log(` Actual: ${JSON.stringify(prop.actualValue)}`);
|
|
401
428
|
});
|
|
402
429
|
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
430
|
+
console.log(`\nš” To resolve immutable property drift:`);
|
|
431
|
+
console.log(` 1. These properties require resource replacement (cannot be updated in place)`);
|
|
432
|
+
console.log(` 2. Options:`);
|
|
433
|
+
console.log(` a) Accept the drift - update your local template to match actual values`);
|
|
434
|
+
console.log(` b) Replace the resource - delete and recreate via CloudFormation`);
|
|
435
|
+
console.log(` c) Use import workflow - remove from stack, then re-import with correct values`);
|
|
436
|
+
console.log(`\n For automated import workflow (coming soon):`);
|
|
437
|
+
console.log(` frigg repair --import-drift ${stackIdentifier.stackName}`);
|
|
411
438
|
}
|
|
412
439
|
|
|
413
440
|
if (failedCount === 0 && skippedImmutableCount === 0) {
|
|
414
|
-
|
|
441
|
+
console.log(`ā Successfully reconciled all ${reconciledCount} property mismatch(es)`);
|
|
415
442
|
} else {
|
|
416
|
-
|
|
443
|
+
console.log(`\nā Reconciled ${reconciledCount} property(ies), ${failedCount} failed`);
|
|
417
444
|
}
|
|
418
445
|
|
|
419
446
|
return { reconciled: reconciledCount, failed: failedCount, success: failedCount === 0 };
|
|
@@ -426,28 +453,21 @@ async function handleReconcileRepair(stackIdentifier, report, options) {
|
|
|
426
453
|
*/
|
|
427
454
|
async function repairCommand(stackName, options = {}) {
|
|
428
455
|
try {
|
|
429
|
-
// Guard: repair only works with AWS (CloudFormation stacks)
|
|
430
|
-
if (isNonAwsProvider()) {
|
|
431
|
-
output.error('The repair command is only available for AWS deployments.');
|
|
432
|
-
output.log('Your appDefinition uses a non-AWS provider.');
|
|
433
|
-
process.exit(1);
|
|
434
|
-
}
|
|
435
|
-
|
|
436
456
|
// Validate required parameter
|
|
437
457
|
if (!stackName) {
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
458
|
+
console.error('Error: Stack name is required');
|
|
459
|
+
console.log('Usage: frigg repair [options] <stack-name>');
|
|
460
|
+
console.log('Options:');
|
|
461
|
+
console.log(' --import Import orphaned resources');
|
|
462
|
+
console.log(' --reconcile Reconcile property drift');
|
|
463
|
+
console.log(' --yes Skip confirmation prompts');
|
|
444
464
|
process.exit(1);
|
|
445
465
|
}
|
|
446
466
|
|
|
447
467
|
// Validate at least one repair operation is selected
|
|
448
468
|
if (!options.import && !options.reconcile) {
|
|
449
|
-
|
|
450
|
-
|
|
469
|
+
console.error('Error: At least one repair operation must be specified (--import or --reconcile)');
|
|
470
|
+
console.log('Usage: frigg repair [options] <stack-name>');
|
|
451
471
|
process.exit(1);
|
|
452
472
|
}
|
|
453
473
|
|
|
@@ -455,13 +475,13 @@ async function repairCommand(stackName, options = {}) {
|
|
|
455
475
|
const region = options.region || process.env.AWS_REGION || 'us-east-1';
|
|
456
476
|
const verbose = options.verbose || false;
|
|
457
477
|
|
|
458
|
-
|
|
478
|
+
console.log(`\nš„ Running Frigg Repair on stack: ${stackName} (${region})`);
|
|
459
479
|
|
|
460
480
|
// 1. Create stack identifier
|
|
461
481
|
const stackIdentifier = new StackIdentifier({ stackName, region });
|
|
462
482
|
|
|
463
483
|
// 2. Run health check first to identify issues
|
|
464
|
-
|
|
484
|
+
console.log('\nš Running health check to identify issues...');
|
|
465
485
|
|
|
466
486
|
const stackRepository = new AWSStackRepository({ region });
|
|
467
487
|
const resourceDetector = new AWSResourceDetector({ region });
|
|
@@ -477,8 +497,8 @@ async function repairCommand(stackName, options = {}) {
|
|
|
477
497
|
|
|
478
498
|
const report = await runHealthCheckUseCase.execute({ stackIdentifier });
|
|
479
499
|
|
|
480
|
-
|
|
481
|
-
|
|
500
|
+
console.log(`\nHealth Score: ${report.healthScore.value}/100 (${report.healthScore.qualitativeAssessment()})`);
|
|
501
|
+
console.log(`Issues: ${report.getIssueCount()} total (${report.getCriticalIssueCount()} critical)`);
|
|
482
502
|
|
|
483
503
|
// 3. Execute requested repair operations
|
|
484
504
|
const results = {
|
|
@@ -504,24 +524,24 @@ async function repairCommand(stackName, options = {}) {
|
|
|
504
524
|
}
|
|
505
525
|
|
|
506
526
|
// 4. Final summary
|
|
507
|
-
|
|
508
|
-
|
|
527
|
+
console.log('\n' + 'ā'.repeat(80));
|
|
528
|
+
console.log('Repair Summary:');
|
|
509
529
|
if (options.import) {
|
|
510
|
-
|
|
530
|
+
console.log(` Imported: ${results.imported} resource(s)`);
|
|
511
531
|
}
|
|
512
532
|
if (options.reconcile) {
|
|
513
|
-
|
|
533
|
+
console.log(` Reconciled: ${results.reconciled} property(ies)`);
|
|
514
534
|
}
|
|
515
|
-
|
|
516
|
-
|
|
535
|
+
console.log(` Failed: ${results.failed}`);
|
|
536
|
+
console.log('ā'.repeat(80));
|
|
517
537
|
|
|
518
538
|
// Run health check again to verify repairs
|
|
519
|
-
|
|
539
|
+
console.log('\nš Running health check to verify repairs...');
|
|
520
540
|
const verifyReport = await runHealthCheckUseCase.execute({ stackIdentifier });
|
|
521
|
-
|
|
541
|
+
console.log(`\nNew Health Score: ${verifyReport.healthScore.value}/100 (${verifyReport.healthScore.qualitativeAssessment()})`);
|
|
522
542
|
|
|
523
543
|
if (verifyReport.healthScore.value > report.healthScore.value) {
|
|
524
|
-
|
|
544
|
+
console.log(`\nā Health improved by ${verifyReport.healthScore.value - report.healthScore.value} points!`);
|
|
525
545
|
}
|
|
526
546
|
|
|
527
547
|
// 5. Exit with appropriate code
|
|
@@ -531,27 +551,14 @@ async function repairCommand(stackName, options = {}) {
|
|
|
531
551
|
process.exit(0);
|
|
532
552
|
}
|
|
533
553
|
} catch (error) {
|
|
534
|
-
|
|
554
|
+
console.error(`\nā Repair failed: ${error.message}`);
|
|
535
555
|
|
|
536
556
|
if (options.verbose && error.stack) {
|
|
537
|
-
|
|
557
|
+
console.error(`\nStack trace:\n${error.stack}`);
|
|
538
558
|
}
|
|
539
559
|
|
|
540
560
|
process.exit(1);
|
|
541
561
|
}
|
|
542
562
|
}
|
|
543
563
|
|
|
544
|
-
/**
|
|
545
|
-
* Check if the current appDefinition uses a non-AWS provider.
|
|
546
|
-
*/
|
|
547
|
-
function isNonAwsProvider() {
|
|
548
|
-
try {
|
|
549
|
-
const { loadProviderForCli } = require('../utils/provider-helper');
|
|
550
|
-
const result = loadProviderForCli();
|
|
551
|
-
return result && result.providerName !== 'aws';
|
|
552
|
-
} catch {
|
|
553
|
-
return false;
|
|
554
|
-
}
|
|
555
|
-
}
|
|
556
|
-
|
|
557
564
|
module.exports = { repairCommand };
|