@fjall/deploy-core 0.94.1 → 0.95.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/dist/.minified +1 -1
- package/dist/src/aws/organisations/accounts.js +1 -99
- package/dist/src/aws/organisations/backup.js +1 -30
- package/dist/src/aws/organisations/costAllocation.js +1 -28
- package/dist/src/aws/organisations/delegatedAdmin.js +3 -43
- package/dist/src/aws/organisations/identityCentre.js +1 -23
- package/dist/src/aws/organisations/ipam.js +1 -20
- package/dist/src/aws/organisations/organisation.js +1 -103
- package/dist/src/aws/organisations/organisationalUnits.js +1 -239
- package/dist/src/aws/organisations/policies.js +1 -37
- package/dist/src/aws/organisations/ram.js +1 -19
- package/dist/src/aws/organisations/serviceAccess.js +1 -44
- package/dist/src/aws/organisations/trustedAccess.js +1 -19
- package/dist/src/aws/utils/regions.js +1 -1
- package/dist/src/index.js +1 -65
- package/dist/src/orchestration/__tests__/cascadeTestHelpers.js +1 -78
- package/dist/src/orchestration/activeDeploymentGuard.js +5 -39
- package/dist/src/orchestration/applicationDeploy.js +1 -149
- package/dist/src/orchestration/applicationDeployHelpers.js +4 -223
- package/dist/src/orchestration/applicationDestroy.js +1 -131
- package/dist/src/orchestration/builders/dockerBuilder.js +1 -98
- package/dist/src/orchestration/builders/openNextBuilder.js +1 -144
- package/dist/src/orchestration/cascadeHelpers.js +1 -160
- package/dist/src/orchestration/contextHelpers.js +1 -107
- package/dist/src/orchestration/deploy.js +1 -42
- package/dist/src/orchestration/destroy.js +1 -67
- package/dist/src/orchestration/detectionPipeline.js +1 -84
- package/dist/src/orchestration/dockerBuildHelper.js +1 -49
- package/dist/src/orchestration/dockerInterface.js +0 -1
- package/dist/src/orchestration/domainInterface.js +0 -1
- package/dist/src/orchestration/openNextBuild.js +3 -243
- package/dist/src/orchestration/organisationDeploy.js +3 -284
- package/dist/src/orchestration/organisationDestroy.js +3 -189
- package/dist/src/orchestration/organisationSetup.js +1 -247
- package/dist/src/orchestration/resolveOperation.js +1 -123
- package/dist/src/orchestration/welcomeImageHelper.js +1 -64
- package/dist/src/services/application/ApplicationStackService.js +1 -218
- package/dist/src/services/application/applicationStackHelpers.js +4 -248
- package/dist/src/services/infrastructure/CdkCommandRunner.js +2 -244
- package/dist/src/services/infrastructure/CdkOutputAnalyser.js +1 -125
- package/dist/src/services/infrastructure/CdkProcessManager.js +3 -278
- package/dist/src/services/infrastructure/CdkService.js +3 -213
- package/dist/src/services/infrastructure/CloudFormationService.js +1 -248
- package/dist/src/services/infrastructure/ICdkProcessManager.js +0 -1
- package/dist/src/services/supporting/CdkContextBuilder.js +1 -44
- package/dist/src/services/supporting/TemplateHashService.js +1 -152
- package/dist/src/steps/stepRegistry.js +1 -505
- package/dist/src/types/apiClient.js +0 -1
- package/dist/src/types/detection.js +0 -1
- package/dist/src/types/frameworkBuilder.js +0 -8
- package/dist/src/types/params.js +0 -1
- package/dist/src/types/patternDetection.js +1 -88
- package/dist/src/types/stepDefinitions.js +1 -98
- package/package.json +4 -4
|
@@ -1,160 +1 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { logger } from "@fjall/util/logger";
|
|
3
|
-
import { maskSensitiveOutput } from "@fjall/util";
|
|
4
|
-
import { ORGANISATION_TYPES, getOrganisationStackName } from "../types/operations.js";
|
|
5
|
-
import { CdkContextBuilder } from "../services/supporting/CdkContextBuilder.js";
|
|
6
|
-
import { stubCallerIdentity } from "../types/deployment/index.js";
|
|
7
|
-
import { CloudFormationService } from "../services/infrastructure/CloudFormationService.js";
|
|
8
|
-
import { buildParamsContext, collectStackOutputs, assumeCascadeRole, forwardOutput } from "./contextHelpers.js";
|
|
9
|
-
import { STRUCTURAL_ENVIRONMENTS } from "@fjall/util";
|
|
10
|
-
/**
|
|
11
|
-
* Partition provider accounts into platform and member accounts.
|
|
12
|
-
* Used by both deploy and destroy orchestration.
|
|
13
|
-
*/
|
|
14
|
-
export function partitionAccounts(providerAccounts) {
|
|
15
|
-
const platformAccount = providerAccounts.find((acc) => acc.environment === STRUCTURAL_ENVIRONMENTS.PLATFORM);
|
|
16
|
-
const memberAccounts = providerAccounts.filter((acc) => acc.environment !== STRUCTURAL_ENVIRONMENTS.ROOT &&
|
|
17
|
-
acc.environment !== STRUCTURAL_ENVIRONMENTS.PLATFORM);
|
|
18
|
-
return { platformAccount, memberAccounts };
|
|
19
|
-
}
|
|
20
|
-
// Re-export for backwards compatibility (canonical definition in contextHelpers)
|
|
21
|
-
export { buildCascadeRoleArn } from "./contextHelpers.js";
|
|
22
|
-
export async function deployCascadeAccount(params, services, operation, account, deployType, callbacks, ipamPoolId) {
|
|
23
|
-
const operationKey = `${account.name}-${account.id}`;
|
|
24
|
-
const region = services.awsProvider.getRegion();
|
|
25
|
-
callbacks.onCascadeAccountStart?.(operationKey, account.id, region, deployType);
|
|
26
|
-
// Assume role in target account
|
|
27
|
-
const roleResult = await assumeCascadeRole(services.awsProvider, account.id, region, `fjall-cascade-${account.name}`);
|
|
28
|
-
if (!roleResult.success) {
|
|
29
|
-
callbacks.onCascadeAccountComplete?.(operationKey, false, maskSensitiveOutput(roleResult.error.message), region);
|
|
30
|
-
return failure(new Error(`Failed to assume role for ${account.name}: ${maskSensitiveOutput(roleResult.error.message)}`));
|
|
31
|
-
}
|
|
32
|
-
const { provider: accountProvider, credentials: cascadeCredentials } = roleResult.data;
|
|
33
|
-
// Build context for this account (includes IPAM pool ID if available)
|
|
34
|
-
const accountContext = CdkContextBuilder.buildDeploymentContext({
|
|
35
|
-
deployType,
|
|
36
|
-
target: operation.target,
|
|
37
|
-
path: operation.path,
|
|
38
|
-
region,
|
|
39
|
-
accountName: account.name,
|
|
40
|
-
callerIdentity: stubCallerIdentity(account.id),
|
|
41
|
-
ipamPoolId,
|
|
42
|
-
...buildParamsContext({
|
|
43
|
-
orgConfig: params.orgConfig,
|
|
44
|
-
identity: params.identity,
|
|
45
|
-
skipOidc: params.options?.skipOidc
|
|
46
|
-
})
|
|
47
|
-
}, { verbose: params.options?.verbose }, params.orgConfig);
|
|
48
|
-
// Bootstrap the target account
|
|
49
|
-
callbacks.onCascadeAccountPhaseChange?.(operationKey, "bootstrap", region);
|
|
50
|
-
const bootstrapResult = await services.cdkService.runCdkBootstrap(accountContext, forwardOutput(callbacks), cascadeCredentials);
|
|
51
|
-
if (!bootstrapResult.success) {
|
|
52
|
-
callbacks.onCascadeAccountComplete?.(operationKey, false, maskSensitiveOutput(`Bootstrap failed: ${bootstrapResult.error}`), region);
|
|
53
|
-
return failure(new Error(`Bootstrap failed for ${account.name}: ${maskSensitiveOutput(bootstrapResult.error)}`));
|
|
54
|
-
}
|
|
55
|
-
// Deploy the account stack
|
|
56
|
-
callbacks.onCascadeAccountPhaseChange?.(operationKey, "deploy", region);
|
|
57
|
-
const stackName = getOrganisationStackName(deployType === "platform"
|
|
58
|
-
? ORGANISATION_TYPES.PLATFORM
|
|
59
|
-
: ORGANISATION_TYPES.ACCOUNT);
|
|
60
|
-
const deployResult = await services.cdkService.runCdkDeploy(accountContext, stackName, forwardOutput(callbacks), (event) => callbacks.onCascadeAccountResourceProgress?.(operationKey, event, region), accountProvider, cascadeCredentials);
|
|
61
|
-
if (!deployResult.success) {
|
|
62
|
-
callbacks.onCascadeAccountComplete?.(operationKey, false, maskSensitiveOutput(deployResult.error), region);
|
|
63
|
-
return failure(new Error(maskSensitiveOutput(deployResult.error)));
|
|
64
|
-
}
|
|
65
|
-
// Capture stack outputs (OIDC role ARN, etc.) from the target account
|
|
66
|
-
const accountCfn = new CloudFormationService(accountProvider);
|
|
67
|
-
const outputsResult = await accountCfn.getStackOutputs(stackName);
|
|
68
|
-
if (!outputsResult.success) {
|
|
69
|
-
logger.debug("cascadeHelpers", "Failed to read cascade account stack outputs (non-critical)", { stackName, account: account.name });
|
|
70
|
-
}
|
|
71
|
-
const outputs = collectStackOutputs(outputsResult);
|
|
72
|
-
callbacks.onCascadeAccountComplete?.(operationKey, true, undefined, region, outputs);
|
|
73
|
-
return success({ outputs });
|
|
74
|
-
}
|
|
75
|
-
/**
|
|
76
|
-
* Read Platform stack outputs to extract IPAM pool IDs for member accounts.
|
|
77
|
-
* Output keys follow the pattern `IpamPoolId{12-digit-accountId}{regionSuffix}`.
|
|
78
|
-
* Returns a map keyed by `{accountId}-{regionSuffix}` → pool ID.
|
|
79
|
-
* Non-fatal: returns empty map on any error.
|
|
80
|
-
*/
|
|
81
|
-
export async function readPlatformIpamPoolIds(services, platformAccount, callbacks) {
|
|
82
|
-
const poolIds = new Map();
|
|
83
|
-
// Assume role in platform account to read its stack outputs
|
|
84
|
-
const region = services.awsProvider.getRegion();
|
|
85
|
-
const roleResult = await assumeCascadeRole(services.awsProvider, platformAccount.id, region, `fjall-ipam-read-${platformAccount.name}`);
|
|
86
|
-
if (!roleResult.success) {
|
|
87
|
-
logger.debug("organisationDeploy", `Cannot read Platform outputs: ${roleResult.error.message}`);
|
|
88
|
-
return poolIds;
|
|
89
|
-
}
|
|
90
|
-
const platformCfn = new CloudFormationService(roleResult.data.provider);
|
|
91
|
-
const platformStackName = getOrganisationStackName(ORGANISATION_TYPES.PLATFORM);
|
|
92
|
-
const outputsResult = await platformCfn.getStackOutputs(platformStackName);
|
|
93
|
-
if (!outputsResult.success) {
|
|
94
|
-
logger.debug("organisationDeploy", `Failed to read Platform stack outputs: ${outputsResult.error.message}`);
|
|
95
|
-
return poolIds;
|
|
96
|
-
}
|
|
97
|
-
const ipamPattern = /^IpamPoolId(\d{12})(\w+)$/;
|
|
98
|
-
for (const output of outputsResult.data) {
|
|
99
|
-
const match = output.OutputKey?.match(ipamPattern);
|
|
100
|
-
if (match && output.OutputValue) {
|
|
101
|
-
const key = `${match[1]}-${match[2]}`;
|
|
102
|
-
poolIds.set(key, output.OutputValue);
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
if (poolIds.size > 0) {
|
|
106
|
-
callbacks.onLog?.(`Read ${poolIds.size} IPAM pool ID(s) from Platform stack`, "info");
|
|
107
|
-
}
|
|
108
|
-
return poolIds;
|
|
109
|
-
}
|
|
110
|
-
/**
|
|
111
|
-
* Deploy configured domains: apex domains sequentially, then delegated
|
|
112
|
-
* domains in parallel. Delegates to the caller-provided DomainDeployProvider.
|
|
113
|
-
*/
|
|
114
|
-
export async function deployDomains(provider, callbacks) {
|
|
115
|
-
const domains = provider.getDomains();
|
|
116
|
-
if (domains.length === 0) {
|
|
117
|
-
return { domainsDeployed: 0, errors: [] };
|
|
118
|
-
}
|
|
119
|
-
callbacks.onCascadePhaseStart?.("domains");
|
|
120
|
-
const apexDomains = domains.filter((d) => d.type === "apex");
|
|
121
|
-
const delegatedDomains = domains.filter((d) => d.type === "delegated");
|
|
122
|
-
let domainsDeployed = 0;
|
|
123
|
-
const errors = [];
|
|
124
|
-
// Phase A: Apex domains (sequential — delegation roles must exist first)
|
|
125
|
-
for (const domain of apexDomains) {
|
|
126
|
-
const result = await provider.deployDomain(domain.name, callbacks);
|
|
127
|
-
if (result.success) {
|
|
128
|
-
domainsDeployed++;
|
|
129
|
-
}
|
|
130
|
-
else {
|
|
131
|
-
errors.push(`${domain.name}: ${result.error.message}`);
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
// Phase B: Delegated subdomains (parallel — independent of each other)
|
|
135
|
-
if (delegatedDomains.length > 0) {
|
|
136
|
-
const subdomainResults = await Promise.allSettled(delegatedDomains.map((domain) => provider.deployDomain(domain.name, callbacks)));
|
|
137
|
-
for (let i = 0; i < subdomainResults.length; i++) {
|
|
138
|
-
const settled = subdomainResults[i];
|
|
139
|
-
const domain = delegatedDomains[i];
|
|
140
|
-
if (!settled || !domain)
|
|
141
|
-
continue;
|
|
142
|
-
if (settled.status === "fulfilled") {
|
|
143
|
-
if (settled.value.success) {
|
|
144
|
-
domainsDeployed++;
|
|
145
|
-
}
|
|
146
|
-
else {
|
|
147
|
-
errors.push(`${domain.name}: ${settled.value.error.message}`);
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
else {
|
|
151
|
-
const message = settled.reason instanceof Error
|
|
152
|
-
? settled.reason.message
|
|
153
|
-
: String(settled.reason);
|
|
154
|
-
errors.push(`${domain.name}: ${message}`);
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
callbacks.onCascadePhaseComplete?.("domains");
|
|
159
|
-
return { domainsDeployed, errors };
|
|
160
|
-
}
|
|
1
|
+
import{success as v,failure as P}from"@fjall/generator";import{logger as R}from"@fjall/util/logger";import{maskSensitiveOutput as l}from"@fjall/util";import{ORGANISATION_TYPES as O,getOrganisationStackName as A}from"../types/operations.js";import{CdkContextBuilder as x}from"../services/supporting/CdkContextBuilder.js";import{stubCallerIdentity as N}from"../types/deployment/index.js";import{CloudFormationService as S}from"../services/infrastructure/CloudFormationService.js";import{buildParamsContext as T,collectStackOutputs as F,assumeCascadeRole as h,forwardOutput as D}from"./contextHelpers.js";import{STRUCTURAL_ENVIRONMENTS as $}from"@fjall/util";function _(r){const s=r.find(e=>e.environment===$.PLATFORM),i=r.filter(e=>e.environment!==$.ROOT&&e.environment!==$.PLATFORM);return{platformAccount:s,memberAccounts:i}}import{buildCascadeRoleArn as J}from"./contextHelpers.js";async function b(r,s,i,e,c,n,d){const o=`${e.name}-${e.id}`,t=s.awsProvider.getRegion();n.onCascadeAccountStart?.(o,e.id,t,c);const a=await h(s.awsProvider,e.id,t,`fjall-cascade-${e.name}`);if(!a.success)return n.onCascadeAccountComplete?.(o,!1,l(a.error.message),t),P(new Error(`Failed to assume role for ${e.name}: ${l(a.error.message)}`));const{provider:u,credentials:m}=a.data,p=x.buildDeploymentContext({deployType:c,target:i.target,path:i.path,region:t,accountName:e.name,callerIdentity:N(e.id),ipamPoolId:d,...T({orgConfig:r.orgConfig,identity:r.identity,skipOidc:r.options?.skipOidc})},{verbose:r.options?.verbose},r.orgConfig);n.onCascadeAccountPhaseChange?.(o,"bootstrap",t);const f=await s.cdkService.runCdkBootstrap(p,D(n),m);if(!f.success)return n.onCascadeAccountComplete?.(o,!1,l(`Bootstrap failed: ${f.error}`),t),P(new Error(`Bootstrap failed for ${e.name}: ${l(f.error)}`));n.onCascadeAccountPhaseChange?.(o,"deploy",t);const g=A(c==="platform"?O.PLATFORM:O.ACCOUNT),C=await s.cdkService.runCdkDeploy(p,g,D(n),I=>n.onCascadeAccountResourceProgress?.(o,I,t),u,m);if(!C.success)return n.onCascadeAccountComplete?.(o,!1,l(C.error),t),P(new Error(l(C.error)));const w=await new S(u).getStackOutputs(g);w.success||R.debug("cascadeHelpers","Failed to read cascade account stack outputs (non-critical)",{stackName:g,account:e.name});const y=F(w);return n.onCascadeAccountComplete?.(o,!0,void 0,t,y),v({outputs:y})}async function G(r,s,i){const e=new Map,c=r.awsProvider.getRegion(),n=await h(r.awsProvider,s.id,c,`fjall-ipam-read-${s.name}`);if(!n.success)return R.debug("organisationDeploy",`Cannot read Platform outputs: ${n.error.message}`),e;const d=new S(n.data.provider),o=A(O.PLATFORM),t=await d.getStackOutputs(o);if(!t.success)return R.debug("organisationDeploy",`Failed to read Platform stack outputs: ${t.error.message}`),e;const a=/^IpamPoolId(\d{12})(\w+)$/;for(const u of t.data){const m=u.OutputKey?.match(a);if(m&&u.OutputValue){const p=`${m[1]}-${m[2]}`;e.set(p,u.OutputValue)}}return e.size>0&&i.onLog?.(`Read ${e.size} IPAM pool ID(s) from Platform stack`,"info"),e}async function H(r,s){const i=r.getDomains();if(i.length===0)return{domainsDeployed:0,errors:[]};s.onCascadePhaseStart?.("domains");const e=i.filter(o=>o.type==="apex"),c=i.filter(o=>o.type==="delegated");let n=0;const d=[];for(const o of e){const t=await r.deployDomain(o.name,s);t.success?n++:d.push(`${o.name}: ${t.error.message}`)}if(c.length>0){const o=await Promise.allSettled(c.map(t=>r.deployDomain(t.name,s)));for(let t=0;t<o.length;t++){const a=o[t],u=c[t];if(!(!a||!u))if(a.status==="fulfilled")a.value.success?n++:d.push(`${u.name}: ${a.value.error.message}`);else{const m=a.reason instanceof Error?a.reason.message:String(a.reason);d.push(`${u.name}: ${m}`)}}}return s.onCascadePhaseComplete?.("domains"),{domainsDeployed:n,errors:d}}export{J as buildCascadeRoleArn,b as deployCascadeAccount,H as deployDomains,_ as partitionAccounts,G as readPlatformIpamPoolIds};
|
|
@@ -1,107 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { getErrorMessage, maskSensitiveOutput } from "@fjall/util";
|
|
3
|
-
import { SimpleAwsProvider } from "../aws/SimpleAwsProvider.js";
|
|
4
|
-
/**
|
|
5
|
-
* Build the orgConfig/identity context fields shared by all deployment paths.
|
|
6
|
-
* CdkContextBuilder expects orgConfig as a JSON string and fjallOrgId as a string.
|
|
7
|
-
*/
|
|
8
|
-
export function buildParamsContext(params) {
|
|
9
|
-
return {
|
|
10
|
-
...(params.orgConfig !== undefined
|
|
11
|
-
? { orgConfig: JSON.stringify(params.orgConfig) }
|
|
12
|
-
: {}),
|
|
13
|
-
...(params.identity !== undefined
|
|
14
|
-
? { fjallOrgId: params.identity.fjallOrgId }
|
|
15
|
-
: {}),
|
|
16
|
-
...(params.skipOidc ? { fjallOidcConfigured: true } : {})
|
|
17
|
-
};
|
|
18
|
-
}
|
|
19
|
-
/** Forward onOutput callback — reduces lambda repetition across orchestration files. */
|
|
20
|
-
export function forwardOutput(callbacks) {
|
|
21
|
-
return (chunk) => callbacks.onOutput?.(chunk);
|
|
22
|
-
}
|
|
23
|
-
/** Forward onResourceProgress callback — reduces lambda repetition across orchestration files. */
|
|
24
|
-
export function forwardResourceProgress(callbacks) {
|
|
25
|
-
return (event) => callbacks.onResourceProgress?.(event);
|
|
26
|
-
}
|
|
27
|
-
/** Convert CloudFormation getStackOutputs result to Record<string, string>. */
|
|
28
|
-
export function collectStackOutputs(result) {
|
|
29
|
-
if (!result.success || result.data.length === 0)
|
|
30
|
-
return undefined;
|
|
31
|
-
const record = {};
|
|
32
|
-
for (const output of result.data) {
|
|
33
|
-
if (output.OutputKey && output.OutputValue !== undefined) {
|
|
34
|
-
record[output.OutputKey] = output.OutputValue;
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
return Object.keys(record).length > 0 ? record : undefined;
|
|
38
|
-
}
|
|
39
|
-
/** Role name created by the Account CDK stack for cross-account cascade operations. */
|
|
40
|
-
const CASCADE_DEPLOYMENT_ROLE = "fjall-deployment-role";
|
|
41
|
-
/** Build the IAM role ARN used for cross-account cascade operations. */
|
|
42
|
-
export function buildCascadeRoleArn(accountId) {
|
|
43
|
-
return `arn:aws:iam::${accountId}:role/${CASCADE_DEPLOYMENT_ROLE}`;
|
|
44
|
-
}
|
|
45
|
-
/**
|
|
46
|
-
* Assume a cross-account cascade role and create a scoped provider.
|
|
47
|
-
* Callers handle failure (callbacks, logging, return type) — this helper
|
|
48
|
-
* only owns the assume + provider construction.
|
|
49
|
-
*/
|
|
50
|
-
export async function assumeCascadeRole(awsProvider, accountId, region, sessionName) {
|
|
51
|
-
if (!awsProvider.assumeRole) {
|
|
52
|
-
return failure(new Error("AwsProvider does not support assumeRole"));
|
|
53
|
-
}
|
|
54
|
-
const roleArn = buildCascadeRoleArn(accountId);
|
|
55
|
-
let assumed;
|
|
56
|
-
try {
|
|
57
|
-
assumed = await awsProvider.assumeRole(roleArn, sessionName);
|
|
58
|
-
}
|
|
59
|
-
catch (err) {
|
|
60
|
-
return failure(new Error(getErrorMessage(err)));
|
|
61
|
-
}
|
|
62
|
-
const provider = new SimpleAwsProvider({
|
|
63
|
-
accessKeyId: assumed.accessKeyId,
|
|
64
|
-
secretAccessKey: assumed.secretAccessKey,
|
|
65
|
-
sessionToken: assumed.sessionToken,
|
|
66
|
-
region,
|
|
67
|
-
accountId
|
|
68
|
-
});
|
|
69
|
-
return success({
|
|
70
|
-
provider,
|
|
71
|
-
credentials: {
|
|
72
|
-
accessKeyId: assumed.accessKeyId,
|
|
73
|
-
secretAccessKey: assumed.secretAccessKey,
|
|
74
|
-
sessionToken: assumed.sessionToken
|
|
75
|
-
}
|
|
76
|
-
});
|
|
77
|
-
}
|
|
78
|
-
/**
|
|
79
|
-
* Run CDK synth and return failure with masked error if it fails.
|
|
80
|
-
* Calls `onError` on the callbacks so callers only need to handle
|
|
81
|
-
* step-specific reporting (e.g. onStepComplete) before returning.
|
|
82
|
-
*/
|
|
83
|
-
export async function synthOrFail(services, context, callbacks, errorPrefix) {
|
|
84
|
-
const synthResult = await services.cdkService.runCdkSynth(context, (chunk) => callbacks.onCdkOutput?.(chunk, "synth"));
|
|
85
|
-
if (!synthResult.success) {
|
|
86
|
-
const error = new Error(maskSensitiveOutput(`${errorPrefix}: ${synthResult.error}`));
|
|
87
|
-
callbacks.onError?.(error);
|
|
88
|
-
return failure(error);
|
|
89
|
-
}
|
|
90
|
-
return success(undefined);
|
|
91
|
-
}
|
|
92
|
-
/**
|
|
93
|
-
* Run CDK bootstrap and return failure with masked error if it fails.
|
|
94
|
-
* Emits onCDKBootstrap status callbacks and calls onError on failure.
|
|
95
|
-
*/
|
|
96
|
-
export async function bootstrapOrFail(services, context, callbacks) {
|
|
97
|
-
callbacks.onCDKBootstrap?.("bootstrapping");
|
|
98
|
-
const bootstrapResult = await services.cdkService.runCdkBootstrap(context, forwardOutput(callbacks));
|
|
99
|
-
if (!bootstrapResult.success) {
|
|
100
|
-
callbacks.onCDKBootstrap?.("failed");
|
|
101
|
-
const error = new Error(maskSensitiveOutput(`Bootstrap failed: ${bootstrapResult.error}`));
|
|
102
|
-
callbacks.onError?.(error);
|
|
103
|
-
return failure(error);
|
|
104
|
-
}
|
|
105
|
-
callbacks.onCDKBootstrap?.("complete");
|
|
106
|
-
return success(undefined);
|
|
107
|
-
}
|
|
1
|
+
import{success as i,failure as u}from"@fjall/generator";import{getErrorMessage as a,maskSensitiveOutput as c}from"@fjall/util";import{SimpleAwsProvider as p}from"../aws/SimpleAwsProvider.js";function K(e){return{...e.orgConfig!==void 0?{orgConfig:JSON.stringify(e.orgConfig)}:{},...e.identity!==void 0?{fjallOrgId:e.identity.fjallOrgId}:{},...e.skipOidc?{fjallOidcConfigured:!0}:{}}}function l(e){return t=>e.onOutput?.(t)}function w(e){return t=>e.onResourceProgress?.(t)}function E(e){if(!e.success||e.data.length===0)return;const t={};for(const r of e.data)r.OutputKey&&r.OutputValue!==void 0&&(t[r.OutputKey]=r.OutputValue);return Object.keys(t).length>0?t:void 0}const y="fjall-deployment-role";function O(e){return`arn:aws:iam::${e}:role/${y}`}async function A(e,t,r,s){if(!e.assumeRole)return u(new Error("AwsProvider does not support assumeRole"));const n=O(t);let o;try{o=await e.assumeRole(n,s)}catch(f){return u(new Error(a(f)))}const d=new p({accessKeyId:o.accessKeyId,secretAccessKey:o.secretAccessKey,sessionToken:o.sessionToken,region:r,accountId:t});return i({provider:d,credentials:{accessKeyId:o.accessKeyId,secretAccessKey:o.secretAccessKey,sessionToken:o.sessionToken}})}async function R(e,t,r,s){const n=await e.cdkService.runCdkSynth(t,o=>r.onCdkOutput?.(o,"synth"));if(!n.success){const o=new Error(c(`${s}: ${n.error}`));return r.onError?.(o),u(o)}return i(void 0)}async function x(e,t,r){r.onCDKBootstrap?.("bootstrapping");const s=await e.cdkService.runCdkBootstrap(t,l(r));if(!s.success){r.onCDKBootstrap?.("failed");const n=new Error(c(`Bootstrap failed: ${s.error}`));return r.onError?.(n),u(n)}return r.onCDKBootstrap?.("complete"),i(void 0)}export{A as assumeCascadeRole,x as bootstrapOrFail,O as buildCascadeRoleArn,K as buildParamsContext,E as collectStackOutputs,l as forwardOutput,w as forwardResourceProgress,R as synthOrFail};
|
|
@@ -1,42 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { isApplicationOperation } from "../types/operations.js";
|
|
3
|
-
import { createDeployServices } from "./serviceFactory.js";
|
|
4
|
-
import { resolveOperation } from "./resolveOperation.js";
|
|
5
|
-
import { deployApplication } from "./applicationDeploy.js";
|
|
6
|
-
import { deployOrganisation } from "./organisationDeploy.js";
|
|
7
|
-
/**
|
|
8
|
-
* Deploy infrastructure for a target.
|
|
9
|
-
*
|
|
10
|
-
* Primary entry point for both CLI and webapp worker. Determines whether
|
|
11
|
-
* the target is an application or organisation deployment and routes
|
|
12
|
-
* to the appropriate orchestrator.
|
|
13
|
-
*/
|
|
14
|
-
export async function deploy(params) {
|
|
15
|
-
const startTime = Date.now();
|
|
16
|
-
const services = createDeployServices(params);
|
|
17
|
-
const opResult = await resolveOperation(params.target, params.workingDirectory);
|
|
18
|
-
if (!opResult.success) {
|
|
19
|
-
params.callbacks.onError?.(opResult.error);
|
|
20
|
-
return opResult;
|
|
21
|
-
}
|
|
22
|
-
const operation = opResult.data;
|
|
23
|
-
if (isApplicationOperation(operation)) {
|
|
24
|
-
const result = await deployApplication(params, services, operation);
|
|
25
|
-
// Attach duration if not already set
|
|
26
|
-
if (result.success && result.data.durationMs === undefined) {
|
|
27
|
-
return success({
|
|
28
|
-
...result.data,
|
|
29
|
-
durationMs: Date.now() - startTime
|
|
30
|
-
});
|
|
31
|
-
}
|
|
32
|
-
return result;
|
|
33
|
-
}
|
|
34
|
-
const result = await deployOrganisation(params, services, operation);
|
|
35
|
-
if (result.success && result.data.durationMs === undefined) {
|
|
36
|
-
return success({
|
|
37
|
-
...result.data,
|
|
38
|
-
durationMs: Date.now() - startTime
|
|
39
|
-
});
|
|
40
|
-
}
|
|
41
|
-
return result;
|
|
42
|
-
}
|
|
1
|
+
import{success as a}from"@fjall/generator";import{isApplicationOperation as c}from"../types/operations.js";import{createDeployServices as u}from"./serviceFactory.js";import{resolveOperation as d}from"./resolveOperation.js";import{deployApplication as p}from"./applicationDeploy.js";import{deployOrganisation as f}from"./organisationDeploy.js";async function g(t){const n=Date.now(),s=u(t),o=await d(t.target,t.workingDirectory);if(!o.success)return t.callbacks.onError?.(o.error),o;const i=o.data;if(c(i)){const e=await p(t,s,i);return e.success&&e.data.durationMs===void 0?a({...e.data,durationMs:Date.now()-n}):e}const r=await f(t,s,i);return r.success&&r.data.durationMs===void 0?a({...r.data,durationMs:Date.now()-n}):r}export{g as deploy};
|
|
@@ -1,67 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { isApplicationOperation, isOrganisationOperation, getApplicationStackName, getOrganisationStackName, APPLICATION_STACKS } from "../types/operations.js";
|
|
3
|
-
import { createDeployServices } from "./serviceFactory.js";
|
|
4
|
-
import { resolveOperation } from "./resolveOperation.js";
|
|
5
|
-
import { destroyApplication } from "./applicationDestroy.js";
|
|
6
|
-
import { destroyOrganisation } from "./organisationDestroy.js";
|
|
7
|
-
import { checkActiveDeployments } from "./activeDeploymentGuard.js";
|
|
8
|
-
/**
|
|
9
|
-
* Destroy infrastructure for a target.
|
|
10
|
-
*
|
|
11
|
-
* Primary entry point for both CLI and webapp worker. Determines whether
|
|
12
|
-
* the target is an application or organisation and routes to the
|
|
13
|
-
* appropriate orchestrator.
|
|
14
|
-
*
|
|
15
|
-
* Mirrors the deploy() entry point -- same resolution, same service
|
|
16
|
-
* factory, same callback contract.
|
|
17
|
-
*/
|
|
18
|
-
export async function destroy(params) {
|
|
19
|
-
const startTime = Date.now();
|
|
20
|
-
const services = createDeployServices({
|
|
21
|
-
target: params.target,
|
|
22
|
-
workingDirectory: params.workingDirectory,
|
|
23
|
-
awsCredentials: params.awsCredentials,
|
|
24
|
-
callbacks: params.callbacks,
|
|
25
|
-
options: {
|
|
26
|
-
verbose: params.options?.verbose,
|
|
27
|
-
force: params.options?.force,
|
|
28
|
-
cascade: params.options?.cascade,
|
|
29
|
-
skipConfirmation: params.options?.skipConfirmation
|
|
30
|
-
},
|
|
31
|
-
orgConfig: params.orgConfig,
|
|
32
|
-
identity: params.identity
|
|
33
|
-
});
|
|
34
|
-
const opResult = await resolveOperation(params.target, params.workingDirectory);
|
|
35
|
-
if (!opResult.success) {
|
|
36
|
-
params.callbacks.onError?.(opResult.error);
|
|
37
|
-
return opResult;
|
|
38
|
-
}
|
|
39
|
-
const operation = opResult.data;
|
|
40
|
-
// Pre-flight: reject if any target stacks are mid-operation (unless --force)
|
|
41
|
-
if (!params.options?.force) {
|
|
42
|
-
const stackNames = isApplicationOperation(operation)
|
|
43
|
-
? Object.values(APPLICATION_STACKS).map((stack) => getApplicationStackName(operation.appName, stack))
|
|
44
|
-
: [getOrganisationStackName(operation.type)];
|
|
45
|
-
const guardResult = await checkActiveDeployments(stackNames, services.cfnService);
|
|
46
|
-
if (!guardResult.success) {
|
|
47
|
-
params.callbacks.onError?.(guardResult.error);
|
|
48
|
-
return failure(guardResult.error);
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
if (isApplicationOperation(operation)) {
|
|
52
|
-
const result = await destroyApplication(params, services, operation);
|
|
53
|
-
if (result.success && result.data.durationMs === undefined) {
|
|
54
|
-
return success({
|
|
55
|
-
...result.data,
|
|
56
|
-
durationMs: Date.now() - startTime
|
|
57
|
-
});
|
|
58
|
-
}
|
|
59
|
-
return result;
|
|
60
|
-
}
|
|
61
|
-
if (isOrganisationOperation(operation)) {
|
|
62
|
-
return destroyOrganisation(params, services, operation);
|
|
63
|
-
}
|
|
64
|
-
const error = new Error(`Unsupported destroy target: ${params.target}`);
|
|
65
|
-
params.callbacks.onError?.(error);
|
|
66
|
-
return failure(error);
|
|
67
|
-
}
|
|
1
|
+
import{success as u,failure as s}from"@fjall/generator";import{isApplicationOperation as a,isOrganisationOperation as d,getApplicationStackName as g,getOrganisationStackName as p,APPLICATION_STACKS as k}from"../types/operations.js";import{createDeployServices as y}from"./serviceFactory.js";import{resolveOperation as w}from"./resolveOperation.js";import{destroyApplication as b}from"./applicationDestroy.js";import{destroyOrganisation as v}from"./organisationDestroy.js";import{checkActiveDeployments as C}from"./activeDeploymentGuard.js";async function T(o){const f=Date.now(),i=y({target:o.target,workingDirectory:o.workingDirectory,awsCredentials:o.awsCredentials,callbacks:o.callbacks,options:{verbose:o.options?.verbose,force:o.options?.force,cascade:o.options?.cascade,skipConfirmation:o.options?.skipConfirmation},orgConfig:o.orgConfig,identity:o.identity}),r=await w(o.target,o.workingDirectory);if(!r.success)return o.callbacks.onError?.(r.error),r;const t=r.data;if(!o.options?.force){const e=a(t)?Object.values(k).map(l=>g(t.appName,l)):[p(t.type)],n=await C(e,i.cfnService);if(!n.success)return o.callbacks.onError?.(n.error),s(n.error)}if(a(t)){const e=await b(o,i,t);return e.success&&e.data.durationMs===void 0?u({...e.data,durationMs:Date.now()-f}):e}if(d(t))return v(o,i,t);const c=new Error(`Unsupported destroy target: ${o.target}`);return o.callbacks.onError?.(c),s(c)}export{T as destroy};
|
|
@@ -1,84 +1 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { success, failure } from "@fjall/generator";
|
|
3
|
-
import { logger } from "@fjall/util/logger";
|
|
4
|
-
import { maskSensitiveOutput } from "@fjall/util";
|
|
5
|
-
import { hasDockerfile } from "../util/dockerfileDetection.js";
|
|
6
|
-
import { deriveResourcesFromManifestStacks } from "../types/patternDetection.js";
|
|
7
|
-
import { emitProgress, PROGRESS_MESSAGES } from "../services/supporting/helpers.js";
|
|
8
|
-
import { parseRequiredSecretsFromManifest } from "./manifestSecretParser.js";
|
|
9
|
-
/**
|
|
10
|
-
* Pre-deployment analysis pipeline.
|
|
11
|
-
*
|
|
12
|
-
* Detects the application pattern, synthesises infrastructure, computes
|
|
13
|
-
* template hashes, and determines which stacks have changed.
|
|
14
|
-
*/
|
|
15
|
-
export async function runDetectionPipeline(operation, services, context, callbacks) {
|
|
16
|
-
// 1. Detect application pattern
|
|
17
|
-
callbacks.onDetectionPhaseChange?.("detect", "started");
|
|
18
|
-
const resolved = services.frameworkRegistry.resolve({
|
|
19
|
-
appPath: operation.path
|
|
20
|
-
});
|
|
21
|
-
const pattern = resolved?.detection.pattern ?? null;
|
|
22
|
-
const hasDatabase = resolved?.detection.hasDatabase ?? false;
|
|
23
|
-
callbacks.onLog?.(`Pattern detected: ${pattern ?? "standard"}`, "info");
|
|
24
|
-
callbacks.onDetectionPhaseChange?.("detect", "completed");
|
|
25
|
-
const cdkOutPath = join(operation.path, "cdk.out");
|
|
26
|
-
// 2. Synthesise infrastructure
|
|
27
|
-
callbacks.onDetectionPhaseChange?.("synth", "started");
|
|
28
|
-
emitProgress(callbacks, PROGRESS_MESSAGES.SYNTH);
|
|
29
|
-
const synthResult = await services.cdkService.runCdkSynth(context, (chunk) => callbacks.onCdkOutput?.(chunk, "synth"));
|
|
30
|
-
if (!synthResult.success) {
|
|
31
|
-
return failure(new Error(maskSensitiveOutput(`CDK synthesis failed: ${synthResult.error}`)));
|
|
32
|
-
}
|
|
33
|
-
callbacks.onDetectionPhaseChange?.("synth", "completed");
|
|
34
|
-
// 2b. Parse required secrets from manifest (pure data, no AWS calls)
|
|
35
|
-
const requiredSecrets = parseRequiredSecretsFromManifest(cdkOutPath);
|
|
36
|
-
if (requiredSecrets.length > 0) {
|
|
37
|
-
callbacks.onLog?.(`Found ${requiredSecrets.length} required secret path(s) in manifest`, "info");
|
|
38
|
-
}
|
|
39
|
-
// 3. Compute template hashes
|
|
40
|
-
callbacks.onDetectionPhaseChange?.("hash", "started");
|
|
41
|
-
emitProgress(callbacks, PROGRESS_MESSAGES.HASH);
|
|
42
|
-
const hashResult = await services.hashService.getTemplateHashes(cdkOutPath);
|
|
43
|
-
if (!hashResult.success) {
|
|
44
|
-
return failure(new Error(maskSensitiveOutput(`Template hash computation failed: ${hashResult.error.message}`)));
|
|
45
|
-
}
|
|
46
|
-
const currentHashes = hashResult.data;
|
|
47
|
-
// 4. Compare with stored state
|
|
48
|
-
const comparisonResult = await services.hashService.compareWithState(currentHashes, operation.path);
|
|
49
|
-
if (!comparisonResult.success) {
|
|
50
|
-
return failure(new Error(maskSensitiveOutput(`Hash comparison failed: ${comparisonResult.error.message}`)));
|
|
51
|
-
}
|
|
52
|
-
const comparison = comparisonResult.data;
|
|
53
|
-
callbacks.onDetectionPhaseChange?.("hash", "completed");
|
|
54
|
-
// 5. Stale hash detection: unchanged templates whose stacks don't exist in CFN
|
|
55
|
-
const stackChanges = new Map(comparison.stackChanges);
|
|
56
|
-
for (const [stackName, hasChanged] of comparison.stackChanges) {
|
|
57
|
-
if (!hasChanged) {
|
|
58
|
-
const exists = await services.cfnService.stackExists(stackName);
|
|
59
|
-
if (!exists) {
|
|
60
|
-
logger.debug("detectionPipeline", "Stale hash detected — stack missing in CFN", {
|
|
61
|
-
stackName
|
|
62
|
-
});
|
|
63
|
-
stackChanges.set(stackName, true);
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
// 6. Derive resource flags from manifest stacks
|
|
68
|
-
const stackNames = Array.from(currentHashes.keys());
|
|
69
|
-
const resources = deriveResourcesFromManifestStacks(stackNames);
|
|
70
|
-
// 7. Detect Dockerfile on disk (Compute stack alone is not sufficient)
|
|
71
|
-
const dockerfilePresent = resources.hasCompute && hasDockerfile(operation.path);
|
|
72
|
-
const hasDifferences = Array.from(stackChanges.values()).some(Boolean);
|
|
73
|
-
callbacks.onLog?.(`Detection complete: ${comparison.changedCount} changed, ${comparison.unchangedCount} unchanged`, "info");
|
|
74
|
-
return success({
|
|
75
|
-
pattern,
|
|
76
|
-
hasDatabase,
|
|
77
|
-
hasDifferences,
|
|
78
|
-
stackChanges,
|
|
79
|
-
currentHashes,
|
|
80
|
-
resources,
|
|
81
|
-
hasDockerfile: dockerfilePresent,
|
|
82
|
-
requiredSecrets
|
|
83
|
-
});
|
|
84
|
-
}
|
|
1
|
+
import{join as E}from"path";import{success as $,failure as m}from"@fjall/generator";import{logger as H}from"@fjall/util/logger";import{maskSensitiveOutput as d}from"@fjall/util";import{hasDockerfile as v}from"../util/dockerfileDetection.js";import{deriveResourcesFromManifestStacks as x}from"../types/patternDetection.js";import{emitProgress as C,PROGRESS_MESSAGES as D}from"../services/supporting/helpers.js";import{parseRequiredSecretsFromManifest as A}from"./manifestSecretParser.js";async function B(n,t,P,e){e.onDetectionPhaseChange?.("detect","started");const u=t.frameworkRegistry.resolve({appPath:n.path}),f=u?.detection.pattern??null,y=u?.detection.hasDatabase??!1;e.onLog?.(`Pattern detected: ${f??"standard"}`,"info"),e.onDetectionPhaseChange?.("detect","completed");const p=E(n.path,"cdk.out");e.onDetectionPhaseChange?.("synth","started"),C(e,D.SYNTH);const g=await t.cdkService.runCdkSynth(P,s=>e.onCdkOutput?.(s,"synth"));if(!g.success)return m(new Error(d(`CDK synthesis failed: ${g.error}`)));e.onDetectionPhaseChange?.("synth","completed");const r=A(p);r.length>0&&e.onLog?.(`Found ${r.length} required secret path(s) in manifest`,"info"),e.onDetectionPhaseChange?.("hash","started"),C(e,D.HASH);const a=await t.hashService.getTemplateHashes(p);if(!a.success)return m(new Error(d(`Template hash computation failed: ${a.error.message}`)));const i=a.data,h=await t.hashService.compareWithState(i,n.path);if(!h.success)return m(new Error(d(`Hash comparison failed: ${h.error.message}`)));const o=h.data;e.onDetectionPhaseChange?.("hash","completed");const c=new Map(o.stackChanges);for(const[s,l]of o.stackChanges)l||await t.cfnService.stackExists(s)||(H.debug("detectionPipeline","Stale hash detected \u2014 stack missing in CFN",{stackName:s}),c.set(s,!0));const w=Array.from(i.keys()),S=x(w),k=S.hasCompute&&v(n.path),R=Array.from(c.values()).some(Boolean);return e.onLog?.(`Detection complete: ${o.changedCount} changed, ${o.unchangedCount} unchanged`,"info"),$({pattern:f,hasDatabase:y,hasDifferences:R,stackChanges:c,currentHashes:i,resources:S,hasDockerfile:k,requiredSecrets:r})}export{B as runDetectionPipeline};
|
|
@@ -1,49 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { maskSensitiveOutput } from "@fjall/util";
|
|
3
|
-
import { STEP_IDS } from "../types/stepDefinitions.js";
|
|
4
|
-
const DOCKER_BUILD_STEP_NAME = "Building and pushing Docker image";
|
|
5
|
-
/**
|
|
6
|
-
* Run Docker build and push before deploying the Compute stack.
|
|
7
|
-
* Initialises ECR if needed, then builds and pushes the image.
|
|
8
|
-
*/
|
|
9
|
-
export async function runDockerBuild(params, services, operation, callbacks) {
|
|
10
|
-
const dockerProvider = params.dockerProvider;
|
|
11
|
-
if (!dockerProvider)
|
|
12
|
-
return success(undefined);
|
|
13
|
-
const accountId = services.awsProvider.getAccountId();
|
|
14
|
-
if (!accountId) {
|
|
15
|
-
callbacks.onLog?.("Skipping Docker build — account ID not available", "warn");
|
|
16
|
-
return success(undefined);
|
|
17
|
-
}
|
|
18
|
-
const region = services.awsProvider.getRegion();
|
|
19
|
-
callbacks.onStepStart?.(STEP_IDS.DOCKER_OPERATIONS, DOCKER_BUILD_STEP_NAME);
|
|
20
|
-
// Initialise ECR repository (non-fatal — CDK creates it if needed)
|
|
21
|
-
callbacks.onLog?.("Initialising ECR repository…", "info");
|
|
22
|
-
const ecrResult = await dockerProvider.initialiseECR({
|
|
23
|
-
appName: operation.appName,
|
|
24
|
-
region,
|
|
25
|
-
accountId
|
|
26
|
-
});
|
|
27
|
-
if (!ecrResult.success) {
|
|
28
|
-
callbacks.onLog?.(`ECR initialisation failed: ${ecrResult.error.message}`, "warn");
|
|
29
|
-
}
|
|
30
|
-
// Build and push Docker image
|
|
31
|
-
callbacks.onLog?.("Building and pushing Docker image…", "info");
|
|
32
|
-
const buildResult = await dockerProvider.buildAndPush({
|
|
33
|
-
appName: operation.appName,
|
|
34
|
-
appPath: operation.path,
|
|
35
|
-
region,
|
|
36
|
-
accountId
|
|
37
|
-
}, (message, percentage, layerId, status) => {
|
|
38
|
-
callbacks.onDockerProgress?.(maskSensitiveOutput(message), percentage, layerId, status);
|
|
39
|
-
});
|
|
40
|
-
if (!buildResult.success) {
|
|
41
|
-
callbacks.onStepComplete?.(STEP_IDS.DOCKER_OPERATIONS, DOCKER_BUILD_STEP_NAME, "error");
|
|
42
|
-
const error = new Error(maskSensitiveOutput(buildResult.error.message));
|
|
43
|
-
callbacks.onError?.(error);
|
|
44
|
-
return failure(error);
|
|
45
|
-
}
|
|
46
|
-
callbacks.onStepComplete?.(STEP_IDS.DOCKER_OPERATIONS, DOCKER_BUILD_STEP_NAME, "completed");
|
|
47
|
-
callbacks.onLog?.(`Docker image pushed: ${buildResult.data.imageUri}`, "info");
|
|
48
|
-
return success(undefined);
|
|
49
|
-
}
|
|
1
|
+
import{success as u,failure as c}from"@fjall/generator";import{maskSensitiveOutput as m}from"@fjall/util";import{STEP_IDS as d}from"../types/stepDefinitions.js";const p="Building and pushing Docker image";async function C(f,s,r,e){const i=f.dockerProvider;if(!i)return u(void 0);const n=s.awsProvider.getAccountId();if(!n)return e.onLog?.("Skipping Docker build \u2014 account ID not available","warn"),u(void 0);const a=s.awsProvider.getRegion();e.onStepStart?.(d.DOCKER_OPERATIONS,p),e.onLog?.("Initialising ECR repository\u2026","info");const g=await i.initialiseECR({appName:r.appName,region:a,accountId:n});g.success||e.onLog?.(`ECR initialisation failed: ${g.error.message}`,"warn"),e.onLog?.("Building and pushing Docker image\u2026","info");const t=await i.buildAndPush({appName:r.appName,appPath:r.path,region:a,accountId:n},(o,E,D,R)=>{e.onDockerProgress?.(m(o),E,D,R)});if(!t.success){e.onStepComplete?.(d.DOCKER_OPERATIONS,p,"error");const o=new Error(m(t.error.message));return e.onError?.(o),c(o)}return e.onStepComplete?.(d.DOCKER_OPERATIONS,p,"completed"),e.onLog?.(`Docker image pushed: ${t.data.imageUri}`,"info"),u(void 0)}export{C as runDockerBuild};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|