@fjall/deploy-core 0.89.5 → 0.89.6
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/LICENSE +50 -21
- package/README.md +25 -0
- package/dist/.minified +1 -0
- package/dist/src/__test-utils__/awsMockHelpers.d.ts +20 -0
- package/dist/src/__test-utils__/awsMockHelpers.js +1 -0
- package/dist/src/__test-utils__/index.d.ts +1 -0
- package/dist/src/__test-utils__/index.js +1 -0
- package/dist/src/aws/AwsProvider.js +0 -1
- package/dist/src/aws/SimpleAwsProvider.js +1 -70
- package/dist/src/aws/index.d.ts +4 -2
- package/dist/src/aws/index.js +1 -3
- package/dist/src/aws/organisations/accounts.js +10 -10
- package/dist/src/aws/organisations/backup.js +4 -2
- package/dist/src/aws/organisations/costAllocation.js +4 -2
- package/dist/src/aws/organisations/delegatedAdmin.d.ts +9 -0
- package/dist/src/aws/organisations/delegatedAdmin.js +43 -0
- package/dist/src/aws/organisations/identityCentre.d.ts +1 -1
- package/dist/src/aws/organisations/identityCentre.js +6 -2
- package/dist/src/aws/organisations/index.d.ts +4 -3
- package/dist/src/aws/organisations/index.js +1 -12
- package/dist/src/aws/organisations/ipam.js +4 -2
- package/dist/src/aws/organisations/organisation.js +27 -18
- package/dist/src/aws/organisations/organisationalUnits.d.ts +26 -6
- package/dist/src/aws/organisations/organisationalUnits.js +149 -35
- package/dist/src/aws/organisations/policies.js +4 -3
- package/dist/src/aws/organisations/ram.js +6 -2
- package/dist/src/aws/organisations/serviceAccess.js +12 -6
- package/dist/src/aws/organisations/trustedAccess.js +6 -2
- package/dist/src/aws/organisations/types.d.ts +23 -1
- package/dist/src/aws/organisations/types.js +1 -16
- package/dist/src/aws/utils/__tests__/cloudformationTestHelpers.d.ts +6 -0
- package/dist/src/aws/utils/__tests__/cloudformationTestHelpers.js +1 -0
- package/dist/src/aws/utils/cloudformationEventHelpers.d.ts +48 -0
- package/dist/src/aws/utils/cloudformationEventHelpers.js +1 -0
- package/dist/src/aws/utils/cloudformationEventTypes.d.ts +45 -0
- package/dist/src/aws/utils/cloudformationEventTypes.js +1 -0
- package/dist/src/aws/utils/cloudformationEvents.d.ts +8 -54
- package/dist/src/aws/utils/cloudformationEvents.js +1 -596
- package/dist/src/aws/utils/index.d.ts +5 -0
- package/dist/src/aws/utils/index.js +1 -0
- package/dist/src/aws/utils/stackStatus.js +1 -90
- package/dist/src/events/index.d.ts +13 -0
- package/dist/src/events/index.js +1 -0
- package/dist/src/index.d.ts +34 -17
- package/dist/src/index.js +41 -21
- package/dist/src/orchestration/__tests__/cascadeTestHelpers.d.ts +12 -0
- package/dist/src/orchestration/__tests__/cascadeTestHelpers.js +78 -0
- package/dist/src/orchestration/activeDeploymentGuard.d.ts +10 -0
- package/dist/src/orchestration/activeDeploymentGuard.js +39 -0
- package/dist/src/orchestration/applicationDeploy.js +46 -229
- package/dist/src/orchestration/applicationDeployHelpers.d.ts +39 -0
- package/dist/src/orchestration/applicationDeployHelpers.js +223 -0
- package/dist/src/orchestration/applicationDestroy.d.ts +14 -0
- package/dist/src/orchestration/applicationDestroy.js +131 -0
- package/dist/src/orchestration/builders/dockerBuilder.d.ts +17 -0
- package/dist/src/orchestration/builders/dockerBuilder.js +98 -0
- package/dist/src/orchestration/builders/frameworkRegistry.d.ts +23 -0
- package/dist/src/orchestration/builders/frameworkRegistry.js +1 -0
- package/dist/src/orchestration/builders/index.d.ts +4 -0
- package/dist/src/orchestration/builders/index.js +1 -0
- package/dist/src/orchestration/builders/openNextBuilder.d.ts +21 -0
- package/dist/src/orchestration/builders/openNextBuilder.js +144 -0
- package/dist/src/orchestration/cascadeDestroyHelpers.d.ts +30 -0
- package/dist/src/orchestration/cascadeDestroyHelpers.js +1 -0
- package/dist/src/orchestration/cascadeHelpers.d.ts +46 -0
- package/dist/src/orchestration/cascadeHelpers.js +160 -0
- package/dist/src/orchestration/contextHelpers.d.ts +46 -2
- package/dist/src/orchestration/contextHelpers.js +93 -1
- package/dist/src/orchestration/destroy.d.ts +13 -0
- package/dist/src/orchestration/destroy.js +67 -0
- package/dist/src/orchestration/detectionPipeline.d.ts +2 -11
- package/dist/src/orchestration/detectionPipeline.js +29 -10
- package/dist/src/orchestration/dockerBuildHelper.d.ts +10 -0
- package/dist/src/orchestration/dockerBuildHelper.js +49 -0
- package/dist/src/orchestration/dockerInterface.d.ts +4 -2
- package/dist/src/orchestration/index.d.ts +8 -1
- package/dist/src/orchestration/index.js +1 -3
- package/dist/src/orchestration/manifestSecretParser.d.ts +11 -0
- package/dist/src/orchestration/manifestSecretParser.js +1 -0
- package/dist/src/orchestration/openNextBuild.d.ts +28 -0
- package/dist/src/orchestration/openNextBuild.js +243 -0
- package/dist/src/orchestration/organisationDeploy.js +110 -233
- package/dist/src/orchestration/organisationDestroy.d.ts +24 -0
- package/dist/src/orchestration/organisationDestroy.js +189 -0
- package/dist/src/orchestration/organisationSetup.d.ts +6 -4
- package/dist/src/orchestration/organisationSetup.js +28 -8
- package/dist/src/orchestration/resolveOperation.js +68 -6
- package/dist/src/orchestration/serviceFactory.d.ts +4 -0
- package/dist/src/orchestration/serviceFactory.js +1 -16
- package/dist/src/orchestration/spawnHelpers.d.ts +47 -0
- package/dist/src/orchestration/spawnHelpers.js +1 -0
- package/dist/src/orchestration/stackCleanup.d.ts +39 -0
- package/dist/src/orchestration/stackCleanup.js +1 -0
- package/dist/src/orchestration/welcomeImageHelper.d.ts +15 -0
- package/dist/src/orchestration/welcomeImageHelper.js +64 -0
- package/dist/src/services/application/ApplicationStackService.d.ts +21 -30
- package/dist/src/services/application/ApplicationStackService.js +16 -234
- package/dist/src/services/application/applicationStackHelpers.d.ts +46 -0
- package/dist/src/services/application/applicationStackHelpers.js +248 -0
- package/dist/src/services/application/index.d.ts +1 -0
- package/dist/src/services/application/index.js +1 -1
- package/dist/src/services/index.d.ts +6 -0
- package/dist/src/services/index.js +1 -0
- package/dist/src/services/infrastructure/CdkArgumentBuilder.js +1 -67
- package/dist/src/services/infrastructure/CdkCommandRunner.d.ts +10 -2
- package/dist/src/services/infrastructure/CdkCommandRunner.js +18 -15
- package/dist/src/services/infrastructure/CdkErrorFormatter.js +16 -194
- package/dist/src/services/infrastructure/CdkEventMonitoring.js +1 -41
- package/dist/src/services/infrastructure/CdkOutputAnalyser.js +1 -1
- package/dist/src/services/infrastructure/CdkOutputParser.js +2 -33
- package/dist/src/services/infrastructure/CdkProcessManager.d.ts +5 -0
- package/dist/src/services/infrastructure/CdkProcessManager.js +81 -47
- package/dist/src/services/infrastructure/CdkService.d.ts +7 -53
- package/dist/src/services/infrastructure/CdkService.js +41 -83
- package/dist/src/services/infrastructure/CdkServiceTypes.d.ts +50 -0
- package/dist/src/services/infrastructure/CdkServiceTypes.js +0 -0
- package/dist/src/services/infrastructure/CloudFormationService.js +9 -10
- package/dist/src/services/infrastructure/ICdkProcessManager.d.ts +27 -0
- package/dist/src/services/infrastructure/ICdkProcessManager.js +1 -0
- package/dist/src/services/infrastructure/__tests__/cloudFormationTestHelpers.d.ts +9 -0
- package/dist/src/services/infrastructure/__tests__/cloudFormationTestHelpers.js +1 -0
- package/dist/src/services/infrastructure/cdkServiceHelpers.d.ts +9 -0
- package/dist/src/services/infrastructure/cdkServiceHelpers.js +1 -0
- package/dist/src/services/infrastructure/constructMapEnrichment.d.ts +7 -0
- package/dist/src/services/infrastructure/constructMapEnrichment.js +1 -0
- package/dist/src/services/infrastructure/index.d.ts +3 -1
- package/dist/src/services/infrastructure/index.js +1 -7
- package/dist/src/services/supporting/TemplateHashService.js +1 -1
- package/dist/src/services/supporting/helpers.js +1 -81
- package/dist/src/services/supporting/index.js +1 -3
- package/dist/src/steps/index.d.ts +1 -0
- package/dist/src/steps/index.js +1 -0
- package/dist/src/steps/stepRegistry.d.ts +71 -0
- package/dist/src/steps/stepRegistry.js +505 -0
- package/dist/src/types/FjallState.js +1 -118
- package/dist/src/types/ProgressEvent.js +1 -48
- package/dist/src/types/application/ApplicationServiceTypes.js +1 -30
- package/dist/src/types/application/index.js +1 -1
- package/dist/src/types/callbacks.d.ts +76 -4
- package/dist/src/types/callbacks.js +0 -1
- package/dist/src/types/constants.d.ts +2 -0
- package/dist/src/types/constants.js +1 -6
- package/dist/src/types/credentials.js +0 -1
- package/dist/src/types/deployment/DeploymentServiceTypes.d.ts +5 -2
- package/dist/src/types/deployment/DeploymentServiceTypes.js +1 -1
- package/dist/src/types/deployment/DeploymentTypes.js +0 -1
- package/dist/src/types/deployment/cloudformation.js +0 -1
- package/dist/src/types/deployment/index.d.ts +3 -1
- package/dist/src/types/deployment/index.js +1 -1
- package/dist/src/types/deployment/parallel.js +1 -10
- package/dist/src/types/deploymentEventSchema.d.ts +158 -0
- package/dist/src/types/deploymentEventSchema.js +1 -0
- package/dist/src/types/detection.d.ts +22 -0
- package/dist/src/types/detection.js +1 -0
- package/dist/src/types/entitlements.d.ts +31 -0
- package/dist/src/types/entitlements.js +0 -0
- package/dist/src/types/errors/CdkError.js +1 -20
- package/dist/src/types/errors/ServiceError.d.ts +2 -1
- package/dist/src/types/errors/ServiceError.js +1 -119
- package/dist/src/types/errors/index.d.ts +2 -0
- package/dist/src/types/errors/index.js +1 -0
- package/dist/src/types/events.d.ts +3 -9
- package/dist/src/types/events.js +0 -5
- package/dist/src/types/frameworkBuilder.d.ts +96 -0
- package/dist/src/types/frameworkBuilder.js +8 -0
- package/dist/src/types/index.d.ts +19 -4
- package/dist/src/types/index.js +1 -9
- package/dist/src/types/operations.d.ts +3 -2
- package/dist/src/types/operations.js +1 -285
- package/dist/src/types/orgConfig.d.ts +2 -10
- package/dist/src/types/orgConfig.js +0 -11
- package/dist/src/types/params.d.ts +60 -1
- package/dist/src/types/patternDetection.d.ts +14 -16
- package/dist/src/types/patternDetection.js +14 -18
- package/dist/src/types/patternTypes.d.ts +19 -0
- package/dist/src/types/patternTypes.js +1 -0
- package/dist/src/types/stepDefinitions.d.ts +163 -0
- package/dist/src/types/stepDefinitions.js +98 -0
- package/dist/src/types/validation.js +0 -1
- package/dist/src/util/dockerfileDetection.d.ts +5 -0
- package/dist/src/util/dockerfileDetection.js +1 -0
- package/dist/src/util/index.d.ts +4 -3
- package/dist/src/util/index.js +1 -3
- package/dist/src/util/sequencedCallbacks.d.ts +44 -0
- package/dist/src/util/sequencedCallbacks.js +1 -0
- package/package.json +49 -8
- package/dist/src/aws/utils/CloudFormationFailureAnalyser.d.ts +0 -32
- package/dist/src/aws/utils/CloudFormationFailureAnalyser.js +0 -228
- package/dist/src/aws/utils/errors.d.ts +0 -26
- package/dist/src/aws/utils/errors.js +0 -59
- package/dist/src/util/fsHelpers.d.ts +0 -4
- package/dist/src/util/fsHelpers.js +0 -16
- package/dist/src/util/securityHelpers.d.ts +0 -31
- package/dist/src/util/securityHelpers.js +0 -124
- package/dist/src/util/singleton.d.ts +0 -2
- package/dist/src/util/singleton.js +0 -9
- package/dist/src/util/sleep.d.ts +0 -4
- package/dist/src/util/sleep.js +0 -4
|
@@ -5,9 +5,18 @@ import type { ResourceEvent } from "../../types/events.js";
|
|
|
5
5
|
import type { DeploymentContext } from "../../types/deployment/DeploymentTypes.js";
|
|
6
6
|
import { type ApplicationStack } from "../../types/operations.js";
|
|
7
7
|
import type { ParallelDeploymentResult } from "../../types/deployment/parallel.js";
|
|
8
|
-
import {
|
|
8
|
+
import type { AppResourceFlags } from "../../types/patternDetection.js";
|
|
9
9
|
import { type Result } from "@fjall/generator";
|
|
10
10
|
import { ApplicationError, type StackDeploymentData } from "../../types/application/ApplicationServiceTypes.js";
|
|
11
|
+
interface StackCallbacks {
|
|
12
|
+
onOutput?: (chunk: string) => void;
|
|
13
|
+
onResourceProgress?: (event: ResourceEvent) => void;
|
|
14
|
+
}
|
|
15
|
+
interface ParallelStackCallbacks {
|
|
16
|
+
onOutput?: (chunk: string, stackId: ApplicationStack) => void;
|
|
17
|
+
onResourceProgress?: (event: ResourceEvent, stackId: ApplicationStack) => void;
|
|
18
|
+
onStackComplete?: (stack: ApplicationStack, success: boolean, duration: number, error?: Error) => void;
|
|
19
|
+
}
|
|
11
20
|
/**
|
|
12
21
|
* Service for deploying and destroying individual CloudFormation stacks.
|
|
13
22
|
*
|
|
@@ -20,33 +29,26 @@ import { ApplicationError, type StackDeploymentData } from "../../types/applicat
|
|
|
20
29
|
export declare class ApplicationStackService {
|
|
21
30
|
private cdkService;
|
|
22
31
|
private cloudFormationService;
|
|
23
|
-
private aws
|
|
24
|
-
constructor(cdkService: CdkService, cloudFormationService: CloudFormationService);
|
|
25
|
-
|
|
32
|
+
private aws?;
|
|
33
|
+
constructor(cdkService: CdkService, cloudFormationService: CloudFormationService, aws?: AwsProvider | undefined);
|
|
34
|
+
/**
|
|
35
|
+
* Delegate to CdkService.runCdkSynth — exposed for destroyAllStacks orchestration.
|
|
36
|
+
*/
|
|
37
|
+
runCdkSynth(context: DeploymentContext): ReturnType<CdkService["runCdkSynth"]>;
|
|
26
38
|
/**
|
|
27
39
|
* Deploy a specific stack type for an application
|
|
28
40
|
*/
|
|
29
|
-
deployStack(stackType: ApplicationStack, context: DeploymentContext, callbacks?:
|
|
30
|
-
onOutput?: (chunk: string) => void;
|
|
31
|
-
onResourceProgress?: (event: ResourceEvent) => void;
|
|
32
|
-
}): Promise<Result<StackDeploymentData, ApplicationError>>;
|
|
41
|
+
deployStack(stackType: ApplicationStack, context: DeploymentContext, callbacks?: StackCallbacks): Promise<Result<StackDeploymentData, ApplicationError>>;
|
|
33
42
|
/**
|
|
34
43
|
* Deploy multiple stacks in parallel within a deployment phase.
|
|
35
44
|
*
|
|
36
45
|
* Uses Promise.allSettled to ensure all stacks are attempted even if some fail.
|
|
37
46
|
*/
|
|
38
|
-
deployStacksInParallel(stacks: readonly ApplicationStack[], context: DeploymentContext, callbacks?:
|
|
39
|
-
onOutput?: (chunk: string, stackId: ApplicationStack) => void;
|
|
40
|
-
onResourceProgress?: (event: ResourceEvent, stackId: ApplicationStack) => void;
|
|
41
|
-
onStackComplete?: (stack: ApplicationStack, success: boolean, duration: number, error?: Error) => void;
|
|
42
|
-
}): Promise<Result<ParallelDeploymentResult[], ApplicationError>>;
|
|
47
|
+
deployStacksInParallel(stacks: readonly ApplicationStack[], context: DeploymentContext, callbacks?: ParallelStackCallbacks): Promise<Result<ParallelDeploymentResult[], ApplicationError>>;
|
|
43
48
|
/**
|
|
44
49
|
* Destroy a specific stack type
|
|
45
50
|
*/
|
|
46
|
-
destroyStack(stackType: ApplicationStack, context: DeploymentContext, callbacks?:
|
|
47
|
-
onOutput?: (chunk: string) => void;
|
|
48
|
-
onResourceProgress?: (event: ResourceEvent) => void;
|
|
49
|
-
}, useCdkOut?: boolean): Promise<Result<StackDeploymentData, ApplicationError>>;
|
|
51
|
+
destroyStack(stackType: ApplicationStack, context: DeploymentContext, callbacks?: StackCallbacks, useCdkOut?: boolean): Promise<Result<StackDeploymentData, ApplicationError>>;
|
|
50
52
|
/**
|
|
51
53
|
* Destroy multiple stacks in parallel within a destroy phase.
|
|
52
54
|
*
|
|
@@ -54,11 +56,7 @@ export declare class ApplicationStackService {
|
|
|
54
56
|
* be run beforehand. Multiple CDK commands cannot run in parallel because they
|
|
55
57
|
* all try to synthesise to the same cdk.out directory.
|
|
56
58
|
*/
|
|
57
|
-
destroyStacksInParallel(stacks: readonly ApplicationStack[], context: DeploymentContext, callbacks?:
|
|
58
|
-
onOutput?: (chunk: string, stackId: ApplicationStack) => void;
|
|
59
|
-
onResourceProgress?: (event: ResourceEvent, stackId: ApplicationStack) => void;
|
|
60
|
-
onStackComplete?: (stack: ApplicationStack, success: boolean, duration: number, error?: Error) => void;
|
|
61
|
-
}, useCdkOut?: boolean): Promise<Result<ParallelDeploymentResult[], ApplicationError>>;
|
|
59
|
+
destroyStacksInParallel(stacks: readonly ApplicationStack[], context: DeploymentContext, callbacks?: ParallelStackCallbacks, useCdkOut?: boolean): Promise<Result<ParallelDeploymentResult[], ApplicationError>>;
|
|
62
60
|
/**
|
|
63
61
|
* Get stack outputs for a deployed stack
|
|
64
62
|
*/
|
|
@@ -82,12 +80,5 @@ export declare class ApplicationStackService {
|
|
|
82
80
|
}, resources?: AppResourceFlags): Promise<Result<{
|
|
83
81
|
message: string;
|
|
84
82
|
}, ApplicationError>>;
|
|
85
|
-
/**
|
|
86
|
-
* Convert "doesn't exist" errors to success for destroy operations.
|
|
87
|
-
*/
|
|
88
|
-
private convertDestroyResultIfNotExists;
|
|
89
|
-
/**
|
|
90
|
-
* Handle destroy errors and determine if we should continue or fail
|
|
91
|
-
*/
|
|
92
|
-
private handleDestroyError;
|
|
93
83
|
}
|
|
84
|
+
export {};
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { STACK_NOT_FOUND_PATTERN, CDK_NO_STACKS_MATCH
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { success, failure, isFailure } from "@fjall/generator";
|
|
1
|
+
import { STACK_NOT_FOUND_PATTERN, CDK_NO_STACKS_MATCH } from "../../types/constants.js";
|
|
2
|
+
import { getApplicationStackName } from "../../types/operations.js";
|
|
3
|
+
import { success, failure } from "@fjall/generator";
|
|
5
4
|
import { ApplicationError } from "../../types/application/ApplicationServiceTypes.js";
|
|
6
|
-
import { logger } from "@fjall/util";
|
|
5
|
+
import { logger } from "@fjall/util/logger";
|
|
7
6
|
import { convertCloudFormationOutputsToRecord } from "../supporting/helpers.js";
|
|
7
|
+
import { destroyAllStacks, mapSettledResults, resolveWebsiteUrl } from "./applicationStackHelpers.js";
|
|
8
8
|
/**
|
|
9
9
|
* Service for deploying and destroying individual CloudFormation stacks.
|
|
10
10
|
*
|
|
@@ -18,13 +18,17 @@ export class ApplicationStackService {
|
|
|
18
18
|
cdkService;
|
|
19
19
|
cloudFormationService;
|
|
20
20
|
aws;
|
|
21
|
-
constructor(cdkService, cloudFormationService) {
|
|
21
|
+
constructor(cdkService, cloudFormationService, aws) {
|
|
22
22
|
this.cdkService = cdkService;
|
|
23
23
|
this.cloudFormationService = cloudFormationService;
|
|
24
|
-
}
|
|
25
|
-
setAwsContext(aws) {
|
|
26
24
|
this.aws = aws;
|
|
27
25
|
}
|
|
26
|
+
/**
|
|
27
|
+
* Delegate to CdkService.runCdkSynth — exposed for destroyAllStacks orchestration.
|
|
28
|
+
*/
|
|
29
|
+
async runCdkSynth(context) {
|
|
30
|
+
return this.cdkService.runCdkSynth(context);
|
|
31
|
+
}
|
|
28
32
|
/**
|
|
29
33
|
* Deploy a specific stack type for an application
|
|
30
34
|
*/
|
|
@@ -111,20 +115,7 @@ export class ApplicationStackService {
|
|
|
111
115
|
onStackCompleteCallback?.(stack, result.success, duration, result.success ? undefined : result.error);
|
|
112
116
|
return deploymentResult;
|
|
113
117
|
}));
|
|
114
|
-
|
|
115
|
-
if (settledResult.status === "fulfilled") {
|
|
116
|
-
return settledResult.value;
|
|
117
|
-
}
|
|
118
|
-
return {
|
|
119
|
-
stack: stacks[index],
|
|
120
|
-
success: false,
|
|
121
|
-
error: settledResult.reason instanceof Error
|
|
122
|
-
? settledResult.reason
|
|
123
|
-
: new Error(String(settledResult.reason)),
|
|
124
|
-
duration: 0
|
|
125
|
-
};
|
|
126
|
-
});
|
|
127
|
-
return success(deployResults);
|
|
118
|
+
return success(mapSettledResults(results, stacks));
|
|
128
119
|
}
|
|
129
120
|
/**
|
|
130
121
|
* Destroy a specific stack type
|
|
@@ -191,20 +182,7 @@ export class ApplicationStackService {
|
|
|
191
182
|
onStackCompleteCallback?.(stack, destroyResult.success, duration, destroyResult.success ? undefined : destroyResult.error);
|
|
192
183
|
return destroyResult;
|
|
193
184
|
}));
|
|
194
|
-
|
|
195
|
-
if (settledResult.status === "fulfilled") {
|
|
196
|
-
return settledResult.value;
|
|
197
|
-
}
|
|
198
|
-
return {
|
|
199
|
-
stack: stacks[index],
|
|
200
|
-
success: false,
|
|
201
|
-
error: settledResult.reason instanceof Error
|
|
202
|
-
? settledResult.reason
|
|
203
|
-
: new Error(String(settledResult.reason)),
|
|
204
|
-
duration: 0
|
|
205
|
-
};
|
|
206
|
-
});
|
|
207
|
-
return success(destroyResults);
|
|
185
|
+
return success(mapSettledResults(results, stacks));
|
|
208
186
|
}
|
|
209
187
|
/**
|
|
210
188
|
* Get stack outputs for a deployed stack
|
|
@@ -228,209 +206,13 @@ export class ApplicationStackService {
|
|
|
228
206
|
* Checks Compute stack for LoadBalancer URL, then CDN stack for CloudFront distribution.
|
|
229
207
|
*/
|
|
230
208
|
async resolveWebsiteUrl(appName) {
|
|
231
|
-
|
|
232
|
-
const computeStackName = getApplicationStackName(appName, APPLICATION_STACKS.COMPUTE);
|
|
233
|
-
const computeOutputs = await this.cloudFormationService.getStackOutputs(computeStackName);
|
|
234
|
-
if (computeOutputs.success && computeOutputs.data.length > 0) {
|
|
235
|
-
const lbUrl = computeOutputs.data.find((o) => o.OutputKey?.includes("LoadBalancerUrl"));
|
|
236
|
-
if (lbUrl?.OutputValue) {
|
|
237
|
-
return lbUrl.OutputValue.toLowerCase();
|
|
238
|
-
}
|
|
239
|
-
const lbDns = computeOutputs.data.find((o) => o.OutputKey?.includes("LoadBalancerDnsName"));
|
|
240
|
-
if (lbDns?.OutputValue) {
|
|
241
|
-
return `http://${lbDns.OutputValue.toLowerCase()}`;
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
const cdnStackName = getApplicationStackName(appName, APPLICATION_STACKS.CDN);
|
|
245
|
-
const cdnOutputs = await this.cloudFormationService.getStackOutputs(cdnStackName);
|
|
246
|
-
if (cdnOutputs.success && cdnOutputs.data.length > 0) {
|
|
247
|
-
const dist = cdnOutputs.data.find((o) => o.OutputKey?.includes("DistributionDomainName"));
|
|
248
|
-
if (dist?.OutputValue) {
|
|
249
|
-
return `https://${dist.OutputValue.toLowerCase()}`;
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
return undefined;
|
|
253
|
-
}
|
|
254
|
-
catch (error) {
|
|
255
|
-
logger.debug("ApplicationStackService", "URL resolution failed", {
|
|
256
|
-
error: error instanceof Error ? error.message : String(error)
|
|
257
|
-
});
|
|
258
|
-
return undefined;
|
|
259
|
-
}
|
|
209
|
+
return resolveWebsiteUrl(appName, this.cloudFormationService);
|
|
260
210
|
}
|
|
261
211
|
/**
|
|
262
212
|
* Destroy all stacks for an application in reverse order.
|
|
263
213
|
* Supports parallel destruction for OpenNext patterns (nextjs/payload).
|
|
264
214
|
*/
|
|
265
215
|
async destroyAllStacks(context, callbacks, resources) {
|
|
266
|
-
|
|
267
|
-
const pattern = patternResult.pattern;
|
|
268
|
-
const appName = context.target;
|
|
269
|
-
if (isOpenNextPattern(pattern)) {
|
|
270
|
-
const destroyGroups = getParallelDestroyGroups();
|
|
271
|
-
for (const group of destroyGroups) {
|
|
272
|
-
const stacks = group.stacks;
|
|
273
|
-
if (stacks.length === 1) {
|
|
274
|
-
const stackType = stacks[0];
|
|
275
|
-
const stackName = getApplicationStackName(appName, stackType);
|
|
276
|
-
callbacks?.onStackStart?.(stackType, stackName);
|
|
277
|
-
const result = await this.destroyStack(stackType, context, {
|
|
278
|
-
onOutput: callbacks?.onOutput
|
|
279
|
-
? (chunk) => callbacks.onOutput?.(chunk, stackType)
|
|
280
|
-
: undefined,
|
|
281
|
-
onResourceProgress: callbacks?.onResourceProgress
|
|
282
|
-
? (event) => callbacks.onResourceProgress?.(event, stackType)
|
|
283
|
-
: undefined
|
|
284
|
-
});
|
|
285
|
-
const finalResult = this.convertDestroyResultIfNotExists(result, stackType, appName);
|
|
286
|
-
if (callbacks?.onStackComplete) {
|
|
287
|
-
await callbacks.onStackComplete(stackType, finalResult);
|
|
288
|
-
}
|
|
289
|
-
const errorResult = this.handleDestroyError(finalResult, stackType);
|
|
290
|
-
if (errorResult) {
|
|
291
|
-
if (errorResult.continue)
|
|
292
|
-
continue;
|
|
293
|
-
return errorResult.result;
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
else {
|
|
297
|
-
const synthResult = await this.cdkService.runCdkSynth(context);
|
|
298
|
-
if (!synthResult.success) {
|
|
299
|
-
return failure(new ApplicationError(synthResult.error, {
|
|
300
|
-
errorType: "synth_failed",
|
|
301
|
-
appName,
|
|
302
|
-
operation: "destroy-parallel-synth"
|
|
303
|
-
}));
|
|
304
|
-
}
|
|
305
|
-
callbacks?.onParallelPhaseStart?.(stacks, group.description);
|
|
306
|
-
for (const stackType of stacks) {
|
|
307
|
-
callbacks?.onStackStart?.(stackType, getApplicationStackName(appName, stackType));
|
|
308
|
-
}
|
|
309
|
-
const parallelResult = await this.destroyStacksInParallel(stacks, context, {
|
|
310
|
-
onOutput: callbacks?.onOutput,
|
|
311
|
-
onResourceProgress: callbacks?.onResourceProgress,
|
|
312
|
-
onStackComplete: async (stack, stackSuccess, _duration, error) => {
|
|
313
|
-
const result = stackSuccess
|
|
314
|
-
? success({
|
|
315
|
-
stackName: `${appName}${stack}`,
|
|
316
|
-
stackType: stack,
|
|
317
|
-
outputs: {}
|
|
318
|
-
})
|
|
319
|
-
: failure(error instanceof ApplicationError
|
|
320
|
-
? error
|
|
321
|
-
: new ApplicationError(error?.message ?? "Stack destruction failed", {
|
|
322
|
-
errorType: "destroy_failed",
|
|
323
|
-
appName,
|
|
324
|
-
operation: `destroy-${stack}`
|
|
325
|
-
}));
|
|
326
|
-
await callbacks?.onStackComplete?.(stack, result);
|
|
327
|
-
}
|
|
328
|
-
}, true);
|
|
329
|
-
if (parallelResult.success) {
|
|
330
|
-
callbacks?.onParallelPhaseComplete?.(parallelResult.data);
|
|
331
|
-
const failures = parallelResult.data.filter((r) => !r.success);
|
|
332
|
-
if (failures.length > 0) {
|
|
333
|
-
const realFailures = failures.filter((f) => f.error &&
|
|
334
|
-
!f.error.message.includes(STACK_NOT_FOUND_PATTERN) &&
|
|
335
|
-
!f.error.message.includes(CDK_NO_STACKS_MATCH));
|
|
336
|
-
if (realFailures.length > 0) {
|
|
337
|
-
const failedStacks = realFailures
|
|
338
|
-
.map((f) => f.stack)
|
|
339
|
-
.join(", ");
|
|
340
|
-
const errorDetails = realFailures
|
|
341
|
-
.map((f) => `${f.stack}: ${f.error?.message || "Unknown error"}`)
|
|
342
|
-
.join("\n");
|
|
343
|
-
return failure(new ApplicationError(`Failed to destroy stacks: ${failedStacks}\n\n${errorDetails}`, {
|
|
344
|
-
errorType: "destroy_failed",
|
|
345
|
-
appName,
|
|
346
|
-
operation: "destroy-parallel"
|
|
347
|
-
}));
|
|
348
|
-
}
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
}
|
|
353
|
-
}
|
|
354
|
-
else {
|
|
355
|
-
const stackOrder = getApplicationDestroyOrder({ pattern, resources });
|
|
356
|
-
for (const stackType of stackOrder) {
|
|
357
|
-
const stackName = getApplicationStackName(appName, stackType);
|
|
358
|
-
callbacks?.onStackStart?.(stackType, stackName);
|
|
359
|
-
const result = await this.destroyStack(stackType, context, {
|
|
360
|
-
onOutput: callbacks?.onOutput
|
|
361
|
-
? (chunk) => callbacks.onOutput?.(chunk, stackType)
|
|
362
|
-
: undefined,
|
|
363
|
-
onResourceProgress: callbacks?.onResourceProgress
|
|
364
|
-
? (event) => callbacks.onResourceProgress?.(event, stackType)
|
|
365
|
-
: undefined
|
|
366
|
-
});
|
|
367
|
-
const finalResult = this.convertDestroyResultIfNotExists(result, stackType, appName);
|
|
368
|
-
if (callbacks?.onStackComplete) {
|
|
369
|
-
await callbacks.onStackComplete(stackType, finalResult);
|
|
370
|
-
}
|
|
371
|
-
const errorResult = this.handleDestroyError(finalResult, stackType);
|
|
372
|
-
if (errorResult) {
|
|
373
|
-
if (errorResult.continue)
|
|
374
|
-
continue;
|
|
375
|
-
return errorResult.result;
|
|
376
|
-
}
|
|
377
|
-
}
|
|
378
|
-
}
|
|
379
|
-
return success({ message: "All stacks destroyed" });
|
|
380
|
-
}
|
|
381
|
-
/**
|
|
382
|
-
* Convert "doesn't exist" errors to success for destroy operations.
|
|
383
|
-
*/
|
|
384
|
-
convertDestroyResultIfNotExists(result, stackType, appName) {
|
|
385
|
-
if (result.success)
|
|
386
|
-
return result;
|
|
387
|
-
const errorMessage = isFailure(result) ? result.error.message : "";
|
|
388
|
-
const isAlreadyDeleted = errorMessage.includes(STACK_NOT_FOUND_PATTERN) ||
|
|
389
|
-
errorMessage.includes(CDK_NO_STACKS_MATCH);
|
|
390
|
-
if (isAlreadyDeleted) {
|
|
391
|
-
return success({
|
|
392
|
-
stackName: getApplicationStackName(appName, stackType),
|
|
393
|
-
stackType,
|
|
394
|
-
outputs: {}
|
|
395
|
-
});
|
|
396
|
-
}
|
|
397
|
-
return result;
|
|
398
|
-
}
|
|
399
|
-
/**
|
|
400
|
-
* Handle destroy errors and determine if we should continue or fail
|
|
401
|
-
*/
|
|
402
|
-
handleDestroyError(result, stackType) {
|
|
403
|
-
if (result.success)
|
|
404
|
-
return null;
|
|
405
|
-
const errorMessage = isFailure(result) ? result.error.message : "";
|
|
406
|
-
if (errorMessage.includes("TSError") ||
|
|
407
|
-
errorMessage.includes("Unable to compile TypeScript") ||
|
|
408
|
-
errorMessage.includes("Subprocess exited with error")) {
|
|
409
|
-
const tsErrorMatch = errorMessage.match(/infrastructure\.ts\(\d+,\d+\): error TS\d+: .+/);
|
|
410
|
-
const errorDetail = tsErrorMatch
|
|
411
|
-
? tsErrorMatch[0]
|
|
412
|
-
: `Check ${INFRASTRUCTURE_FILENAME} for syntax errors`;
|
|
413
|
-
return {
|
|
414
|
-
continue: false,
|
|
415
|
-
result: failure(new ApplicationError(`CDK synthesis failed: ${errorDetail}`, {
|
|
416
|
-
errorType: "synth_failed",
|
|
417
|
-
operation: `destroy-${stackType}`
|
|
418
|
-
}))
|
|
419
|
-
};
|
|
420
|
-
}
|
|
421
|
-
if (errorMessage.includes(STACK_NOT_FOUND_PATTERN) ||
|
|
422
|
-
errorMessage.includes(CDK_NO_STACKS_MATCH)) {
|
|
423
|
-
return {
|
|
424
|
-
continue: true,
|
|
425
|
-
result: success({ message: "Stack already deleted" })
|
|
426
|
-
};
|
|
427
|
-
}
|
|
428
|
-
return {
|
|
429
|
-
continue: false,
|
|
430
|
-
result: failure(new ApplicationError(`Failed to destroy ${stackType} stack: ${errorMessage}`, {
|
|
431
|
-
errorType: "destroy_failed",
|
|
432
|
-
operation: `destroy-${stackType}`
|
|
433
|
-
}))
|
|
434
|
-
};
|
|
216
|
+
return destroyAllStacks(this, context, callbacks, resources);
|
|
435
217
|
}
|
|
436
218
|
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { ResourceEvent } from "../../types/events.js";
|
|
2
|
+
import type { DeploymentContext } from "../../types/deployment/DeploymentTypes.js";
|
|
3
|
+
import { type ApplicationStack } from "../../types/operations.js";
|
|
4
|
+
import type { ParallelDeploymentResult } from "../../types/deployment/parallel.js";
|
|
5
|
+
import type { AppResourceFlags } from "../../types/patternDetection.js";
|
|
6
|
+
import { type Result } from "@fjall/generator";
|
|
7
|
+
import { ApplicationError, type StackDeploymentData } from "../../types/application/ApplicationServiceTypes.js";
|
|
8
|
+
import type { ApplicationStackService } from "./ApplicationStackService.js";
|
|
9
|
+
import type { CloudFormationService } from "../infrastructure/CloudFormationService.js";
|
|
10
|
+
/**
|
|
11
|
+
* Convert "doesn't exist" errors to success for destroy operations.
|
|
12
|
+
*/
|
|
13
|
+
export declare function convertDestroyResultIfNotExists(result: Result<StackDeploymentData, ApplicationError>, stackType: ApplicationStack, appName: string): Result<StackDeploymentData, ApplicationError>;
|
|
14
|
+
/**
|
|
15
|
+
* Handle destroy errors and determine if we should continue or fail
|
|
16
|
+
*/
|
|
17
|
+
export declare function handleDestroyError(result: Result<StackDeploymentData, ApplicationError>, stackType: ApplicationStack): {
|
|
18
|
+
continue: boolean;
|
|
19
|
+
result: Result<{
|
|
20
|
+
message: string;
|
|
21
|
+
}, ApplicationError>;
|
|
22
|
+
} | null;
|
|
23
|
+
/**
|
|
24
|
+
* Destroy all stacks for an application in reverse order.
|
|
25
|
+
* Supports parallel destruction for OpenNext patterns (nextjs/payload).
|
|
26
|
+
*/
|
|
27
|
+
export declare function destroyAllStacks(service: ApplicationStackService, context: DeploymentContext, callbacks?: {
|
|
28
|
+
onOutput?: (chunk: string, stackId?: ApplicationStack) => void;
|
|
29
|
+
onResourceProgress?: (event: ResourceEvent, stackId?: ApplicationStack) => void;
|
|
30
|
+
onStackStart?: (stackType: ApplicationStack, stackName: string) => void;
|
|
31
|
+
onStackComplete?: (stackType: ApplicationStack, result: Result<StackDeploymentData, ApplicationError>) => void | Promise<void>;
|
|
32
|
+
onParallelPhaseStart?: (stacks: readonly ApplicationStack[], description: string) => void;
|
|
33
|
+
onParallelPhaseComplete?: (results: ParallelDeploymentResult[]) => void;
|
|
34
|
+
}, resources?: AppResourceFlags): Promise<Result<{
|
|
35
|
+
message: string;
|
|
36
|
+
}, ApplicationError>>;
|
|
37
|
+
/**
|
|
38
|
+
* Map Promise.allSettled results to ParallelDeploymentResult[], handling
|
|
39
|
+
* rejected promises with a fallback stack identity.
|
|
40
|
+
*/
|
|
41
|
+
export declare function mapSettledResults(settled: Array<PromiseSettledResult<ParallelDeploymentResult>>, stacks: readonly ApplicationStack[]): ParallelDeploymentResult[];
|
|
42
|
+
/**
|
|
43
|
+
* Resolve the website URL for an application from CloudFormation stack outputs.
|
|
44
|
+
* Checks Compute stack for LoadBalancer URL, then CDN stack for CloudFront distribution.
|
|
45
|
+
*/
|
|
46
|
+
export declare function resolveWebsiteUrl(appName: string, cloudFormationService: CloudFormationService): Promise<string | undefined>;
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
import { STACK_NOT_FOUND_PATTERN, CDK_NO_STACKS_MATCH, INFRASTRUCTURE_FILENAME } from "../../types/constants.js";
|
|
2
|
+
import { APPLICATION_STACKS, getApplicationStackName, getApplicationDestroyOrder, getParallelDestroyGroups } from "../../types/operations.js";
|
|
3
|
+
import { FrameworkRegistry } from "../../orchestration/builders/frameworkRegistry.js";
|
|
4
|
+
import { success, failure, isFailure } from "@fjall/generator";
|
|
5
|
+
import { ApplicationError } from "../../types/application/ApplicationServiceTypes.js";
|
|
6
|
+
import { logger } from "@fjall/util/logger";
|
|
7
|
+
/**
|
|
8
|
+
* Convert "doesn't exist" errors to success for destroy operations.
|
|
9
|
+
*/
|
|
10
|
+
export function convertDestroyResultIfNotExists(result, stackType, appName) {
|
|
11
|
+
if (result.success)
|
|
12
|
+
return result;
|
|
13
|
+
const errorMessage = isFailure(result) ? result.error.message : "";
|
|
14
|
+
const isAlreadyDeleted = errorMessage.includes(STACK_NOT_FOUND_PATTERN) ||
|
|
15
|
+
errorMessage.includes(CDK_NO_STACKS_MATCH);
|
|
16
|
+
if (isAlreadyDeleted) {
|
|
17
|
+
return success({
|
|
18
|
+
stackName: getApplicationStackName(appName, stackType),
|
|
19
|
+
stackType,
|
|
20
|
+
outputs: {},
|
|
21
|
+
skipped: true
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
return result;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Handle destroy errors and determine if we should continue or fail
|
|
28
|
+
*/
|
|
29
|
+
export function handleDestroyError(result, stackType) {
|
|
30
|
+
if (result.success)
|
|
31
|
+
return null;
|
|
32
|
+
const errorMessage = isFailure(result) ? result.error.message : "";
|
|
33
|
+
if (errorMessage.includes("TSError") ||
|
|
34
|
+
errorMessage.includes("Unable to compile TypeScript") ||
|
|
35
|
+
errorMessage.includes("Subprocess exited with error")) {
|
|
36
|
+
const tsErrorMatch = errorMessage.match(/infrastructure\.ts\(\d+,\d+\): error TS\d+: .+/);
|
|
37
|
+
const errorDetail = tsErrorMatch
|
|
38
|
+
? tsErrorMatch[0]
|
|
39
|
+
: `Check ${INFRASTRUCTURE_FILENAME} for syntax errors`;
|
|
40
|
+
return {
|
|
41
|
+
continue: false,
|
|
42
|
+
result: failure(new ApplicationError(`CDK synthesis failed: ${errorDetail}`, {
|
|
43
|
+
errorType: "synth_failed",
|
|
44
|
+
operation: `destroy-${stackType}`
|
|
45
|
+
}))
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
if (errorMessage.includes(STACK_NOT_FOUND_PATTERN) ||
|
|
49
|
+
errorMessage.includes(CDK_NO_STACKS_MATCH)) {
|
|
50
|
+
return {
|
|
51
|
+
continue: true,
|
|
52
|
+
result: success({ message: "Stack already deleted" })
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
return {
|
|
56
|
+
continue: false,
|
|
57
|
+
result: failure(new ApplicationError(`Failed to destroy ${stackType} stack: ${errorMessage}`, {
|
|
58
|
+
errorType: "destroy_failed",
|
|
59
|
+
operation: `destroy-${stackType}`
|
|
60
|
+
}))
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Destroy all stacks for an application in reverse order.
|
|
65
|
+
* Supports parallel destruction for OpenNext patterns (nextjs/payload).
|
|
66
|
+
*/
|
|
67
|
+
export async function destroyAllStacks(service, context, callbacks, resources) {
|
|
68
|
+
const appName = context.target;
|
|
69
|
+
// Use FrameworkRegistry to resolve builder and plan
|
|
70
|
+
const registry = FrameworkRegistry.createDefault();
|
|
71
|
+
const resolved = registry.resolve({ appPath: context.path });
|
|
72
|
+
// Determine destroy strategy from builder plan if available, else fall back to pattern detection
|
|
73
|
+
let useParallelDestroy;
|
|
74
|
+
let destroyOrder;
|
|
75
|
+
if (resolved) {
|
|
76
|
+
const plan = resolved.builder.plan({ appPath: context.path }, resolved.detection);
|
|
77
|
+
useParallelDestroy = plan.parallelDestroy;
|
|
78
|
+
destroyOrder = plan.destroyOrder;
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
// No builder matched — infrastructure.ts missing. Use sequential destroy with default order.
|
|
82
|
+
useParallelDestroy = false;
|
|
83
|
+
destroyOrder = getApplicationDestroyOrder({ pattern: null, resources });
|
|
84
|
+
}
|
|
85
|
+
if (useParallelDestroy) {
|
|
86
|
+
const destroyGroups = getParallelDestroyGroups();
|
|
87
|
+
for (const group of destroyGroups) {
|
|
88
|
+
const stacks = group.stacks;
|
|
89
|
+
if (stacks.length === 1) {
|
|
90
|
+
const stackType = stacks[0];
|
|
91
|
+
const stackName = getApplicationStackName(appName, stackType);
|
|
92
|
+
callbacks?.onStackStart?.(stackType, stackName);
|
|
93
|
+
const result = await service.destroyStack(stackType, context, {
|
|
94
|
+
onOutput: callbacks?.onOutput
|
|
95
|
+
? (chunk) => callbacks.onOutput?.(chunk, stackType)
|
|
96
|
+
: undefined,
|
|
97
|
+
onResourceProgress: callbacks?.onResourceProgress
|
|
98
|
+
? (event) => callbacks.onResourceProgress?.(event, stackType)
|
|
99
|
+
: undefined
|
|
100
|
+
});
|
|
101
|
+
const finalResult = convertDestroyResultIfNotExists(result, stackType, appName);
|
|
102
|
+
if (callbacks?.onStackComplete) {
|
|
103
|
+
await callbacks.onStackComplete(stackType, finalResult);
|
|
104
|
+
}
|
|
105
|
+
const errorResult = handleDestroyError(finalResult, stackType);
|
|
106
|
+
if (errorResult) {
|
|
107
|
+
if (errorResult.continue)
|
|
108
|
+
continue;
|
|
109
|
+
return errorResult.result;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
const synthResult = await service.runCdkSynth(context);
|
|
114
|
+
if (!synthResult.success) {
|
|
115
|
+
return failure(new ApplicationError(synthResult.error, {
|
|
116
|
+
errorType: "synth_failed",
|
|
117
|
+
appName,
|
|
118
|
+
operation: "destroy-parallel-synth"
|
|
119
|
+
}));
|
|
120
|
+
}
|
|
121
|
+
callbacks?.onParallelPhaseStart?.(stacks, group.description);
|
|
122
|
+
for (const stackType of stacks) {
|
|
123
|
+
callbacks?.onStackStart?.(stackType, getApplicationStackName(appName, stackType));
|
|
124
|
+
}
|
|
125
|
+
const parallelResult = await service.destroyStacksInParallel(stacks, context, {
|
|
126
|
+
onOutput: callbacks?.onOutput,
|
|
127
|
+
onResourceProgress: callbacks?.onResourceProgress,
|
|
128
|
+
onStackComplete: async (stack, stackSuccess, _duration, error) => {
|
|
129
|
+
const result = stackSuccess
|
|
130
|
+
? success({
|
|
131
|
+
stackName: getApplicationStackName(appName, stack),
|
|
132
|
+
stackType: stack,
|
|
133
|
+
outputs: {}
|
|
134
|
+
})
|
|
135
|
+
: failure(error instanceof ApplicationError
|
|
136
|
+
? error
|
|
137
|
+
: new ApplicationError(error?.message ?? "Stack destruction failed", {
|
|
138
|
+
errorType: "destroy_failed",
|
|
139
|
+
appName,
|
|
140
|
+
operation: `destroy-${stack}`
|
|
141
|
+
}));
|
|
142
|
+
await callbacks?.onStackComplete?.(stack, result);
|
|
143
|
+
}
|
|
144
|
+
}, true);
|
|
145
|
+
if (parallelResult.success) {
|
|
146
|
+
callbacks?.onParallelPhaseComplete?.(parallelResult.data);
|
|
147
|
+
const failures = parallelResult.data.filter((r) => !r.success);
|
|
148
|
+
if (failures.length > 0) {
|
|
149
|
+
const realFailures = failures.filter((f) => f.error &&
|
|
150
|
+
!f.error.message.includes(STACK_NOT_FOUND_PATTERN) &&
|
|
151
|
+
!f.error.message.includes(CDK_NO_STACKS_MATCH));
|
|
152
|
+
if (realFailures.length > 0) {
|
|
153
|
+
const failedStacks = realFailures.map((f) => f.stack).join(", ");
|
|
154
|
+
const errorDetails = realFailures
|
|
155
|
+
.map((f) => `${f.stack}: ${f.error?.message || "Unknown error"}`)
|
|
156
|
+
.join("\n");
|
|
157
|
+
return failure(new ApplicationError(`Failed to destroy stacks: ${failedStacks}\n\n${errorDetails}`, {
|
|
158
|
+
errorType: "destroy_failed",
|
|
159
|
+
appName,
|
|
160
|
+
operation: "destroy-parallel"
|
|
161
|
+
}));
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
else {
|
|
169
|
+
for (const stackType of destroyOrder) {
|
|
170
|
+
const stackName = getApplicationStackName(appName, stackType);
|
|
171
|
+
callbacks?.onStackStart?.(stackType, stackName);
|
|
172
|
+
const result = await service.destroyStack(stackType, context, {
|
|
173
|
+
onOutput: callbacks?.onOutput
|
|
174
|
+
? (chunk) => callbacks.onOutput?.(chunk, stackType)
|
|
175
|
+
: undefined,
|
|
176
|
+
onResourceProgress: callbacks?.onResourceProgress
|
|
177
|
+
? (event) => callbacks.onResourceProgress?.(event, stackType)
|
|
178
|
+
: undefined
|
|
179
|
+
});
|
|
180
|
+
const finalResult = convertDestroyResultIfNotExists(result, stackType, appName);
|
|
181
|
+
if (callbacks?.onStackComplete) {
|
|
182
|
+
await callbacks.onStackComplete(stackType, finalResult);
|
|
183
|
+
}
|
|
184
|
+
const errorResult = handleDestroyError(finalResult, stackType);
|
|
185
|
+
if (errorResult) {
|
|
186
|
+
if (errorResult.continue)
|
|
187
|
+
continue;
|
|
188
|
+
return errorResult.result;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
return success({ message: "All stacks destroyed" });
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Map Promise.allSettled results to ParallelDeploymentResult[], handling
|
|
196
|
+
* rejected promises with a fallback stack identity.
|
|
197
|
+
*/
|
|
198
|
+
export function mapSettledResults(settled, stacks) {
|
|
199
|
+
return settled.map((settledResult, index) => {
|
|
200
|
+
if (settledResult.status === "fulfilled") {
|
|
201
|
+
return settledResult.value;
|
|
202
|
+
}
|
|
203
|
+
const stack = stacks[index] ?? APPLICATION_STACKS.NETWORK;
|
|
204
|
+
return {
|
|
205
|
+
stack,
|
|
206
|
+
success: false,
|
|
207
|
+
error: settledResult.reason instanceof Error
|
|
208
|
+
? settledResult.reason
|
|
209
|
+
: new Error(String(settledResult.reason)),
|
|
210
|
+
duration: 0
|
|
211
|
+
};
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Resolve the website URL for an application from CloudFormation stack outputs.
|
|
216
|
+
* Checks Compute stack for LoadBalancer URL, then CDN stack for CloudFront distribution.
|
|
217
|
+
*/
|
|
218
|
+
export async function resolveWebsiteUrl(appName, cloudFormationService) {
|
|
219
|
+
try {
|
|
220
|
+
const computeStackName = getApplicationStackName(appName, APPLICATION_STACKS.COMPUTE);
|
|
221
|
+
const computeOutputs = await cloudFormationService.getStackOutputs(computeStackName);
|
|
222
|
+
if (computeOutputs.success && computeOutputs.data.length > 0) {
|
|
223
|
+
const lbUrl = computeOutputs.data.find((o) => o.OutputKey?.includes("LoadBalancerUrl"));
|
|
224
|
+
if (lbUrl?.OutputValue) {
|
|
225
|
+
return lbUrl.OutputValue.toLowerCase();
|
|
226
|
+
}
|
|
227
|
+
const lbDns = computeOutputs.data.find((o) => o.OutputKey?.includes("LoadBalancerDnsName"));
|
|
228
|
+
if (lbDns?.OutputValue) {
|
|
229
|
+
return `http://${lbDns.OutputValue.toLowerCase()}`;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
const cdnStackName = getApplicationStackName(appName, APPLICATION_STACKS.CDN);
|
|
233
|
+
const cdnOutputs = await cloudFormationService.getStackOutputs(cdnStackName);
|
|
234
|
+
if (cdnOutputs.success && cdnOutputs.data.length > 0) {
|
|
235
|
+
const dist = cdnOutputs.data.find((o) => o.OutputKey?.includes("DistributionDomainName"));
|
|
236
|
+
if (dist?.OutputValue) {
|
|
237
|
+
return `https://${dist.OutputValue.toLowerCase()}`;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
return undefined;
|
|
241
|
+
}
|
|
242
|
+
catch (error) {
|
|
243
|
+
logger.warn("ApplicationStackService", "URL resolution failed", {
|
|
244
|
+
error: error instanceof Error ? error.message : String(error)
|
|
245
|
+
});
|
|
246
|
+
return undefined;
|
|
247
|
+
}
|
|
248
|
+
}
|