@fjall/deploy-core 2.7.1 → 2.9.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/backup.d.ts +12 -0
- package/dist/src/aws/organisations/backup.js +1 -1
- package/dist/src/orchestration/cascadeHelpers.js +1 -1
- package/dist/src/orchestration/organisationDeploy.js +4 -4
- package/dist/src/services/infrastructure/CdkArgumentBuilder.js +1 -1
- package/dist/src/services/infrastructure/CdkServiceTypes.d.ts +1 -0
- package/dist/src/services/infrastructure/cdkServiceHelpers.js +1 -1
- package/dist/src/steps/stepRegistry.js +1 -1
- package/dist/src/types/callbackKeys.d.ts +7 -0
- package/dist/src/types/callbackKeys.js +1 -0
- package/dist/src/types/callbacks.d.ts +12 -0
- package/dist/src/types/deployment/DeploymentTypes.d.ts +1 -0
- package/package.json +4 -4
package/dist/.minified
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
120 files minified at 2026-06-02T05:28:03.549Z
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type BackupClient } from "@aws-sdk/client-backup";
|
|
2
2
|
import { type Result } from "@fjall/generator";
|
|
3
|
+
export declare const BACKUP_VAULT_NAME = "backupVault";
|
|
3
4
|
export interface BackupGlobalSettings {
|
|
4
5
|
enableCrossAccountBackup?: boolean;
|
|
5
6
|
enableDelegatedAdministrator?: boolean;
|
|
@@ -10,3 +11,14 @@ export interface BackupGlobalSettings {
|
|
|
10
11
|
* Idempotent — safe to call repeatedly with the same settings.
|
|
11
12
|
*/
|
|
12
13
|
export declare function updateBackupGlobalSettings(client: BackupClient, settings?: BackupGlobalSettings): Promise<Result<void>>;
|
|
14
|
+
/**
|
|
15
|
+
* Probe whether the disaster-recovery backup vault already exists in the target
|
|
16
|
+
* account/region, to decide adopt-vs-create. A retained, vault-locked DR vault
|
|
17
|
+
* survives stack teardown and collides on the next CREATE.
|
|
18
|
+
*
|
|
19
|
+
* Returns success(true) when present and success(false) ONLY on
|
|
20
|
+
* ResourceNotFoundException. Every other error (AccessDenied, throttling)
|
|
21
|
+
* propagates as failure — a mis-read "exists" would flip a vault-less region
|
|
22
|
+
* into a broken by-reference deploy.
|
|
23
|
+
*/
|
|
24
|
+
export declare function describeBackupVaultExists(client: BackupClient): Promise<Result<boolean>>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{UpdateGlobalSettingsCommand as
|
|
1
|
+
import{DescribeBackupVaultCommand as l,UpdateGlobalSettingsCommand as c}from"@aws-sdk/client-backup";import{success as r,failure as n}from"@fjall/generator";import{getErrorMessage as o,maskSensitiveOutput as p}from"@fjall/util";import{SDK_TIMEOUT_MS as i}from"./types.js";const s="backupVault",b={enableCrossAccountBackup:!0,enableDelegatedAdministrator:!0,enableMpa:!1};async function f(a,t){try{const e={...b,...t},u={isCrossAccountBackupEnabled:e.enableCrossAccountBackup.toString(),isDelegatedAdministratorEnabled:e.enableDelegatedAdministrator.toString(),isMpaEnabled:e.enableMpa.toString()};return await a.send(new c({GlobalSettings:u}),{abortSignal:AbortSignal.timeout(i)}),r(void 0)}catch(e){return n(new Error(`Failed to update backup global settings: ${o(e)}`))}}async function A(a){try{return await a.send(new l({BackupVaultName:s}),{abortSignal:AbortSignal.timeout(i)}),r(!0)}catch(t){return t instanceof Error&&t.name==="ResourceNotFoundException"?r(!1):n(new Error(`Failed to describe backup vault "${s}": ${p(o(t))}`))}}export{s as BACKUP_VAULT_NAME,A as describeBackupVaultExists,f as updateBackupGlobalSettings};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{join as
|
|
1
|
+
import{join as L}from"path";import{success as M,failure as C}from"@fjall/generator";import{logger as R}from"@fjall/util/logger";import{maskSensitiveOutput as p}from"@fjall/util";import{ORGANISATION_TYPES as h,getOrganisationStackName as B}from"../types/operations.js";import{CdkContextBuilder as G}from"../services/supporting/CdkContextBuilder.js";import{stubCallerIdentity as H}from"../types/deployment/index.js";import{CloudFormationService as U}from"../services/infrastructure/CloudFormationService.js";import{getCascadeStateFilePath as z}from"../types/FjallState.js";import{BackupClient as K}from"@aws-sdk/client-backup";import{describeBackupVaultExists as Y}from"../aws/organisations/backup.js";import{buildParamsContext as X,collectStackOutputs as b,assumeCascadeRole as j,forwardOutput as D}from"./contextHelpers.js";import{STRUCTURAL_ENVIRONMENTS as v}from"@fjall/util";import{DEFAULT_REGION as q}from"../aws/utils/regions.js";const de=4;function pe(s){const n=s.find(e=>e.environment===v.PLATFORM),i=s.filter(e=>e.environment!==v.ROOT&&e.environment!==v.PLATFORM);return{platformAccount:n,memberAccounts:i}}function me(s){const n=s?.primaryRegion??q,i=s?.secondaryRegions??[],e=[n,...i],a=s?.disasterRecoveryRegion;return a&&!e.includes(a)&&e.push(a),e}function fe(s,n){const i=[];for(const e of n)for(const a of s)i.push({account:a,region:e});return i}import{buildCascadeRoleArn as Ae}from"./contextHelpers.js";async function le(s,n,i,e,a,r,u){const t=u?.region??e.region??n.awsProvider.getRegion(),o=`${e.name} (${t})`,c=u?.orgConfig??s.orgConfig,m=u?.ipamPoolId;r.onCascadeAccountStart?.(o,e.id,t,a);const d=await j(n.awsProvider,e.id,t,`fjall-cascade-${e.name}`);if(!d.success)return r.onCascadeAccountComplete?.(o,!1,p(d.error.message),t),C(new Error(`Failed to assume role for ${e.name}: ${p(d.error.message)}`));const{provider:l,credentials:y}=d.data,w=L(s.workingDirectory,"fjall",a==="platform"?h.PLATFORM:h.ACCOUNT),V=t.replace(/-/g,""),x=L(w,`cdk.out.${e.id}.${V}`),A=G.buildDeploymentContext({deployType:a,target:i.target,path:w,assemblyDir:x,environment:e.environment,region:t,accountName:e.name,callerIdentity:H(e.id),ipamPoolId:m,...X({orgConfig:c,identity:s.identity,skipOidc:s.options?.skipOidc,skipAccountGlobals:u?.skipAccountGlobals})},{verbose:s.options?.verbose},c),P=await Y(l.getClient(K));if(!P.success)return r.onCascadeAccountComplete?.(o,!1,p(P.error.message),t),C(new Error(`Backup vault probe failed for ${e.name}: ${p(P.error.message)}`));A.fjallAdoptBackupVault=P.data,r.onCascadeAccountPhaseChange?.(o,"synth",t);const O=await n.cdkService.runCdkSynth(A,D(r),y);if(!O.success)return r.onCascadeAccountComplete?.(o,!1,p(`Synth failed: ${O.error}`),t),C(new Error(`Synth failed for ${e.name}: ${p(O.error)}`));const f=B(a==="platform"?h.PLATFORM:h.ACCOUNT),E=z(w,e.id,t),S=new U(l),{changed:_,currentHash:I}=await n.hashService.compareCascadeStack(x,f,E);if(!_&&s.options?.force!==!0&&await S.stackExists(f)){const g=await S.getStackOutputs(f);g.success||R.debug("cascadeHelpers","Failed to read outputs for skipped cascade account (non-critical)",{stackName:f,account:e.name});const T=b(g);return r.onLog?.(`${e.name}: no infrastructure changes \u2014 skipping deploy`,"info"),r.onCascadeAccountComplete?.(o,!0,void 0,t,T,!0),M({outputs:T,skipped:!0})}r.onCascadeAccountPhaseChange?.(o,"bootstrap",t);const $=await n.cdkService.runCdkBootstrap(A,D(r),y);if(!$.success)return r.onCascadeAccountComplete?.(o,!1,p(`Bootstrap failed: ${$.error}`),t),C(new Error(`Bootstrap failed for ${e.name}: ${p($.error)}`));r.onCascadeAccountPhaseChange?.(o,"deploy",t);const k=await n.cdkService.runCdkDeploy(A,f,D(r),g=>r.onCascadeAccountResourceProgress?.(o,g,t),l,y);if(!k.success)return r.onCascadeAccountComplete?.(o,!1,p(k.error),t),C(new Error(p(k.error)));const F=await S.getStackOutputs(f);F.success||R.debug("cascadeHelpers","Failed to read cascade account stack outputs (non-critical)",{stackName:f,account:e.name});const N=b(F);return I!==void 0&&((await n.hashService.persistCascadeStack(E,f,I)).success||R.debug("cascadeHelpers","Failed to persist cascade hash state (non-critical)",{stackName:f,account:e.name})),r.onCascadeAccountComplete?.(o,!0,void 0,t,N,!1),M({outputs:N,skipped:!1})}async function ge(s,n,i){const e=new Map,a=s.awsProvider.getRegion(),r=await j(s.awsProvider,n.id,a,`fjall-ipam-read-${n.name}`);if(!r.success)return R.debug("organisationDeploy",`Cannot read Platform outputs: ${r.error.message}`),e;const u=new U(r.data.provider),t=B(h.PLATFORM),o=await u.getStackOutputs(t);if(!o.success)return R.debug("organisationDeploy",`Failed to read Platform stack outputs: ${o.error.message}`),e;const c=/^IpamPoolId(\d{12})(\w+)$/;for(const m of o.data){const d=m.OutputKey?.match(c);if(d&&m.OutputValue){const l=`${d[1]}-${d[2]}`;e.set(l,m.OutputValue)}}return e.size>0&&i.onLog?.(`Read ${e.size} IPAM pool ID(s) from Platform stack`,"info"),e}async function Ce(s,n){const i=s.getDomains();if(i.length===0)return{domainsDeployed:0,errors:[]};n.onCascadePhaseStart?.("domains");const e=i.filter(t=>t.type==="apex"),a=i.filter(t=>t.type==="delegated");let r=0;const u=[];for(const t of e){const o=await s.deployDomain(t.name,n);o.success?r++:u.push(`${t.name}: ${o.error.message}`)}if(a.length>0){const t=await Promise.allSettled(a.map(o=>s.deployDomain(o.name,n)));for(let o=0;o<t.length;o++){const c=t[o],m=a[o];if(!(!c||!m))if(c.status==="fulfilled")c.value.success?r++:u.push(`${m.name}: ${c.value.error.message}`);else{const d=c.reason instanceof Error?c.reason.message:String(c.reason);u.push(`${m.name}: ${d}`)}}}return n.onCascadePhaseComplete?.("domains"),{domainsDeployed:r,errors:u}}export{de as CASCADE_MAX_CONCURRENCY,fe as buildAccountRegionPairs,Ae as buildCascadeRoleArn,me as buildRegionList,le as deployCascadeAccount,Ce as deployDomains,pe as partitionAccounts,ge as readPlatformIpamPoolIds};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import{success as H,failure as P}from"@fjall/generator";import{OrganizationsClient as Pt}from"@aws-sdk/client-organizations";import{ORGANISATION_TYPES as W,getOrganisationStackName as dt}from"../types/operations.js";import{CdkContextBuilder as It}from"../services/supporting/CdkContextBuilder.js";import{stubCallerIdentity as Nt}from"../types/deployment/index.js";import{ensureOrganisationExists as wt}from"../aws/organisations/organisation.js";import{buildParamsContext as yt,collectStackOutputs as ut,synthOrFail as lt,bootstrapOrFail as pt,forwardOutput as mt,forwardResourceProgress as gt}from"./contextHelpers.js";import{partitionAccounts as ft,deployCascadeAccount as Ct,readPlatformIpamPoolIds as ht,deployDomains as Dt,buildRegionList as Tt,buildAccountRegionPairs as kt,CASCADE_MAX_CONCURRENCY as bt}from"./cascadeHelpers.js";import{projectScalarSummary as St}from"./cascadeSummary.js";import{reconcileProviderAccounts as Lt,mergeReconciledProviderAccounts as Mt}from"./reconcileProviderAccounts.js";import{maskSensitiveOutput as u,STRUCTURAL_ENVIRONMENTS as _t,mapSettledWithConcurrency as Gt}from"@fjall/util";import{INFRA_STEP_NAME as j,STEP_IDS as h,STEP_NAMES as J}from"../types/stepDefinitions.js";async function qt(e,o,r){const g=Date.now();switch(r.type){case W.ORGANISATION:return Yt(e,o,r,g);case W.PLATFORM:return Ot(e,o,r,"platform",g);case W.ACCOUNT:return Ot(e,o,r,"account",g);default:{const t=r.type;return P(new Error(`Unsupported organisation type: ${String(t)}`))}}}function At(e,o,r,g,t,a){return It.buildDeploymentContext({deployType:g,target:r.target,path:r.path,region:o.awsProvider.getRegion(),accountName:a,callerIdentity:Nt(o.awsProvider.getAccountId()),orgId:t.orgId,rootId:t.rootId,managementAccountId:t.managementAccountId,...yt({orgConfig:e.orgConfig,identity:e.identity,skipOidc:e.options?.skipOidc})},{verbose:e.options?.verbose,infraOnly:e.options?.infraOnly},e.orgConfig)}async function Et(e){const o=e.awsProvider.getClient(Pt),r=await wt(o);return r.success?H({orgId:r.data.orgId,rootId:r.data.rootId,managementAccountId:r.data.managementAccountId}):P(r.error)}const n={CONNECT:{id:h.CONNECT,name:j.CONNECT},PREPARE:{id:h.PREPARE_ENVIRONMENT,name:j.PREPARE},DEPLOY:{id:h.DEPLOY,name:j.DEPLOY},MONITORING:{id:h.MONITORING,name:j.MONITORING},ORG_DEPLOY:{id:h.ORG_DEPLOY,name:J.ORG_DEPLOY},CASCADE_PLATFORM:{id:h.CASCADE_PLATFORM,name:J.CASCADE_PLATFORM},CASCADE_ACCOUNTS:{id:h.CASCADE_ACCOUNTS,name:J.CASCADE_ACCOUNTS}},S=4;async function Ot(e,o,r,g,t){const{callbacks:a}=e;a.onStepComplete?.(n.CONNECT.id,n.CONNECT.name,"completed",0,S),a.onStepStart?.(n.PREPARE.id,n.PREPARE.name,1,S);const c=await Et(o);if(!c.success){a.onStepComplete?.(n.PREPARE.id,n.PREPARE.name,"error",1,S);const I=new Error(u(c.error.message));return a.onError?.(I),P(I)}const Y=At(e,o,r,g,c.data,g==="account"?r.target:void 0);a.onLog?.(`Synthesising ${g} infrastructure\u2026`,"info");const b=await lt(o,Y,a,"CDK synthesis failed");if(!b.success)return a.onStepComplete?.(n.PREPARE.id,n.PREPARE.name,"error",1,S),b;const L=await pt(o,Y,a);if(!L.success)return a.onStepComplete?.(n.PREPARE.id,n.PREPARE.name,"error",1,S),L;a.onStepComplete?.(n.PREPARE.id,n.PREPARE.name,"completed",1,S);const D=dt(r.type);a.onStepStart?.(n.DEPLOY.id,n.DEPLOY.name,2,S);const T=await o.cdkService.runCdkDeploy(Y,D,mt(a),gt(a),o.awsProvider);if(!T.success){a.onStepComplete?.(n.DEPLOY.id,n.DEPLOY.name,"error",2,S);const I=new Error(u(T.error));return a.onError?.(I),P(I)}a.onStepComplete?.(n.DEPLOY.id,n.DEPLOY.name,"completed",2,S);const k=await o.cfnService.getStackOutputs(D);k.success||a.onLog?.("Failed to read stack outputs (non-critical)","debug");const z=ut(k);return a.onStepStart?.(n.MONITORING.id,n.MONITORING.name,3,S),a.onStepComplete?.(n.MONITORING.id,n.MONITORING.name,"completed",3,S),H({target:r.target,deploymentType:"organisation",outputs:z,durationMs:Date.now()-t})}async function Yt(e,o,r,g){const{callbacks:t,options:a}=e;let c=e.orgConfig?.providerAccounts??[];if(c.length===0||c.every(s=>s.environment===_t.ROOT)){const s=await Lt(o,e.workingDirectory);if(s.success){const{providerAccounts:i,missingAccountNames:E}=s.data;i.length>0&&(c=Mt(e.orgConfig,i).providerAccounts,t.onLog?.(`Reconciled ${i.length} account(s) from AWS Organizations`,"info")),E.length>0&&(t.onCascadeMissingAccounts?.(E),t.onProgress?.({type:"warning",message:u(`Accounts declared in ACCOUNTS but not yet in AWS Organizations (cascade will skip): ${E.join(", ")}`)}))}else t.onProgress?.({type:"warning",message:u(`Could not reconcile accounts from AWS Organizations \u2014 cascade may skip accounts: ${s.error.message}`)})}const b=e.orgConfig?.primaryRegion??o.awsProvider.getRegion();c=c.map(s=>s.region!==void 0?s:{...s,region:b});const L=e.orgConfig!==void 0?{...e.orgConfig,providerAccounts:c}:c.length>0?{providerAccounts:c}:void 0,D=await Et(o);if(!D.success){const s=new Error(u(D.error.message));return t.onError?.(s),P(s)}const T=At(e,o,r,"organisation",D.data),k=a?.cascade!==!1,{platformAccount:z,memberAccounts:I}=ft(c),Q=k&&z!==void 0?1:0,Z=k&&I.length>0?1:0,l=2+Q+Z;t.onCascadeAccountsReconciled?.({hasPlatformAccount:Q>0,hasMemberAccounts:Z>0});const{id:$,name:F}=n.PREPARE;t.onStepStart?.($,F,0,l),t.onLog?.("Synthesising organisation infrastructure\u2026","info");const tt=await lt(o,T,t,"CDK synthesis failed");if(!tt.success)return t.onStepComplete?.($,F,"error",0,l),tt;const et=await pt(o,T,t);if(!et.success)return t.onStepComplete?.($,F,"error",0,l),et;t.onStepComplete?.($,F,"completed",0,l);const{id:K,name:V}=n.ORG_DEPLOY;t.onStepStart?.(K,V,1,l);const ot=dt(W.ORGANISATION),nt=await o.cdkService.runCdkDeploy(T,ot,mt(t),gt(t),o.awsProvider);if(!nt.success){t.onStepComplete?.(K,V,"error",1,l);const s=new Error(u(nt.error));return t.onError?.(s),P(s)}const rt=await o.cfnService.getStackOutputs(ot);rt.success||t.onLog?.("Failed to read org stack outputs (non-critical)","debug");const Rt=ut(rt);t.onStepComplete?.(K,V,"completed",1,l);const p=[],x=[];if(k&&c.length>0){t.onCascadeStart?.();const s=Date.now();let i=2,E=!1,U,at=!1;const v=[],st=d=>({members:v,...U!==void 0?{platform:U}:{},domainsDeployed:at,errors:p,totalDurationMs:d}),{platformAccount:A,memberAccounts:B}=ft(c);if(A){const{id:d,name:m}=n.CASCADE_PLATFORM;t.onStepStart?.(d,m,i,l),t.onCascadePhaseStart?.("platform");let f;const X=Date.now();try{f=await Ct(e,o,r,A,"platform",t,{orgConfig:L})}catch(O){const _=u(O instanceof Error?O.message:String(O));p.push({accountId:A.id,error:_}),t.onCascadePhaseComplete?.("platform"),t.onStepComplete?.(d,m,"error",i,l),f=P(new Error(_))}const M=Date.now()-X;if(f.success){E=!0;const O=f.data.skipped===!0;f.data.outputs&&x.push({accountId:A.id,outputs:f.data.outputs}),t.onCascadePhaseComplete?.("platform"),t.onStepComplete?.(d,m,O?"skipped":"completed",i,l),U={accountId:A.id,result:O?"skipped":"succeeded",durationMs:M}}else p.some(O=>O.accountId===A.id)||(p.push({accountId:A.id,error:u(f.error.message)}),t.onCascadePhaseComplete?.("platform"),t.onStepComplete?.(d,m,"error",i,l)),U={accountId:A.id,result:"failed",durationMs:M,error:u(f.error.message)};i++}let ct=new Map;if(E&&A&&(ct=await ht(o,A,t)),e.domainProvider){const d=await Dt(e.domainProvider,t);at=d.domainsDeployed>0;for(const m of d.errors)p.push({accountId:"domains",error:u(m)})}if(B.length>0){const{id:d,name:m}=n.CASCADE_ACCOUNTS;t.onStepStart?.(d,m,i,l),t.onCascadePhaseStart?.("accounts");const f=Tt(e.orgConfig),X=f[0]??b,M=kt(B,f);(await Gt(M,bt,async({account:R,region:G})=>{const N=G.replace(/-/g,""),C=ct.get(`${R.id}-${N}`),w=Date.now();return{result:await Ct(e,o,r,R,"account",t,{ipamPoolId:C,orgConfig:L,region:G,skipAccountGlobals:G!==X}),durationMs:Date.now()-w}})).forEach((R,G)=>{const N=M[G];if(!N)return;const C=N.account;if(R.status==="rejected"){const y=u(R.reason instanceof Error?R.reason.message:String(R.reason));v.push({accountId:C.id,accountName:C.name,region:N.region,result:"failed",durationMs:0,error:y}),p.push({accountId:C.id,error:y});return}const{result:w,durationMs:q}=R.value;if(w.success){const y=w.data.skipped===!0;w.data.outputs&&x.push({accountId:C.id,outputs:w.data.outputs}),v.push({accountId:C.id,accountName:C.name,region:N.region,result:y?"skipped":"succeeded",durationMs:q})}else{const y=u(w.error.message);v.push({accountId:C.id,accountName:C.name,region:N.region,result:"failed",durationMs:q,error:y}),p.push({accountId:C.id,error:y})}});const _=St(st(0));t.onCascadePhaseComplete?.("accounts"),t.onStepComplete?.(d,m,_.accountsFailed>0?"error":_.accountsSkipped===B.length?"skipped":"completed",i,l)}const it=st(Date.now()-s);if(t.onCascadeComplete?.(St(it)),t.onCascadeLedger?.(it),p.length>0){const d=p.map(m=>` ${m.accountId}: ${m.error}`).join(`
|
|
2
|
-
`);t.onLog?.(
|
|
3
|
-
${
|
|
1
|
+
import{join as It}from"node:path";import{success as nt,failure as I}from"@fjall/generator";import{OrganizationsClient as wt}from"@aws-sdk/client-organizations";import{BackupClient as yt}from"@aws-sdk/client-backup";import{ORGANISATION_TYPES as H,getOrganisationStackName as mt}from"../types/operations.js";import{CdkContextBuilder as Nt}from"../services/supporting/CdkContextBuilder.js";import{stubCallerIdentity as Dt}from"../types/deployment/index.js";import{ensureOrganisationExists as kt}from"../aws/organisations/organisation.js";import{describeBackupVaultExists as Tt}from"../aws/organisations/backup.js";import{buildParamsContext as bt,collectStackOutputs as rt,synthOrFail as gt,bootstrapOrFail as ft,forwardOutput as Ct,forwardResourceProgress as St}from"./contextHelpers.js";import{partitionAccounts as Et,deployCascadeAccount as At,readPlatformIpamPoolIds as Lt,deployDomains as Mt,buildRegionList as _t,buildAccountRegionPairs as $t,CASCADE_MAX_CONCURRENCY as Gt}from"./cascadeHelpers.js";import{projectScalarSummary as Ot}from"./cascadeSummary.js";import{reconcileProviderAccounts as Yt,mergeReconciledProviderAccounts as xt}from"./reconcileProviderAccounts.js";import{maskSensitiveOutput as c,STRUCTURAL_ENVIRONMENTS as Ft,mapSettledWithConcurrency as vt}from"@fjall/util";import{INFRA_STEP_NAME as V,STEP_IDS as T,STEP_NAMES as at}from"../types/stepDefinitions.js";async function ne(o,e,s){const C=Date.now();switch(s.type){case H.ORGANISATION:return Ut(o,e,s,C);case H.PLATFORM:return ht(o,e,s,"platform",C);case H.ACCOUNT:return ht(o,e,s,"account",C);default:{const t=s.type;return I(new Error(`Unsupported organisation type: ${String(t)}`))}}}function Rt(o,e,s,C,t,r){return Nt.buildDeploymentContext({deployType:C,target:s.target,path:s.path,region:e.awsProvider.getRegion(),accountName:r,callerIdentity:Dt(e.awsProvider.getAccountId()),orgId:t.orgId,rootId:t.rootId,managementAccountId:t.managementAccountId,...bt({orgConfig:o.orgConfig,identity:o.identity,skipOidc:o.options?.skipOidc})},{verbose:o.options?.verbose,infraOnly:o.options?.infraOnly},o.orgConfig)}async function Pt(o){const e=o.awsProvider.getClient(wt),s=await kt(e);return s.success?nt({orgId:s.data.orgId,rootId:s.data.rootId,managementAccountId:s.data.managementAccountId}):I(s.error)}const n={CONNECT:{id:T.CONNECT,name:V.CONNECT},PREPARE:{id:T.PREPARE_ENVIRONMENT,name:V.PREPARE},DEPLOY:{id:T.DEPLOY,name:V.DEPLOY},MONITORING:{id:T.MONITORING,name:V.MONITORING},ORG_DEPLOY:{id:T.ORG_DEPLOY,name:at.ORG_DEPLOY},CASCADE_PLATFORM:{id:T.CASCADE_PLATFORM,name:at.CASCADE_PLATFORM},CASCADE_ACCOUNTS:{id:T.CASCADE_ACCOUNTS,name:at.CASCADE_ACCOUNTS}},f=4;async function ht(o,e,s,C,t){const{callbacks:r}=o;r.onStepComplete?.(n.CONNECT.id,n.CONNECT.name,"completed",0,f),r.onStepStart?.(n.PREPARE.id,n.PREPARE.name,1,f);const d=await Pt(e);if(!d.success){r.onStepComplete?.(n.PREPARE.id,n.PREPARE.name,"error",1,f);const S=new Error(c(d.error.message));return r.onError?.(S),I(S)}const _=Rt(o,e,s,C,d.data,C==="account"?s.target:void 0),b=await Tt(e.awsProvider.getClient(yt));if(!b.success){r.onStepComplete?.(n.PREPARE.id,n.PREPARE.name,"error",1,f);const S=new Error(c(b.error.message));return r.onError?.(S),I(S)}_.fjallAdoptBackupVault=b.data,r.onLog?.(`Synthesising ${C} infrastructure\u2026`,"info");const $=await gt(e,_,r,"CDK synthesis failed");if(!$.success)return r.onStepComplete?.(n.PREPARE.id,n.PREPARE.name,"error",1,f),$;const L=await ft(e,_,r);if(!L.success)return r.onStepComplete?.(n.PREPARE.id,n.PREPARE.name,"error",1,f),L;r.onStepComplete?.(n.PREPARE.id,n.PREPARE.name,"completed",1,f);const h=mt(s.type);r.onStepStart?.(n.DEPLOY.id,n.DEPLOY.name,2,f);const M=await e.cdkService.runCdkDeploy(_,h,Ct(r),St(r),e.awsProvider);if(!M.success){r.onStepComplete?.(n.DEPLOY.id,n.DEPLOY.name,"error",2,f);const S=new Error(c(M.error));return r.onError?.(S),I(S)}r.onStepComplete?.(n.DEPLOY.id,n.DEPLOY.name,"completed",2,f);const v=await e.cfnService.getStackOutputs(h);v.success||r.onLog?.("Failed to read stack outputs (non-critical)","debug");const K=rt(v);return r.onStepStart?.(n.MONITORING.id,n.MONITORING.name,3,f),r.onStepComplete?.(n.MONITORING.id,n.MONITORING.name,"completed",3,f),nt({target:s.target,deploymentType:"organisation",outputs:K,durationMs:Date.now()-t})}async function Ut(o,e,s,C){const{callbacks:t,options:r}=o;let d=o.orgConfig?.providerAccounts??[];if(d.length===0||d.every(a=>a.environment===Ft.ROOT)){const a=await Yt(e,o.workingDirectory);if(a.success){const{providerAccounts:i,missingAccountNames:m}=a.data;i.length>0&&(d=xt(o.orgConfig,i).providerAccounts,t.onLog?.(`Reconciled ${i.length} account(s) from AWS Organizations`,"info")),m.length>0&&(t.onCascadeMissingAccounts?.(m),t.onProgress?.({type:"warning",message:c(`Accounts declared in ACCOUNTS but not yet in AWS Organizations (cascade will skip): ${m.join(", ")}`)}))}else t.onProgress?.({type:"warning",message:c(`Could not reconcile accounts from AWS Organizations \u2014 cascade may skip accounts: ${a.error.message}`)})}const b=o.orgConfig?.primaryRegion??e.awsProvider.getRegion();d=d.map(a=>a.region!==void 0?a:{...a,region:b});const $=o.orgConfig!==void 0?{...o.orgConfig,providerAccounts:d}:d.length>0?{providerAccounts:d}:void 0,L=await Pt(e);if(!L.success){const a=new Error(c(L.error.message));return t.onError?.(a),I(a)}const h=Rt(o,e,s,"organisation",L.data),M=r?.cascade!==!1,{platformAccount:v,memberAccounts:K}=Et(d),S=M&&v!==void 0?1:0,st=M&&K.length>0?1:0,l=2+S+st;t.onCascadeAccountsReconciled?.({hasPlatformAccount:S>0,hasMemberAccounts:st>0});const{id:U,name:W}=n.PREPARE;t.onStepStart?.(U,W,0,l),t.onLog?.("Synthesising organisation infrastructure\u2026","info");const ct=await gt(e,h,t,"CDK synthesis failed");if(!ct.success)return t.onStepComplete?.(U,W,"error",0,l),ct;const it=await ft(e,h,t);if(!it.success)return t.onStepComplete?.(U,W,"error",0,l),it;t.onStepComplete?.(U,W,"completed",0,l);const{id:X,name:q}=n.ORG_DEPLOY,N=mt(H.ORGANISATION);let dt=!0;const G=await e.hashService.getTemplateHashes(It(h.path,"cdk.out"));if(G.success){const a=await e.hashService.compareWithState(G.data,h.path);a.success?dt=a.data.stackChanges.get(N)??!0:t.onLog?.(c(`Org root change detection failed \u2014 deploying to be safe: ${a.error.message}`),"warn")}else t.onLog?.(c(`Org root template hashing failed \u2014 deploying to be safe: ${G.error.message}`),"warn");const J=dt||r?.force===!0||!await e.cfnService.stackExists(N);t.onOrgChangesDetected?.({hasOrgChanges:J});let Q;if(J){t.onStepStart?.(X,q,1,l);const a=await e.cdkService.runCdkDeploy(h,N,Ct(t),St(t),e.awsProvider);if(!a.success){t.onStepComplete?.(X,q,"error",1,l);const O=new Error(c(a.error));return t.onError?.(O),I(O)}const i=await e.cfnService.getStackOutputs(N);i.success||t.onLog?.("Failed to read org stack outputs (non-critical)","debug"),Q=rt(i);const m=G.success?G.data.get(N):void 0;if(m!==void 0){const O=await e.hashService.updateStateAfterDeploy(h.path,new Map([[N,m]]));O.success||t.onLog?.(`Warning: failed to update state file \u2014 next deploy may re-deploy the org root: ${c(O.error.message)}`,"warn")}t.onStepComplete?.(X,q,"completed",1,l)}else{t.onLog?.("Organisation root: no infrastructure changes \u2014 skipping deploy","info");const a=await e.cfnService.getStackOutputs(N);a.success||t.onLog?.("Failed to read org stack outputs (non-critical)","debug"),Q=rt(a)}const p=[],j=[];let z=J;if(M&&d.length>0){t.onCascadeStart?.();const a=Date.now();let i=2,m=!1,O,Z=!1;const B=[],ut=u=>({members:B,...O!==void 0?{platform:O}:{},domainsDeployed:Z,errors:p,totalDurationMs:u}),{platformAccount:R,memberAccounts:tt}=Et(d);if(R){const{id:u,name:g}=n.CASCADE_PLATFORM;t.onStepStart?.(u,g,i,l),t.onCascadePhaseStart?.("platform");let E;const et=Date.now();try{E=await At(o,e,s,R,"platform",t,{orgConfig:$})}catch(P){const x=c(P instanceof Error?P.message:String(P));p.push({accountId:R.id,error:x}),t.onCascadePhaseComplete?.("platform"),t.onStepComplete?.(u,g,"error",i,l),E=I(new Error(x))}const Y=Date.now()-et;if(E.success){m=!0;const P=E.data.skipped===!0;P||(z=!0),E.data.outputs&&j.push({accountId:R.id,outputs:E.data.outputs}),t.onCascadePhaseComplete?.("platform"),t.onStepComplete?.(u,g,P?"skipped":"completed",i,l),O={accountId:R.id,result:P?"skipped":"succeeded",durationMs:Y}}else p.some(P=>P.accountId===R.id)||(p.push({accountId:R.id,error:c(E.error.message)}),t.onCascadePhaseComplete?.("platform"),t.onStepComplete?.(u,g,"error",i,l)),O={accountId:R.id,result:"failed",durationMs:Y,error:c(E.error.message)};i++}let lt=new Map;if(m&&R&&(lt=await Lt(e,R,t)),o.domainProvider){const u=await Mt(o.domainProvider,t);Z=u.domainsDeployed>0,Z&&(z=!0);for(const g of u.errors)p.push({accountId:"domains",error:c(g)})}if(tt.length>0){const{id:u,name:g}=n.CASCADE_ACCOUNTS;t.onStepStart?.(u,g,i,l),t.onCascadePhaseStart?.("accounts");const E=_t(o.orgConfig),et=E[0]??b,Y=$t(tt,E);(await vt(Y,Gt,async({account:w,region:F})=>{const D=F.replace(/-/g,""),A=lt.get(`${w.id}-${D}`),k=Date.now();return{result:await At(o,e,s,w,"account",t,{ipamPoolId:A,orgConfig:$,region:F,skipAccountGlobals:F!==et}),durationMs:Date.now()-k}})).forEach((w,F)=>{const D=Y[F];if(!D)return;const A=D.account;if(w.status==="rejected"){const y=c(w.reason instanceof Error?w.reason.message:String(w.reason));B.push({accountId:A.id,accountName:A.name,region:D.region,result:"failed",durationMs:0,error:y}),p.push({accountId:A.id,error:y});return}const{result:k,durationMs:ot}=w.value;if(k.success){const y=k.data.skipped===!0;y||(z=!0),k.data.outputs&&j.push({accountId:A.id,outputs:k.data.outputs}),B.push({accountId:A.id,accountName:A.name,region:D.region,result:y?"skipped":"succeeded",durationMs:ot})}else{const y=c(k.error.message);B.push({accountId:A.id,accountName:A.name,region:D.region,result:"failed",durationMs:ot,error:y}),p.push({accountId:A.id,error:y})}});const x=Ot(ut(0));t.onCascadePhaseComplete?.("accounts"),t.onStepComplete?.(u,g,x.accountsFailed>0?"error":x.accountsSkipped===tt.length?"skipped":"completed",i,l)}const pt=ut(Date.now()-a);if(t.onCascadeComplete?.(Ot(pt)),t.onCascadeLedger?.(pt),p.length>0){const u=p.map(g=>` ${g.accountId}: ${g.error}`).join(`
|
|
2
|
+
`);t.onLog?.(c(`Cascade failed for ${p.length} target(s):
|
|
3
|
+
${u}`),"warn")}}if(p.length>0){const a=p.map(m=>c(`${m.accountId}: ${m.error}`)).join(`
|
|
4
4
|
`),i=new Error(`Organisation root deployed, but the cascade failed for ${p.length} target(s):
|
|
5
|
-
${
|
|
5
|
+
${a}`);return t.onError?.(i),I(i)}return nt({target:s.target,deploymentType:"organisation",outputs:Q,...j.length>0?{cascadeOutputs:j}:{},...z?{}:{noChanges:!0},durationMs:Date.now()-C})}export{ne as deployOrganisation};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{filterDangerousEnvVars as
|
|
1
|
+
import{filterDangerousEnvVars as u}from"@fjall/util";class s{buildContextArgs(r){const a=[];return r?.accountId&&a.push("-c",`accountId=${r.accountId}`),r?.environment&&a.push("-c",`environment=${r.environment}`),r?.managedAccount&&a.push("-c","managedAccount=true"),r?.accountName&&a.push("-c",`accountName=${r.accountName}`),r?.orgId&&a.push("-c",`orgId=${r.orgId}`),r?.rootId&&a.push("-c",`rootId=${r.rootId}`),r?.managementAccountId&&a.push("-c",`managementAccountId=${r.managementAccountId}`),r?.ipamPoolId&&a.push("-c",`ipamPoolId=${r.ipamPoolId}`),r?.fjallOrgId&&a.push("-c",`fjallOrgId=${r.fjallOrgId}`),r?.fjallOidcConfigured&&a.push("-c",`fjallOidcConfigured=${r.fjallOidcConfigured}`),r?.fjallAccountGlobalsConfigured&&a.push("-c",`fjallAccountGlobalsConfigured=${r.fjallAccountGlobalsConfigured}`),r?.orgConfig&&a.push("-c",`orgConfig=${r.orgConfig}`),r?.fjallAdoptBackupVault&&a.push("-c","fjallAdoptBackupVault=true"),a}buildParameterArgs(r){if(r===void 0)return[];const a=Object.entries(r);if(a.length===0)return[];const o=[];for(const[e,n]of a){if(!/^[A-Za-z][A-Za-z0-9]*$/.test(e))throw new Error(`Invalid CloudFormation parameter name "${e}": must match /^[A-Za-z][A-Za-z0-9]*$/ (alphanumeric, leading letter, no separators).`);if(n==="")throw new Error(`CloudFormation parameter "${e}" has an empty value.`);if(/[,\n\r]/.test(n))throw new Error(`CloudFormation parameter "${e}" value contains "," or newline \u2014 cdk's --parameters splits on "," so the deploy would silently fragment.`);o.push("--parameters",`${e}=${n}`)}return o}injectCascadeCredentials(r,a){a&&(r.AWS_ACCESS_KEY_ID=a.accessKeyId,r.AWS_SECRET_ACCESS_KEY=a.secretAccessKey,delete r.AWS_SESSION_TOKEN,a.sessionToken&&(r.AWS_SESSION_TOKEN=a.sessionToken))}buildCdkEnv(r){const a={...u(process.env),CI:"true",FORCE_COLOR:"0",CDK_DISABLE_VERSION_CHECK:"1"};return r?.context?.region&&(a.AWS_REGION=r.context.region,a.AWS_DEFAULT_REGION=r.context.region,a.CDK_DEFAULT_REGION=r.context.region),r?.context?.accountId&&(a.CDK_DEFAULT_ACCOUNT=r.context.accountId),this.injectCascadeCredentials(a,r?.credentials),a}}export{s as CdkArgumentBuilder};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{getApplicationStackName as i,getOrganisationStackName as u,isApplicationStack as
|
|
1
|
+
import{getApplicationStackName as i,getOrganisationStackName as u,isApplicationStack as l}from"../../types/operations.js";const p=5e3;function t(a,o){if(a&&!a.includes("*"))return a;if(a){const e=a.match(/\*?(\w+)\*?/);if(e?.[1]){const n=e[1],r=o.target;return l(n)?i(r,n):`${r}${n}`}return a}}function c(a){const o=a.deployType;return o==="organisation"||o==="platform"||o==="account"?u(o):`${a.target}Network`}function f(a,o,e){return{accountId:o,region:e,environment:a.environment,managedAccount:a.isManagedAccount,accountName:a.accountName,orgId:a.orgId,rootId:a.rootId,managementAccountId:a.managementAccountId,ipamPoolId:a.ipamPoolId,fjallOrgId:a.fjallOrgId,fjallOidcConfigured:a.fjallOidcConfigured?"true":void 0,fjallAccountGlobalsConfigured:a.fjallAccountGlobalsConfigured?"true":void 0,orgConfig:a.orgConfig,fjallAdoptBackupVault:a.fjallAdoptBackupVault?"true":void 0}}export{p as STACK_DETECTION_FALLBACK_MS,f as buildDeploymentCdkContext,c as getFallbackStackName,t as resolveStackName};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{APPLICATION_STACKS as n,APPLICATION_DEPLOY_ORDER as A,getApplicationStepName as t,getApplicationStepId as a}from"../types/index.js";import{STEP_IDS as e,STEP_NAMES as s,INFRA_STEP_NAME as d}from"../types/index.js";const O={Network:e.NETWORK_DESTROY,Storage:e.STORAGE_DESTROY,Messaging:e.MESSAGING_DESTROY,Database:e.DATABASE_DESTROY,Compute:e.COMPUTE_DESTROY,Cdn:e.CDN_DESTROY};function
|
|
1
|
+
import{APPLICATION_STACKS as n,APPLICATION_DEPLOY_ORDER as A,getApplicationStepName as t,getApplicationStepId as a}from"../types/index.js";import{STEP_IDS as e,STEP_NAMES as s,INFRA_STEP_NAME as d}from"../types/index.js";const O={Network:e.NETWORK_DESTROY,Storage:e.STORAGE_DESTROY,Messaging:e.MESSAGING_DESTROY,Database:e.DATABASE_DESTROY,Compute:e.COMPUTE_DESTROY,Cdn:e.CDN_DESTROY};function S(f){return O[f]??`${f.toLowerCase()}-destroy`}class p{static DEPLOYMENT_STEPS=new Map([["application-deploy",[{id:e.AUTH,name:s.AUTH},{id:e.DETECT_CONFIG,name:s.PREPARE_DEPLOY},{id:e.BOOTSTRAP,name:s.BOOTSTRAP,conditions:{requiresInfra:!0}},{id:a(n.NETWORK,"deploy"),name:t(n.NETWORK,"deploy"),conditions:{requiresInfra:!0,requiresNetwork:!0}},{id:a(n.STORAGE,"deploy"),name:t(n.STORAGE,"deploy"),conditions:{requiresInfra:!0,requiresStorage:!0}},{id:a(n.MESSAGING,"deploy"),name:t(n.MESSAGING,"deploy"),conditions:{requiresInfra:!0,requiresMessaging:!0}},{id:a(n.DATABASE,"deploy"),name:t(n.DATABASE,"deploy"),conditions:{requiresInfra:!0,requiresDatabase:!0}},{id:e.DOCKER_OPERATIONS,name:"Docker operations",conditions:{requiresDocker:!0,requiresCompute:!0,skipForInfraOnly:!0}},{id:e.TAG_ECR_IMAGES,name:"Tagging container images",conditions:{requiresImageTagging:!0,requiresCompute:!0,skipForInfraOnly:!0}},{id:a(n.COMPUTE,"deploy"),name:t(n.COMPUTE,"deploy"),conditions:{requiresInfra:!0,requiresCompute:!0}},{id:a(n.CDN,"deploy"),name:t(n.CDN,"deploy"),conditions:{requiresInfra:!0,requiresCdn:!0}}]],["application-destroy",[{id:e.AUTH,name:s.AUTH},{id:e.DETECT_CONFIG,name:s.CHECK_INFRA_STATE},{id:a(n.CDN,"destroy"),name:t(n.CDN,"destroy"),conditions:{requiresCdn:!0}},{id:a(n.COMPUTE,"destroy"),name:t(n.COMPUTE,"destroy")},{id:a(n.DATABASE,"destroy"),name:t(n.DATABASE,"destroy")},{id:a(n.MESSAGING,"destroy"),name:t(n.MESSAGING,"destroy"),conditions:{requiresMessaging:!0}},{id:a(n.STORAGE,"destroy"),name:t(n.STORAGE,"destroy"),conditions:{requiresStorage:!0}},{id:a(n.NETWORK,"destroy"),name:t(n.NETWORK,"destroy")}]],["organisation-deploy",[{id:e.AUTH,name:s.AUTH},{id:e.ORG_SETUP,name:s.ORG_SETUP},{id:e.PREPARE_ENVIRONMENT,name:d.PREPARE},{id:e.ORG_DEPLOY,name:s.ORG_DEPLOY,conditions:{requiresOrgChanges:!0}},{id:e.CASCADE_PLATFORM,name:s.CASCADE_PLATFORM,conditions:{requiresPlatformAccount:!0}},{id:e.CASCADE_ACCOUNTS,name:s.CASCADE_ACCOUNTS,conditions:{requiresMemberAccounts:!0}}]],["organisation-destroy",[{id:e.AUTH,name:s.AUTH},{id:e.CFN_CHECK,name:s.CHECK_INFRA_STATE},{id:e.CASCADE_ACCOUNTS,name:"Destroying account infrastructure",conditions:{requiresMemberAccounts:!0}},{id:e.CASCADE_PLATFORM,name:"Destroying platform infrastructure",conditions:{requiresPlatformAccount:!0}},{id:e.DESTROY,name:"Destroying organisation infrastructure"}]],["platform-deploy",[{id:e.AUTH,name:s.AUTH},{id:e.CONNECT,name:d.CONNECT},{id:e.PREPARE_ENVIRONMENT,name:d.PREPARE},{id:e.DEPLOY,name:d.DEPLOY},{id:e.MONITORING,name:d.MONITORING}]],["platform-destroy",[{id:e.AUTH,name:s.AUTH},{id:e.CFN_CHECK,name:"Checking infrastructure state"},{id:e.ORG_DESTROY,name:"Destroying platform infrastructure"}]],["account-deploy",[{id:e.AUTH,name:s.AUTH},{id:e.CONNECT,name:d.CONNECT},{id:e.PREPARE_ENVIRONMENT,name:d.PREPARE},{id:e.DEPLOY,name:d.DEPLOY},{id:e.MONITORING,name:d.MONITORING}]],["account-destroy",[{id:e.AUTH,name:s.AUTH},{id:e.CFN_CHECK,name:"Checking infrastructure state"},{id:e.ORG_DESTROY,name:"Destroying account infrastructure"}]]]);static getSteps(r){if(r.deploymentType==="application"&&r.operation==="deploy"&&r.deployOnly)return[{id:e.AUTH,name:s.AUTH},{id:e.DOCKER_OPERATIONS,name:"Build and push Docker container"},{id:a(n.COMPUTE,"deploy"),name:t(n.COMPUTE,"deploy")}];const o=`${r.deploymentType}-${r.operation}`;return(this.DEPLOYMENT_STEPS.get(o)??[]).filter(i=>{if(r.deploymentType==="application"){const l=r.builderName==="opennext";if(i.conditions?.requiresInfra&&r.deployOnly)return!1;if(r.resources){const c=r.resources;if(i.conditions?.requiresNetwork&&!c.hasNetwork||i.conditions?.requiresCompute&&!c.hasCompute||i.conditions?.requiresDatabase&&!c.hasDatabase||i.conditions?.requiresStorage&&!c.hasStorage||i.conditions?.requiresMessaging&&!c.hasMessaging||i.conditions?.requiresCdn&&!c.hasCdn)return!1}else if(r.operation==="deploy"&&!l&&(i.conditions?.requiresNetwork||i.conditions?.requiresCompute||i.conditions?.requiresDatabase||i.conditions?.requiresStorage||i.conditions?.requiresMessaging||i.conditions?.requiresCdn))return!1;if(i.conditions?.requiresDocker||i.id===e.DOCKER_OPERATIONS)return l||r.infraOnly||r.resources&&!r.resources.hasCompute?!1:r.deployOnly?r.hasDockerfile===!0:r.hasDockerfile===!0||r.hasDockerfile===!1&&!r.isManagedAccount;if(i.conditions?.requiresImageTagging)return r.infraOnly||l||r.resources&&!r.resources.hasCompute?!1:r.hasDockerfile===!1}return!(i.conditions?.requiresMemberAccounts&&!r.hasMemberAccounts||i.conditions?.requiresPlatformAccount&&!r.hasPlatformAccount||i.conditions?.requiresOrgChanges&&r.hasOrgChanges!==!0||i.conditions?.requiresPlatformChanges&&r.hasPlatformChanges!==!0||i.conditions?.requiresAccountChanges&&r.hasAccountChanges!==!0||i.conditions?.requiresDomainConfiguration&&!r.hasDomainConfiguration||i.conditions?.requiresDomainChanges&&r.hasDomainChanges!==!0)}).map(i=>i.id===e.DOCKER_OPERATIONS?{...i,name:r.hasDockerfile?"Building and pushing Docker image":"Initialising container repository"}:i)}static getStepNames(r){return this.getSteps(r).map(o=>o.name)}static getStepIndex(r,o){return this.getSteps(o).findIndex(E=>E.id===r)}static getStepById(r,o="application"){const u=`${o}-deploy`,i=(this.DEPLOYMENT_STEPS.get(u)||[]).find(T=>T.id===r);if(i)return i;const l=`${o}-destroy`;return(this.DEPLOYMENT_STEPS.get(l)||[]).find(T=>T.id===r)}static isStepIncluded(r,o){return this.getSteps(o).some(E=>E.id===r)}static createContext(r,o,u){return{deploymentType:r,operation:o,deployOnly:u?.deployOnly??!1,infraOnly:u?.infraOnly??!1,isManagedAccount:u?.isManagedAccount??!1,hasDockerfile:u?.hasDockerfile??!1,pattern:u?.pattern??null,builderName:u?.builderName,resources:u?.resources}}static getDeploymentTypes(){return["application","organisation","platform","account"]}static getStackNames(r){switch(r){case"application":return[...A];case"organisation":return["Organisation"];case"platform":return["Platform"];case"account":return["Account"];default:return[]}}static getInfraStepIds(){return[e.CFN_CHECK,e.BOOTSTRAP,e.DIFF,e.NETWORK,e.STORAGE,e.MESSAGING,e.DATABASE,e.COMPUTE,e.CDN]}static getDockerStepIds(){return[e.ECR_INIT,e.DOCKER_DEPLOY]}}export{p as StepRegistry,S as getDestroyStepId};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { DeployCallbacks } from "./callbacks.js";
|
|
2
|
+
/**
|
|
3
|
+
* Every {@link DeployCallbacks} key (43 callbacks + the `verbose` flag),
|
|
4
|
+
* derived from the type-checked {@link CALLBACK_KEY_PRESENCE} map so the list
|
|
5
|
+
* can never silently diverge from the interface.
|
|
6
|
+
*/
|
|
7
|
+
export declare const ALL_CALLBACK_KEYS: Array<keyof DeployCallbacks>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const e={onStepStart:!0,onStepComplete:!0,onProgress:!0,onLog:!0,onOutput:!0,onResourceProgress:!0,onParallelPhaseStart:!0,onParallelPhaseComplete:!0,onParallelStackResourceProgress:!0,onDockerProgress:!0,onBuildPushStart:!0,onBuildPushProgress:!0,onBuildPushComplete:!0,onTaskDefRegistered:!0,onECSUpdate:!0,onECSProgress:!0,onECSComplete:!0,onMigrationsStart:!0,onMigrationsComplete:!0,onCDKBootstrap:!0,onCdkOutput:!0,onCascadeStart:!0,onCascadeAccountsReconciled:!0,onOrgChangesDetected:!0,onCascadeMissingAccounts:!0,onCascadePhaseStart:!0,onCascadePhaseComplete:!0,onCascadeAccountStart:!0,onCascadeAccountPhaseChange:!0,onCascadeAccountResourceProgress:!0,onCascadeAccountComplete:!0,onCascadeComplete:!0,onCascadeLedger:!0,onStackCleanupProgress:!0,onDetectionComplete:!0,onDetectionPhaseChange:!0,onSSOUrl:!0,onAuthComplete:!0,onOpenNextBuildStart:!0,onOpenNextProgress:!0,onOpenNextBuildComplete:!0,onOpenNextBuildError:!0,onError:!0,verbose:!0},t=Object.keys(e);export{t as ALL_CALLBACK_KEYS};
|
|
@@ -114,6 +114,18 @@ export interface DeployCallbacks {
|
|
|
114
114
|
hasPlatformAccount: boolean;
|
|
115
115
|
hasMemberAccounts: boolean;
|
|
116
116
|
}) => void;
|
|
117
|
+
/**
|
|
118
|
+
* @emittedBy engine — fired once after org synthesis, before the org root
|
|
119
|
+
* deploy, carrying the template-hash verdict for the Organisation root stack.
|
|
120
|
+
* Consumers that pre-declare a fixed step list (the CLI Ink UI) use it to
|
|
121
|
+
* reveal the "Deploying organisation" pill, which is hidden at mount until the
|
|
122
|
+
* engine confirms a change. `hasOrgChanges` is false on a clean re-run (the
|
|
123
|
+
* root is skipped) and true when the root will deploy (changed, forced, or the
|
|
124
|
+
* stack is absent).
|
|
125
|
+
*/
|
|
126
|
+
onOrgChangesDetected?: (info: {
|
|
127
|
+
hasOrgChanges: boolean;
|
|
128
|
+
}) => void;
|
|
117
129
|
/**
|
|
118
130
|
* @emittedBy engine — fired once after account reconciliation when accounts
|
|
119
131
|
* are declared in ACCOUNTS but not yet present in AWS Organizations (the
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fjall/deploy-core",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.9.0",
|
|
4
4
|
"description": "Shared deployment engine for Fjall — used by CLI and webapp worker",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/src/index.js",
|
|
@@ -73,8 +73,8 @@
|
|
|
73
73
|
"@aws-sdk/client-s3": "^3.1038.0",
|
|
74
74
|
"@aws-sdk/client-sso-admin": "^3.1038.0",
|
|
75
75
|
"@aws-sdk/client-sts": "^3.1038.0",
|
|
76
|
-
"@fjall/generator": "^2.
|
|
77
|
-
"@fjall/util": "^2.
|
|
76
|
+
"@fjall/generator": "^2.9.0",
|
|
77
|
+
"@fjall/util": "^2.9.0",
|
|
78
78
|
"@smithy/node-http-handler": "^4.6.1",
|
|
79
79
|
"tsx": "^4.21.0",
|
|
80
80
|
"zod": "^4.4.3"
|
|
@@ -83,5 +83,5 @@
|
|
|
83
83
|
"@types/node": "^25.6.0",
|
|
84
84
|
"vitest": "^4.1.5"
|
|
85
85
|
},
|
|
86
|
-
"gitHead": "
|
|
86
|
+
"gitHead": "72c4668ef5ead59c0b981eca8037620875bcd1ba"
|
|
87
87
|
}
|