@fjall/deploy-core 2.4.8 → 2.6.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/index.d.ts +4 -0
- package/dist/src/index.js +1 -1
- package/dist/src/orchestration/accountsConfig.d.ts +34 -0
- package/dist/src/orchestration/accountsConfig.js +1 -0
- package/dist/src/orchestration/cascadeHelpers.d.ts +19 -5
- package/dist/src/orchestration/cascadeHelpers.js +1 -1
- package/dist/src/orchestration/contextHelpers.js +1 -1
- package/dist/src/orchestration/dockerBuildHelper.js +1 -1
- package/dist/src/orchestration/index.d.ts +4 -0
- package/dist/src/orchestration/index.js +1 -1
- package/dist/src/orchestration/organisationDeploy.js +5 -3
- package/dist/src/orchestration/organisationDestroy.js +2 -2
- package/dist/src/orchestration/organisationSetup.js +1 -1
- package/dist/src/orchestration/reconcileProviderAccounts.d.ts +29 -0
- package/dist/src/orchestration/reconcileProviderAccounts.js +1 -0
- package/dist/src/services/infrastructure/CdkService.d.ts +1 -1
- package/dist/src/services/infrastructure/CdkService.js +3 -3
- package/dist/src/services/infrastructure/cdkServiceHelpers.js +1 -1
- package/dist/src/services/infrastructure/constructMapEnrichment.d.ts +1 -1
- package/dist/src/services/infrastructure/constructMapEnrichment.js +1 -1
- package/dist/src/services/supporting/CdkContextBuilder.d.ts +2 -0
- package/dist/src/services/supporting/CdkContextBuilder.js +1 -1
- package/dist/src/steps/stepRegistry.js +1 -1
- package/dist/src/types/callbacks.d.ts +8 -0
- package/dist/src/types/deployment/DeploymentTypes.d.ts +16 -0
- package/dist/src/types/deploymentEventSchema.d.ts +5 -1
- package/dist/src/types/deploymentEventSchema.js +1 -1
- package/dist/src/types/stepDefinitions.d.ts +4 -0
- package/dist/src/types/stepDefinitions.js +1 -1
- package/package.json +5 -4
package/dist/.minified
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
118 files minified at 2026-05-30T05:11:04.003Z
|
package/dist/src/index.d.ts
CHANGED
|
@@ -43,6 +43,10 @@ export { type Result, success, failure, isSuccess, isFailure } from "@fjall/gene
|
|
|
43
43
|
export { deploy } from "./orchestration/index.js";
|
|
44
44
|
export { destroy } from "./orchestration/index.js";
|
|
45
45
|
export { partitionAccounts } from "./orchestration/index.js";
|
|
46
|
+
export { reconcileProviderAccounts, mergeReconciledProviderAccounts } from "./orchestration/index.js";
|
|
47
|
+
export type { ReconcileResult } from "./orchestration/index.js";
|
|
48
|
+
export { parseAccountsConfiguration, flattenAccountsToEnvironments, extractAllAccountNames, accountsConfigToOUTree, isStringArray, isAccountsConfig } from "./orchestration/index.js";
|
|
49
|
+
export type { AccountsConfig } from "./orchestration/index.js";
|
|
46
50
|
export { runOpenNextBuild } from "./orchestration/index.js";
|
|
47
51
|
export { runOrganisationSetup } from "./orchestration/index.js";
|
|
48
52
|
export type { OrgSetupPhase, OrgSetupCallbacks, OrgSetupConfig, OrgSetupResult, DockerProvider, DockerProgressCallback, DockerBuildParams, DockerBuildResult, ECRInitParams, ECRInitResult, TagImagesParams, TagImagesResult, TagByDigestParams, DomainDeployProvider, DomainConfig, DomainDeployResult, DeployServices } from "./orchestration/index.js";
|
package/dist/src/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{DeploymentEventSchema as t,DEPLOYMENT_EVENT_TYPES as o,DEPLOYMENT_EVENT_RESOURCE_CATEGORIES as a,CASCADE_PHASES as i,CASCADE_ACCOUNT_STATUSES as
|
|
1
|
+
import{DeploymentEventSchema as t,DEPLOYMENT_EVENT_TYPES as o,DEPLOYMENT_EVENT_RESOURCE_CATEGORIES as a,CASCADE_PHASES as i,CASCADE_ACCOUNT_STATUSES as s}from"./types/index.js";import{SimpleAwsProvider as c}from"./aws/index.js";import{ensureOrganisationExists as p,describeOrganisation as S,enablePolicyTypes as l,enableServiceAccess as A,enableRamSharing as u,activateTrustedAccess as T,enableIpamDelegatedAdmin as m,updateBackupGlobalSettings as O,listAccounts as P,findAccount as d,createAccount as _,ensureOrganisationalUnitsExist as C,placeAccountsInOUs as f,buildAccountToOUMap as R,activateCostAllocationTags as N,checkIdentityCentreStatus as g,extractErrorName as x,isOULeaf as D,registerSecurityDelegates as I,SECURITY_SERVICE_PRINCIPALS as L}from"./aws/index.js";import{STEP_IDS as k,STEP_NAMES as F,INFRASTRUCTURE_STEP_NAMES as y,INFRA_STEP_NAME as U}from"./types/index.js";import{ProgressReporter as h,APPLICATION_STACKS as Y,ORGANISATION_TYPES as b,APPLICATION_DEPLOY_ORDER as G,APPLICATION_DESTROY_ORDER as B,OPENNEXT_DEPLOY_ORDER as w,OPENNEXT_DESTROY_ORDER as H,PARALLEL_DEPLOY_GROUPS as K,PARALLEL_DESTROY_GROUPS as V,OPENNEXT_PARALLEL_GROUPS as X,PARALLEL_OPERATION_TYPES as j,isApplicationOperation as q,isOrganisationOperation as z,getParallelDeployGroups as J,getParallelDestroyGroups as Q,getApplicationDeployOrder as W,getApplicationDestroyOrder as Z,getApplicationStackName as $,getOrganisationStackName as ee,isApplicationStack as re,getApplicationStepName as te,getApplicationStepId as oe,toPascalCase as ae,isOpenNextPattern as ie,OPENNEXT_PATTERNS as se,deriveResourcesFromManifestStacks as ne,STACK_NOT_FOUND_PATTERN as ce,STACK_FAILED_STATE_PATTERN as Ee,CDK_NO_STACKS_MATCH as pe,INFRASTRUCTURE_FILENAME as Se,ApplicationError as le,wrapApplicationError as Ae,FjallStateFileSchema as ue,readStateFile as Te,writeStateFile as me,createEmptyState as Oe,deleteStateFile as Pe,updateTemplateHash as de,getStateFilePath as _e,stubCallerIdentity as Ce}from"./types/index.js";import{detectPattern as Re}from"./types/index.js";import{detectPayloadPattern as ge}from"./types/index.js";import{detectDatabase as De}from"./types/index.js";import{CloudFormationEventMonitor as Le}from"./aws/index.js";import{CdkService as ke,CdkArgumentBuilder as Fe,CdkProcessManager as ye,CdkEventMonitor as Ue,startStackMonitoring as Me,DEFAULT_DEPLOY_TIMEOUT_MS as he,isCdkError as Ye,formatInfrastructureError as be,getStructuralHint as Ge,getSourceContext as Be,hasCdkDifferences as we,parseDiffOutput as He,CloudFormationService as Ke,CloudFormationError as Ve,EcsService as Xe,EcsError as je,EcsServiceResolver as qe,TemplateHashService as ze,TemplateHashError as Je,CdkContextBuilder as Qe,emitProgress as We,PROGRESS_MESSAGES as Ze,parseBuildPhase as $e,buildStepContextBuildConfig as er,convertCloudFormationOutputsToRecord as rr,ApplicationStackService as tr}from"./services/index.js";import{CdkError as ar}from"./types/errors/index.js";import{BaseServiceError as sr,ValidationError as nr,AuthError as cr,AwsError as Er,DeploymentError as pr,NetworkError as Sr,FileSystemError as lr,ConfigError as Ar,toServiceError as ur}from"./types/errors/index.js";import{filterDangerousEnvVars as mr,maskSensitiveOutput as Or,parseShellArgs as Pr,sleep as dr}from"@fjall/util";import{hasDockerfile as Cr}from"./util/dockerfileDetection.js";import{createSequencedCallbacks as Rr}from"./util/sequencedCallbacks.js";import{fileExists as gr}from"@fjall/util/fsHelpers";import{success as Dr,failure as Ir,isSuccess as Lr,isFailure as vr}from"@fjall/generator";import{deploy as Fr}from"./orchestration/index.js";import{destroy as Ur}from"./orchestration/index.js";import{partitionAccounts as hr}from"./orchestration/index.js";import{reconcileProviderAccounts as br,mergeReconciledProviderAccounts as Gr}from"./orchestration/index.js";import{parseAccountsConfiguration as wr,flattenAccountsToEnvironments as Hr,extractAllAccountNames as Kr,accountsConfigToOUTree as Vr,isStringArray as Xr,isAccountsConfig as jr}from"./orchestration/index.js";import{runOpenNextBuild as zr}from"./orchestration/index.js";import{runOrganisationSetup as Qr}from"./orchestration/index.js";import{FrameworkRegistry as Zr}from"./orchestration/index.js";import{openNextBuilder as et,dockerBuilder as rt}from"./orchestration/index.js";import{StepRegistry as ot,getDestroyStepId as at}from"./steps/index.js";export{G as APPLICATION_DEPLOY_ORDER,B as APPLICATION_DESTROY_ORDER,Y as APPLICATION_STACKS,le as ApplicationError,tr as ApplicationStackService,cr as AuthError,Er as AwsError,sr as BaseServiceError,s as CASCADE_ACCOUNT_STATUSES,i as CASCADE_PHASES,pe as CDK_NO_STACKS_MATCH,Fe as CdkArgumentBuilder,Qe as CdkContextBuilder,ar as CdkError,Ue as CdkEventMonitor,ye as CdkProcessManager,ke as CdkService,Ve as CloudFormationError,Le as CloudFormationEventMonitor,Ke as CloudFormationService,Ar as ConfigError,he as DEFAULT_DEPLOY_TIMEOUT_MS,a as DEPLOYMENT_EVENT_RESOURCE_CATEGORIES,o as DEPLOYMENT_EVENT_TYPES,pr as DeploymentError,t as DeploymentEventSchema,je as EcsError,Xe as EcsService,qe as EcsServiceResolver,lr as FileSystemError,ue as FjallStateFileSchema,Zr as FrameworkRegistry,Se as INFRASTRUCTURE_FILENAME,y as INFRASTRUCTURE_STEP_NAMES,U as INFRA_STEP_NAME,Sr as NetworkError,w as OPENNEXT_DEPLOY_ORDER,H as OPENNEXT_DESTROY_ORDER,X as OPENNEXT_PARALLEL_GROUPS,se as OPENNEXT_PATTERNS,b as ORGANISATION_TYPES,K as PARALLEL_DEPLOY_GROUPS,V as PARALLEL_DESTROY_GROUPS,j as PARALLEL_OPERATION_TYPES,Ze as PROGRESS_MESSAGES,h as ProgressReporter,L as SECURITY_SERVICE_PRINCIPALS,Ee as STACK_FAILED_STATE_PATTERN,ce as STACK_NOT_FOUND_PATTERN,k as STEP_IDS,F as STEP_NAMES,c as SimpleAwsProvider,ot as StepRegistry,Je as TemplateHashError,ze as TemplateHashService,nr as ValidationError,Vr as accountsConfigToOUTree,N as activateCostAllocationTags,T as activateTrustedAccess,R as buildAccountToOUMap,er as buildStepContextBuildConfig,g as checkIdentityCentreStatus,rr as convertCloudFormationOutputsToRecord,_ as createAccount,Oe as createEmptyState,Rr as createSequencedCallbacks,Pe as deleteStateFile,Fr as deploy,ne as deriveResourcesFromManifestStacks,S as describeOrganisation,Ur as destroy,De as detectDatabase,Re as detectPattern,ge as detectPayloadPattern,rt as dockerBuilder,We as emitProgress,m as enableIpamDelegatedAdmin,l as enablePolicyTypes,u as enableRamSharing,A as enableServiceAccess,p as ensureOrganisationExists,C as ensureOrganisationalUnitsExist,Kr as extractAllAccountNames,x as extractErrorName,Ir as failure,gr as fileExists,mr as filterDangerousEnvVars,d as findAccount,Hr as flattenAccountsToEnvironments,be as formatInfrastructureError,W as getApplicationDeployOrder,Z as getApplicationDestroyOrder,$ as getApplicationStackName,oe as getApplicationStepId,te as getApplicationStepName,at as getDestroyStepId,ee as getOrganisationStackName,J as getParallelDeployGroups,Q as getParallelDestroyGroups,Be as getSourceContext,_e as getStateFilePath,Ge as getStructuralHint,we as hasCdkDifferences,Cr as hasDockerfile,jr as isAccountsConfig,q as isApplicationOperation,re as isApplicationStack,Ye as isCdkError,vr as isFailure,D as isOULeaf,ie as isOpenNextPattern,z as isOrganisationOperation,Xr as isStringArray,Lr as isSuccess,P as listAccounts,Or as maskSensitiveOutput,Gr as mergeReconciledProviderAccounts,et as openNextBuilder,wr as parseAccountsConfiguration,$e as parseBuildPhase,He as parseDiffOutput,Pr as parseShellArgs,hr as partitionAccounts,f as placeAccountsInOUs,Te as readStateFile,br as reconcileProviderAccounts,I as registerSecurityDelegates,zr as runOpenNextBuild,Qr as runOrganisationSetup,dr as sleep,Me as startStackMonitoring,Ce as stubCallerIdentity,Dr as success,ae as toPascalCase,ur as toServiceError,O as updateBackupGlobalSettings,de as updateTemplateHash,Ae as wrapApplicationError,me as writeStateFile};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { type Result } from "@fjall/generator";
|
|
2
|
+
import { ConfigError } from "../types/errors/ServiceError.js";
|
|
3
|
+
import type { OUTree } from "../aws/organisations/types.js";
|
|
4
|
+
export type AccountsConfig = {
|
|
5
|
+
readonly [key: string]: string | readonly string[] | AccountsConfig;
|
|
6
|
+
};
|
|
7
|
+
export declare function isStringArray(value: unknown): value is string[];
|
|
8
|
+
export declare function isAccountsConfig(value: unknown): value is AccountsConfig;
|
|
9
|
+
/**
|
|
10
|
+
* Recursively extract account→environment pairs using leaf keys.
|
|
11
|
+
* Parent keys (e.g. "workloads") are skipped — only leaf keys become environments.
|
|
12
|
+
*/
|
|
13
|
+
export declare function flattenAccountsToEnvironments(config: AccountsConfig): Array<{
|
|
14
|
+
accountName: string;
|
|
15
|
+
environment: string;
|
|
16
|
+
}>;
|
|
17
|
+
/**
|
|
18
|
+
* Extract all account names from any depth of nesting.
|
|
19
|
+
*/
|
|
20
|
+
export declare function extractAllAccountNames(config: AccountsConfig): string[];
|
|
21
|
+
/**
|
|
22
|
+
* Convert AccountsConfig to deploy-core's OUTree format for nested OU creation.
|
|
23
|
+
*/
|
|
24
|
+
export declare function accountsConfigToOUTree(config: AccountsConfig): OUTree;
|
|
25
|
+
/**
|
|
26
|
+
* Parse the ACCOUNTS configuration from `<baseDir>/fjall/organisation/infrastructure.ts`.
|
|
27
|
+
* Returns null (not an error) if the file does not exist.
|
|
28
|
+
*
|
|
29
|
+
* `baseDir` is the repository root — `process.cwd()` for the CLI (run from the
|
|
30
|
+
* user's repo) and the prepared-clone `workingDirectory` for the worker. There
|
|
31
|
+
* is intentionally no `process.cwd()` default: in the worker `process.cwd()` is
|
|
32
|
+
* the worker dir, not the clone, so callers must pass the resolved base.
|
|
33
|
+
*/
|
|
34
|
+
export declare function parseAccountsConfiguration(baseDir: string): Promise<Result<AccountsConfig | null, ConfigError>>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{success as u,failure as a}from"@fjall/generator";import{maskSensitiveOutput as A,getErrorMessage as C}from"@fjall/util";import{ConfigError as f}from"../types/errors/ServiceError.js";const c="infrastructure.ts";function g(n){return Array.isArray(n)&&n.every(t=>typeof t=="string")}function p(n){return typeof n!="object"||n===null?!1:Object.entries(n).every(([t,r])=>typeof t=="string"&&(typeof r=="string"||g(r)||p(r)))}function O(n){const t=[];for(const[r,e]of Object.entries(n))if(typeof e=="string")t.push({accountName:e,environment:r});else if(Array.isArray(e))for(const o of e)t.push({accountName:o,environment:r});else typeof e=="object"&&e!==null&&t.push(...O(e));return t}function d(n){const t=[];for(const r of Object.values(n))typeof r=="string"?t.push(r):Array.isArray(r)?t.push(...r):typeof r=="object"&&r!==null&&t.push(...d(r));return t}function T(n){const t={};for(const[r,e]of Object.entries(n))typeof e=="string"?t[r]=[e]:Array.isArray(e)?t[r]=[...e]:typeof e=="object"&&e!==null&&(t[r]=T(e));return t}async function S(n){try{const t=await import("path"),r=await import("fs"),{pathToFileURL:e}=await import("url"),o=t.join(n,"fjall","organisation",c);if(!r.existsSync(o))return u(null);const{tsImport:l}=await import("tsx/esm/api"),y=e(o).href,s=await l(o,y),m=s.default,i=s.ACCOUNTS||m?.ACCOUNTS;return i?p(i)?u(i):a(new f("ACCOUNTS configuration has invalid structure","ACCOUNTS",o,{providedValue:i})):a(new f(`ACCOUNTS constant not found in organisation/${c}`,"ACCOUNTS",o,{moduleKeys:Object.keys(s),hasDefault:s.default!==void 0}))}catch(t){const r=A(C(t));return a(new f(`Failed to import ACCOUNTS from organisation/${c}: ${r}`,"ACCOUNTS",void 0,{originalError:t}))}}export{T as accountsConfigToOUTree,d as extractAllAccountNames,O as flattenAccountsToEnvironments,p as isAccountsConfig,g as isStringArray,S as parseAccountsConfiguration};
|
|
@@ -1,10 +1,19 @@
|
|
|
1
1
|
import { type Result } from "@fjall/generator";
|
|
2
2
|
import type { DeployParams } from "../types/params.js";
|
|
3
|
+
import type { OrgConfig } from "../types/orgConfig.js";
|
|
3
4
|
import type { DeployCallbacks } from "../types/callbacks.js";
|
|
4
5
|
import type { OrganisationOperation } from "../types/operations.js";
|
|
5
6
|
import type { DeployServices } from "./serviceFactory.js";
|
|
6
7
|
import type { DomainDeployProvider } from "./domainInterface.js";
|
|
7
8
|
import type { ProviderAccount } from "@fjall/util/config";
|
|
9
|
+
/**
|
|
10
|
+
* Max member accounts deployed/destroyed in parallel during a cascade. Each
|
|
11
|
+
* task spawns a `cdk` process, assumes a role, synthesises and deploys — so the
|
|
12
|
+
* ceiling is the worker host's process/memory budget, NOT an AWS API limit
|
|
13
|
+
* (the accounts are distinct, so CloudFormation/STS quotas don't contend).
|
|
14
|
+
* Shared by deploy and destroy so they fan out identically.
|
|
15
|
+
*/
|
|
16
|
+
export declare const CASCADE_MAX_CONCURRENCY = 4;
|
|
8
17
|
/**
|
|
9
18
|
* Partition provider accounts into platform and member accounts.
|
|
10
19
|
* Used by both deploy and destroy orchestration.
|
|
@@ -21,11 +30,16 @@ export { buildCascadeRoleArn } from "./contextHelpers.js";
|
|
|
21
30
|
export interface CascadeAccountResult {
|
|
22
31
|
outputs?: Record<string, string>;
|
|
23
32
|
}
|
|
24
|
-
export
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
33
|
+
export interface DeployCascadeAccountOptions {
|
|
34
|
+
ipamPoolId?: string;
|
|
35
|
+
/**
|
|
36
|
+
* Effective org config carrying the reconciled provider accounts. The cascade
|
|
37
|
+
* context must serialise THIS (not the possibly-stale `params.orgConfig`) so the
|
|
38
|
+
* synthesised app's `getConfig()` can resolve every account by name/id.
|
|
39
|
+
*/
|
|
40
|
+
orgConfig?: OrgConfig;
|
|
41
|
+
}
|
|
42
|
+
export declare function deployCascadeAccount(params: DeployParams, services: DeployServices, operation: OrganisationOperation, account: ProviderAccount, deployType: "platform" | "account", callbacks: DeployCallbacks, options?: DeployCascadeAccountOptions): Promise<Result<CascadeAccountResult>>;
|
|
29
43
|
/**
|
|
30
44
|
* Read Platform stack outputs to extract IPAM pool IDs for member accounts.
|
|
31
45
|
* Output keys follow the pattern `IpamPoolId{12-digit-accountId}{regionSuffix}`.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{success as
|
|
1
|
+
import{join as I}from"path";import{success as k,failure as C}from"@fjall/generator";import{logger as O}from"@fjall/util/logger";import{maskSensitiveOutput as f}from"@fjall/util";import{ORGANISATION_TYPES as p,getOrganisationStackName as x}from"../types/operations.js";import{CdkContextBuilder as L}from"../services/supporting/CdkContextBuilder.js";import{stubCallerIdentity as U}from"../types/deployment/index.js";import{CloudFormationService as N}from"../services/infrastructure/CloudFormationService.js";import{buildParamsContext as j,collectStackOutputs as B,assumeCascadeRole as E,forwardOutput as w}from"./contextHelpers.js";import{STRUCTURAL_ENVIRONMENTS as S}from"@fjall/util";const W=4;function Z(r){const s=r.find(e=>e.environment===S.PLATFORM),d=r.filter(e=>e.environment!==S.ROOT&&e.environment!==S.PLATFORM);return{platformAccount:s,memberAccounts:d}}import{buildCascadeRoleArn as re}from"./contextHelpers.js";async function b(r,s,d,e,i,n,c){const t=`${e.name}-${e.id}`,o=e.region??s.awsProvider.getRegion(),a=c?.orgConfig??r.orgConfig,m=c?.ipamPoolId;n.onCascadeAccountStart?.(t,e.id,o,i);const u=await E(s.awsProvider,e.id,o,`fjall-cascade-${e.name}`);if(!u.success)return n.onCascadeAccountComplete?.(t,!1,f(u.error.message),o),C(new Error(`Failed to assume role for ${e.name}: ${f(u.error.message)}`));const{provider:l,credentials:g}=u.data,h=I(r.workingDirectory,"fjall",i==="platform"?p.PLATFORM:p.ACCOUNT),T=o.replace(/-/g,""),F=I(h,`cdk.out.${e.id}.${T}`),P=L.buildDeploymentContext({deployType:i,target:d.target,path:h,assemblyDir:F,environment:e.environment,region:o,accountName:e.name,callerIdentity:U(e.id),ipamPoolId:m,...j({orgConfig:a,identity:r.identity,skipOidc:r.options?.skipOidc})},{verbose:r.options?.verbose},a);n.onCascadeAccountPhaseChange?.(t,"synth",o);const A=await s.cdkService.runCdkSynth(P,w(n),g);if(!A.success)return n.onCascadeAccountComplete?.(t,!1,f(`Synth failed: ${A.error}`),o),C(new Error(`Synth failed for ${e.name}: ${f(A.error)}`));n.onCascadeAccountPhaseChange?.(t,"bootstrap",o);const R=await s.cdkService.runCdkBootstrap(P,w(n),g);if(!R.success)return n.onCascadeAccountComplete?.(t,!1,f(`Bootstrap failed: ${R.error}`),o),C(new Error(`Bootstrap failed for ${e.name}: ${f(R.error)}`));n.onCascadeAccountPhaseChange?.(t,"deploy",o);const $=x(i==="platform"?p.PLATFORM:p.ACCOUNT),y=await s.cdkService.runCdkDeploy(P,$,w(n),M=>n.onCascadeAccountResourceProgress?.(t,M,o),l,g);if(!y.success)return n.onCascadeAccountComplete?.(t,!1,f(y.error),o),C(new Error(f(y.error)));const D=await new N(l).getStackOutputs($);D.success||O.debug("cascadeHelpers","Failed to read cascade account stack outputs (non-critical)",{stackName:$,account:e.name});const v=B(D);return n.onCascadeAccountComplete?.(t,!0,void 0,o,v),k({outputs:v})}async function ee(r,s,d){const e=new Map,i=r.awsProvider.getRegion(),n=await E(r.awsProvider,s.id,i,`fjall-ipam-read-${s.name}`);if(!n.success)return O.debug("organisationDeploy",`Cannot read Platform outputs: ${n.error.message}`),e;const c=new N(n.data.provider),t=x(p.PLATFORM),o=await c.getStackOutputs(t);if(!o.success)return O.debug("organisationDeploy",`Failed to read Platform stack outputs: ${o.error.message}`),e;const a=/^IpamPoolId(\d{12})(\w+)$/;for(const m of o.data){const u=m.OutputKey?.match(a);if(u&&m.OutputValue){const l=`${u[1]}-${u[2]}`;e.set(l,m.OutputValue)}}return e.size>0&&d.onLog?.(`Read ${e.size} IPAM pool ID(s) from Platform stack`,"info"),e}async function oe(r,s){const d=r.getDomains();if(d.length===0)return{domainsDeployed:0,errors:[]};s.onCascadePhaseStart?.("domains");const e=d.filter(t=>t.type==="apex"),i=d.filter(t=>t.type==="delegated");let n=0;const c=[];for(const t of e){const o=await r.deployDomain(t.name,s);o.success?n++:c.push(`${t.name}: ${o.error.message}`)}if(i.length>0){const t=await Promise.allSettled(i.map(o=>r.deployDomain(o.name,s)));for(let o=0;o<t.length;o++){const a=t[o],m=i[o];if(!(!a||!m))if(a.status==="fulfilled")a.value.success?n++:c.push(`${m.name}: ${a.value.error.message}`);else{const u=a.reason instanceof Error?a.reason.message:String(a.reason);c.push(`${m.name}: ${u}`)}}}return s.onCascadePhaseComplete?.("domains"),{domainsDeployed:n,errors:c}}export{W as CASCADE_MAX_CONCURRENCY,re as buildCascadeRoleArn,b as deployCascadeAccount,oe as deployDomains,Z as partitionAccounts,ee as readPlatformIpamPoolIds};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{success as
|
|
1
|
+
import{success as f,failure as u}from"@fjall/generator";import{getErrorMessage as y,maskSensitiveOutput as d,sleep as A}from"@fjall/util";import{logger as O}from"@fjall/util/logger";import{SimpleAwsProvider as R}from"../aws/SimpleAwsProvider.js";function M(e){return{...e.orgConfig!==void 0?{orgConfig:JSON.stringify(e.orgConfig)}:{},...e.identity!==void 0?{fjallOrgId:e.identity.fjallOrgId}:{},...e.skipOidc?{fjallOidcConfigured:!0}:{}}}function S(e){return r=>e.onOutput?.(r)}function k(e){return r=>e.onResourceProgress?.(r)}function T(e){if(!e.success||e.data.length===0)return;const r={};for(const t of e.data)t.OutputKey&&t.OutputValue!==void 0&&(r[t.OutputKey]=t.OutputValue);return Object.keys(r).length>0?r:void 0}const g="OrganizationAccountAccessRole",p=5,C=5e3,w=3e4;function $(e){return`arn:aws:iam::${e}:role/${g}`}async function D(e,r,t,s){if(!e.assumeRole)return u(new Error("AwsProvider does not support assumeRole"));const o=$(r),i=e.assumeRole.bind(e);let n;for(let c=0;c<=p;c++)try{n=await i(o,s);break}catch(a){const l=a instanceof Error?a.name:void 0;if(l==="AccessDenied"||l==="AccessDeniedException")return u(new Error(`Access denied assuming ${g} in account ${r}. The role may not exist or may not trust the management account.`));if(c<p){const m=Math.min(C*2**c,w);O.debug("assumeCascadeRole",`Attempt ${c+1} failed for account ${r}, retrying in ${Math.round(m/1e3)}s`,{error:d(y(a))}),await A(m);continue}return u(new Error(`Failed to assume role in account ${r} after ${p+1} attempts: ${d(y(a))}`))}if(!n)return u(new Error(`Failed to assume role in account ${r}`));const E=new R({accessKeyId:n.accessKeyId,secretAccessKey:n.secretAccessKey,sessionToken:n.sessionToken,region:t,accountId:r});return f({provider:E,credentials:{accessKeyId:n.accessKeyId,secretAccessKey:n.secretAccessKey,sessionToken:n.sessionToken}})}async function B(e,r,t,s){const o=await e.cdkService.runCdkSynth(r,i=>t.onCdkOutput?.(i,"synth"));if(!o.success){const i=new Error(d(`${s}: ${o.error}`));return t.onError?.(i),u(i)}return f(void 0)}async function P(e,r,t){t.onCDKBootstrap?.("bootstrapping");const s=await e.cdkService.runCdkBootstrap(r,S(t));if(!s.success){t.onCDKBootstrap?.("failed");const o=new Error(d(`Bootstrap failed: ${s.error}`));return t.onError?.(o),u(o)}return t.onCDKBootstrap?.("complete"),f(void 0)}export{D as assumeCascadeRole,P as bootstrapOrFail,$ as buildCascadeRoleArn,M as buildParamsContext,T as collectStackOutputs,S as forwardOutput,k as forwardResourceProgress,B as synthOrFail};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{isAbsolute as T,join as
|
|
1
|
+
import{isAbsolute as T,join as b,dirname as k,resolve as y}from"node:path";import{success as D,failure as S}from"@fjall/generator";import{deriveContentHashTag as v,maskSensitiveOutput as C}from"@fjall/util";import{logger as E}from"@fjall/util/logger";import{parseDockerServicesFromManifest as A}from"@fjall/util/manifest";import{STEP_IDS as N}from"../types/stepDefinitions.js";const O="dockerBuildHelper",R="Building and pushing Docker image";function w(t,n){const r=t.docker.path,e=t.docker.context,s=T(r)?r:b(n,r);return{buildContext:e?T(e)?y(e):y(n,e):y(k(s)),dockerfilePath:s,target:t.docker.target}}function I(t){return`${t.buildContext}|${t.dockerfilePath}|${t.target??""}`}function x(t){return t.startsWith("cn-")?"amazonaws.com.cn":"amazonaws.com"}function L(t,n,r){return`${t}.dkr.ecr.${n}.${x(n)}/${r.toLowerCase()}`}function B(t,n){const r=n.name.toLowerCase(),e=n.docker.target,s=e?`-${e.toLowerCase()}`:"";return[`${t}:${r}${s}-latest`]}function _(t,n,r){const e=new Map;for(const s of t){const a=w(s,n),c=I(a),d=B(r,s),i=e.get(c);i?(i.members.push(s),i.latestTags.push(...d)):e.set(c,{identity:a,members:[s],latestTags:[...d]})}return Array.from(e.values())}async function K(t,n,r,e,s){const a=await t.buildAndPush({appName:n.appName,appPath:n.path,region:r,accountId:e},(c,d,i,p)=>{s.onDockerProgress?.(C(c),d,i,p)});return a.success?D({imageUri:a.data.imageUri,imageTag:a.data.imageTag}):S(a.error)}async function H(t,n,r,e,s,a,c){const d=t.members[0],i={appName:r.appName,appPath:t.identity.buildContext,region:e,accountId:s,buildContext:t.identity.buildContext,dockerfilePath:t.identity.dockerfilePath,imageTags:t.latestTags,serviceName:d.name,...t.identity.target!==void 0&&{target:t.identity.target},...d.docker.buildArgs!==void 0&&{buildArgs:d.docker.buildArgs}},p=await n.buildAndPush(i,(u,h,m,g)=>{c.onDockerProgress?.(C(u),h,m,g)});if(!p.success)return S(p.error);const f=p.data.imageDigest;if(typeof f!="string"||!f.startsWith("sha256:"))return S(new Error(`Buildx push succeeded but returned an invalid digest: ${C(String(f))}`));const l={},P=[];for(const u of t.members){const h=u.docker.target,m=v(f,u.name,h);if(!m.success)return S(m.error);const g=m.data,$=h?`${u.name}-${h}`:u.name;l[$]=g,P.push(`${a}:${g}`)}const o=await n.tagByDigest({sourceImage:a,digest:f,tags:P});return o.success?D({contentHashTagsByService:l}):S(o.error)}async function W(t,n,r,e){const s=t.dockerProvider;if(!s)return D({});const a=n.awsProvider.getAccountId();if(!a)return e.onLog?.("Skipping Docker build \u2014 account ID not available","warn"),D({});const c=n.awsProvider.getRegion(),d=b(r.path,"cdk.out"),i=A(d),p=L(a,c,r.appName);E.debug(O,"runDockerBuild starting",{appName:r.appName,appPath:r.path,accountId:a,region:c,cdkOutPath:d,manifestServiceCount:i.length,manifestServices:i.map(o=>({name:o.name,path:o.docker.path,context:o.docker.context,target:o.docker.target})),stepId:N.DOCKER_OPERATIONS,stepName:R}),e.onStepStart?.(N.DOCKER_OPERATIONS,R),e.onLog?.("Initialising ECR repository\u2026","info");const f=await s.initialiseECR({appName:r.appName,region:c,accountId:a});if(f.success||e.onLog?.(`ECR initialisation failed: ${C(f.error.message)}`,"warn"),i.length===0){e.onLog?.("Building and pushing Docker image\u2026","info");const o=await K(s,r,c,a,e);if(!o.success){E.debug(O,"Docker buildAndPush (legacy) failed",{appName:r.appName,error:C(o.error.message)}),e.onStepComplete?.(N.DOCKER_OPERATIONS,R,"error");const u=new Error(C(o.error.message));return e.onError?.(u),S(u)}return e.onStepComplete?.(N.DOCKER_OPERATIONS,R,"completed"),e.onLog?.(`Docker image pushed: ${o.data.imageUri}`,"info"),D({})}const l=_(i,r.path,p);e.onLog?.(`Building and pushing ${i.length} service image(s) in ${l.length} group(s)\u2026`,"info");const P={};for(let o=0;o<l.length;o++){const u=l[o],h=u.members.map(g=>g.name).join(", ");e.onLog?.(`Building group ${o+1}/${l.length} (${h})\u2026`,"info");const m=await H(u,s,r,c,a,p,e);if(!m.success){E.debug(O,"Docker buildAndPush failed",{appName:r.appName,leader:u.members[0]?.name,members:h,error:C(m.error.message)}),e.onStepComplete?.(N.DOCKER_OPERATIONS,R,"error");const g=new Error(C(m.error.message));return e.onError?.(g),S(g)}for(const[g,$]of Object.entries(m.data.contentHashTagsByService))P[g]=$}return E.debug(O,"Docker buildAndPush succeeded",{appName:r.appName,services:i.map(o=>o.name),groupCount:l.length,contentHashTags:P}),e.onStepComplete?.(N.DOCKER_OPERATIONS,R,"completed"),e.onLog?.(`Docker images pushed for ${i.length} service(s)`,"info"),D(P)}export{R as DOCKER_BUILD_STEP_NAME,W as runDockerBuild};
|
|
@@ -5,6 +5,10 @@ export { destroyOrganisation } from "./organisationDestroy.js";
|
|
|
5
5
|
export { cleanupFailedStack, isCleanableState, SAFE_CLEANUP_STATES } from "./stackCleanup.js";
|
|
6
6
|
export type { CascadeDestroyAccountResult } from "./cascadeDestroyHelpers.js";
|
|
7
7
|
export { partitionAccounts } from "./cascadeHelpers.js";
|
|
8
|
+
export { reconcileProviderAccounts, mergeReconciledProviderAccounts } from "./reconcileProviderAccounts.js";
|
|
9
|
+
export type { ReconcileResult } from "./reconcileProviderAccounts.js";
|
|
10
|
+
export { parseAccountsConfiguration, flattenAccountsToEnvironments, extractAllAccountNames, accountsConfigToOUTree, isStringArray, isAccountsConfig } from "./accountsConfig.js";
|
|
11
|
+
export type { AccountsConfig } from "./accountsConfig.js";
|
|
8
12
|
export type { DockerProvider, DockerProgressCallback, DockerServiceConfig, DockerBuildParams, DockerBuildResult, ECRInitParams, ECRInitResult, TagImagesParams, TagImagesResult, TagByDigestParams } from "./dockerInterface.js";
|
|
9
13
|
export type { DomainDeployProvider, DomainConfig, DomainDeployResult } from "./domainInterface.js";
|
|
10
14
|
export type { DeployServices } from "./serviceFactory.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{deploy as
|
|
1
|
+
import{deploy as e}from"./deploy.js";import{destroy as n}from"./destroy.js";import{deployOrganisation as i}from"./organisationDeploy.js";import{destroyOrganisation as p}from"./organisationDestroy.js";import{cleanupFailedStack as f,isCleanableState as m,SAFE_CLEANUP_STATES as u}from"./stackCleanup.js";import{partitionAccounts as l}from"./cascadeHelpers.js";import{reconcileProviderAccounts as d,mergeReconciledProviderAccounts as g}from"./reconcileProviderAccounts.js";import{parseAccountsConfiguration as y,flattenAccountsToEnvironments as C,extractAllAccountNames as O,accountsConfigToOUTree as T,isStringArray as E,isAccountsConfig as v}from"./accountsConfig.js";import{runOpenNextBuild as P}from"./openNextBuild.js";import{runOrganisationSetup as U}from"./organisationSetup.js";export*from"./builders/index.js";export{u as SAFE_CLEANUP_STATES,T as accountsConfigToOUTree,f as cleanupFailedStack,e as deploy,i as deployOrganisation,n as destroy,p as destroyOrganisation,O as extractAllAccountNames,C as flattenAccountsToEnvironments,v as isAccountsConfig,m as isCleanableState,E as isStringArray,g as mergeReconciledProviderAccounts,y as parseAccountsConfiguration,l as partitionAccounts,d as reconcileProviderAccounts,P as runOpenNextBuild,U as runOrganisationSetup};
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
-
import{success as
|
|
2
|
-
`);t.onLog?.(
|
|
3
|
-
${
|
|
1
|
+
import{success as W,failure as O}from"@fjall/generator";import{OrganizationsClient as lt}from"@aws-sdk/client-organizations";import{ORGANISATION_TYPES as $,getOrganisationStackName as J}from"../types/operations.js";import{CdkContextBuilder as mt}from"../services/supporting/CdkContextBuilder.js";import{stubCallerIdentity as pt}from"../types/deployment/index.js";import{ensureOrganisationExists as gt}from"../aws/organisations/organisation.js";import{buildParamsContext as ft,collectStackOutputs as Q,synthOrFail as Z,bootstrapOrFail as tt,forwardOutput as et,forwardResourceProgress as ot}from"./contextHelpers.js";import{partitionAccounts as nt,deployCascadeAccount as rt,readPlatformIpamPoolIds as Ct,deployDomains as St,CASCADE_MAX_CONCURRENCY as Et}from"./cascadeHelpers.js";import{reconcileProviderAccounts as Ot,mergeReconciledProviderAccounts as At}from"./reconcileProviderAccounts.js";import{maskSensitiveOutput as l,STRUCTURAL_ENVIRONMENTS as Rt,mapSettledWithConcurrency as Pt}from"@fjall/util";import{INFRA_STEP_NAME as G,STEP_IDS as I,STEP_NAMES as j}from"../types/stepDefinitions.js";async function Yt(o,e,r){const g=Date.now();switch(r.type){case $.ORGANISATION:return It(o,e,r,g);case $.PLATFORM:return ct(o,e,r,"platform",g);case $.ACCOUNT:return ct(o,e,r,"account",g);default:{const t=r.type;return O(new Error(`Unsupported organisation type: ${String(t)}`))}}}function at(o,e,r,g,t,a){return mt.buildDeploymentContext({deployType:g,target:r.target,path:r.path,region:e.awsProvider.getRegion(),accountName:a,callerIdentity:pt(e.awsProvider.getAccountId()),orgId:t.orgId,rootId:t.rootId,managementAccountId:t.managementAccountId,...ft({orgConfig:o.orgConfig,identity:o.identity,skipOidc:o.options?.skipOidc})},{verbose:o.options?.verbose,infraOnly:o.options?.infraOnly},o.orgConfig)}async function st(o){const e=o.awsProvider.getClient(lt),r=await gt(e);return r.success?W({orgId:r.data.orgId,rootId:r.data.rootId,managementAccountId:r.data.managementAccountId}):O(r.error)}const n={CONNECT:{id:I.CONNECT,name:G.CONNECT},PREPARE:{id:I.PREPARE_ENVIRONMENT,name:G.PREPARE},DEPLOY:{id:I.DEPLOY,name:G.DEPLOY},MONITORING:{id:I.MONITORING,name:G.MONITORING},ORG_DEPLOY:{id:I.ORG_DEPLOY,name:j.ORG_DEPLOY},CASCADE_PLATFORM:{id:I.CASCADE_PLATFORM,name:j.CASCADE_PLATFORM},CASCADE_ACCOUNTS:{id:I.CASCADE_ACCOUNTS,name:j.CASCADE_ACCOUNTS}},C=4;async function ct(o,e,r,g,t){const{callbacks:a}=o;a.onStepComplete?.(n.CONNECT.id,n.CONNECT.name,"completed",0,C),a.onStepStart?.(n.PREPARE.id,n.PREPARE.name,1,C);const c=await st(e);if(!c.success){a.onStepComplete?.(n.PREPARE.id,n.PREPARE.name,"error",1,C);const A=new Error(l(c.error.message));return a.onError?.(A),O(A)}const b=at(o,e,r,g,c.data,g==="account"?r.target:void 0);a.onLog?.(`Synthesising ${g} infrastructure\u2026`,"info");const _=await Z(e,b,a,"CDK synthesis failed");if(!_.success)return a.onStepComplete?.(n.PREPARE.id,n.PREPARE.name,"error",1,C),_;const T=await tt(e,b,a);if(!T.success)return a.onStepComplete?.(n.PREPARE.id,n.PREPARE.name,"error",1,C),T;a.onStepComplete?.(n.PREPARE.id,n.PREPARE.name,"completed",1,C);const N=J(r.type);a.onStepStart?.(n.DEPLOY.id,n.DEPLOY.name,2,C);const y=await e.cdkService.runCdkDeploy(b,N,et(a),ot(a),e.awsProvider);if(!y.success){a.onStepComplete?.(n.DEPLOY.id,n.DEPLOY.name,"error",2,C);const A=new Error(l(y.error));return a.onError?.(A),O(A)}a.onStepComplete?.(n.DEPLOY.id,n.DEPLOY.name,"completed",2,C);const w=await e.cfnService.getStackOutputs(N);w.success||a.onLog?.("Failed to read stack outputs (non-critical)","debug");const F=Q(w);return a.onStepStart?.(n.MONITORING.id,n.MONITORING.name,3,C),a.onStepComplete?.(n.MONITORING.id,n.MONITORING.name,"completed",3,C),W({target:r.target,deploymentType:"organisation",outputs:F,durationMs:Date.now()-t})}async function It(o,e,r,g){const{callbacks:t,options:a}=o;let c=o.orgConfig?.providerAccounts??[];if(c.length===0||c.every(s=>s.environment===Rt.ROOT)){const s=await Ot(e,o.workingDirectory);if(s.success){const{providerAccounts:f,missingAccountNames:S}=s.data;f.length>0&&(c=At(o.orgConfig,f).providerAccounts,t.onLog?.(`Reconciled ${f.length} account(s) from AWS Organizations`,"info")),S.length>0&&(t.onCascadeMissingAccounts?.(S),t.onProgress?.({type:"warning",message:l(`Accounts declared in ACCOUNTS but not yet in AWS Organizations (cascade will skip): ${S.join(", ")}`)}))}else t.onProgress?.({type:"warning",message:l(`Could not reconcile accounts from AWS Organizations \u2014 cascade may skip accounts: ${s.error.message}`)})}const _=o.orgConfig?.primaryRegion??e.awsProvider.getRegion();c=c.map(s=>s.region!==void 0?s:{...s,region:_});const T=o.orgConfig!==void 0?{...o.orgConfig,providerAccounts:c}:c.length>0?{providerAccounts:c}:void 0,N=await st(e);if(!N.success){const s=new Error(l(N.error.message));return t.onError?.(s),O(s)}const y=at(o,e,r,"organisation",N.data),w=a?.cascade!==!1,{platformAccount:F,memberAccounts:A}=nt(c),it=w&&F!==void 0?1:0,dt=w&&A.length>0?1:0,d=2+it+dt,{id:k,name:M}=n.PREPARE;t.onStepStart?.(k,M,0,d),t.onLog?.("Synthesising organisation infrastructure\u2026","info");const z=await Z(e,y,t,"CDK synthesis failed");if(!z.success)return t.onStepComplete?.(k,M,"error",0,d),z;const K=await tt(e,y,t);if(!K.success)return t.onStepComplete?.(k,M,"error",0,d),K;t.onStepComplete?.(k,M,"completed",0,d);const{id:x,name:U}=n.ORG_DEPLOY;t.onStepStart?.(x,U,1,d);const V=J($.ORGANISATION),B=await e.cdkService.runCdkDeploy(y,V,et(t),ot(t),e.awsProvider);if(!B.success){t.onStepComplete?.(x,U,"error",1,d);const s=new Error(l(B.error));return t.onError?.(s),O(s)}const X=await e.cfnService.getStackOutputs(V);X.success||t.onLog?.("Failed to read org stack outputs (non-critical)","debug");const ut=Q(X);t.onStepComplete?.(x,U,"completed",1,d);const u=[],Y=[];if(w&&c.length>0){t.onCascadeStart?.();let s=0,f=0,S=!1,q=!1,R=2;const{platformAccount:E,memberAccounts:v}=nt(c);if(E){const{id:m,name:p}=n.CASCADE_PLATFORM;t.onStepStart?.(m,p,R,d),t.onCascadePhaseStart?.("platform");let P;try{P=await rt(o,e,r,E,"platform",t,{orgConfig:T})}catch(i){const L=l(i instanceof Error?i.message:String(i));u.push({accountId:E.id,error:L}),t.onCascadePhaseComplete?.("platform"),t.onStepComplete?.(m,p,"error",R,d),P=O(new Error(L))}P.success?(S=!0,P.data.outputs&&Y.push({accountId:E.id,outputs:P.data.outputs}),t.onCascadePhaseComplete?.("platform"),t.onStepComplete?.(m,p,"completed",R,d)):u.some(i=>i.accountId===E.id)||(u.push({accountId:E.id,error:l(P.error.message)}),t.onCascadePhaseComplete?.("platform"),t.onStepComplete?.(m,p,"error",R,d)),R++}let H=new Map;if(S&&E&&(H=await Ct(e,E,t)),o.domainProvider){const m=await St(o.domainProvider,t);q=m.domainsDeployed>0;for(const p of m.errors)u.push({accountId:"domains",error:l(p)})}if(v.length>0){const{id:m,name:p}=n.CASCADE_ACCOUNTS;t.onStepStart?.(m,p,R,d),t.onCascadePhaseStart?.("accounts"),(await Pt(v,Et,i=>{const h=(i.region??e.awsProvider.getRegion()).replace(/-/g,""),D=H.get(`${i.id}-${h}`);return rt(o,e,r,i,"account",t,{ipamPoolId:D,orgConfig:T})})).forEach((i,L)=>{const h=v[L];if(!h)return;if(i.status==="rejected"){f++,u.push({accountId:h.id,error:l(i.reason instanceof Error?i.reason.message:String(i.reason))});return}const D=i.value;D.success?(s++,D.data.outputs&&Y.push({accountId:h.id,outputs:D.data.outputs})):(f++,u.push({accountId:h.id,error:l(D.error.message)}))}),t.onCascadePhaseComplete?.("accounts"),t.onStepComplete?.(m,p,f===0?"completed":"error",R,d)}if(t.onCascadeComplete?.({platformDeployed:S,domainsDeployed:q,accountsDeployed:s,accountsFailed:f,errors:u}),u.length>0){const m=u.map(p=>` ${p.accountId}: ${p.error}`).join(`
|
|
2
|
+
`);t.onLog?.(l(`Cascade failed for ${u.length} target(s):
|
|
3
|
+
${m}`),"warn")}}if(u.length>0){const s=u.map(S=>l(`${S.accountId}: ${S.error}`)).join(`
|
|
4
|
+
`),f=new Error(`Organisation root deployed, but the cascade failed for ${u.length} target(s):
|
|
5
|
+
${s}`);return t.onError?.(f),O(f)}return W({target:r.target,deploymentType:"organisation",outputs:ut,...Y.length>0?{cascadeOutputs:Y}:{},durationMs:Date.now()-g})}export{Yt as deployOrganisation};
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import{success as R,failure as
|
|
1
|
+
import{success as R,failure as w}from"@fjall/generator";import{maskSensitiveOutput as C,getErrorMessage as A,mapSettledWithConcurrency as I}from"@fjall/util";import{stubCallerIdentity as P}from"../types/deployment/index.js";import{ORGANISATION_TYPES as T,getOrganisationStackName as _}from"../types/operations.js";import{CdkContextBuilder as $}from"../services/supporting/CdkContextBuilder.js";import{buildParamsContext as N,synthOrFail as k,forwardOutput as L,forwardResourceProgress as b}from"./contextHelpers.js";import{destroyCascadeAccount as E}from"./cascadeDestroyHelpers.js";import{partitionAccounts as v,CASCADE_MAX_CONCURRENCY as G}from"./cascadeHelpers.js";import{DEFAULT_REGION as x}from"../aws/utils/regions.js";import{STEP_IDS as M}from"../types/stepDefinitions.js";const h=M.ORG_DESTROY,S="Destroying organisation infrastructure";async function V(t,r,n){const e=Date.now(),{callbacks:o}=t,d=t.orgConfig?.providerAccounts??[],f=t.orgConfig?.primaryRegion??r.awsProvider.getRegion(),l=F(t),{platformAccount:g,memberAccounts:m}=v(d),D=t.options?.cascade!==!1;o.onLog?.(`Destroying organisation infrastructure (${d.length} accounts, ${l.length} region(s))`,"info");const s=[],p=[];if(D){if(o.onCascadeStart?.(),m.length>0){o.onCascadePhaseStart?.("accounts");const a=U(m,l),c=await I(a,G,({account:i,region:u})=>E(t,r,n,i,"account",u,o));for(let i=0;i<c.length;i++){const u=c[i],y=a[i];!u||!y||(u.status==="fulfilled"?u.value.success?p.push(`Account-${y.account.name}-${y.region}`):s.push({accountId:y.account.id,error:u.value.error??"Unknown error"}):s.push({accountId:y.account.id,error:A(u.reason)}))}}if(g){o.onCascadePhaseStart?.("platform");const a=await E(t,r,n,g,"platform",f,o);a.success?p.push("Platform"):s.push({accountId:g.id,error:a.error??"Platform destroy failed"})}if(o.onCascadeComplete?.({platformDeployed:!1,domainsDeployed:!1,accountsDeployed:0,accountsFailed:s.length,errors:s}),s.length>0){const a=s.map(i=>` ${i.accountId}: ${i.error}`).join(`
|
|
2
2
|
`),c=new Error(`Cascade destroy completed with ${s.length} failure(s):
|
|
3
|
-
${a}`);o.onError?.(c),o.onLog?.(
|
|
3
|
+
${a}`);o.onError?.(c),o.onLog?.(C(c.message),"warn")}}if(s.length>0)return o.onLog?.("Skipping organisation root stack destroy due to cascade failures","warn"),R({target:n.target,deploymentType:"organisation",stacksDestroyed:p,durationMs:Date.now()-e,warnings:s.map(c=>C(`${c.accountId}: ${c.error}`))});o.onStepStart?.(h,S,0,1);const O=await Y(t,r,n,f,o);if(O.success)p.push("Organisation"),o.onStepComplete?.(h,S,"completed",0,1);else return o.onStepComplete?.(h,S,"error",0,1),w(O.error);return R({target:n.target,deploymentType:"organisation",stacksDestroyed:p,durationMs:Date.now()-e})}async function Y(t,r,n,e,o){const d=$.buildDeploymentContext({deployType:"organisation",target:n.target,path:n.path,region:e,callerIdentity:P(r.awsProvider.getAccountId()),...N({orgConfig:t.orgConfig,identity:t.identity})},{verbose:t.options?.verbose},t.orgConfig),f=_(T.ORGANISATION);o.onLog?.("Synthesising organisation infrastructure\u2026","info");const l=await k(r,d,o,"Organisation synth failed");if(!l.success)return l;o.onLog?.(`Destroying ${f} stack\u2026`,"info");const g=await r.cdkService.runCdkDestroy(d,f,L(o),b(o),r.awsProvider,!0);if(!g.success){const m=new Error(C(`Organisation destroy failed: ${g.error}`));return o.onError?.(m),w(m)}return R(void 0)}function F(t){const r=t.orgConfig?.primaryRegion??x,n=t.orgConfig?.secondaryRegions??[],e=[r,...n],o=t.orgConfig?.disasterRecoveryRegion;return o&&!e.includes(o)&&e.push(o),e}function U(t,r){const n=[];for(const e of r)for(const o of t)n.push({account:o,region:e});return n}export{V as destroyOrganisation};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{success as
|
|
1
|
+
import{success as C,failure as l}from"@fjall/generator";import{OrganizationsClient as L}from"@aws-sdk/client-organizations";import{RAMClient as D}from"@aws-sdk/client-ram";import{CloudFormationClient as F}from"@aws-sdk/client-cloudformation";import{EC2Client as j}from"@aws-sdk/client-ec2";import{BackupClient as B}from"@aws-sdk/client-backup";import{CostExplorerClient as W}from"@aws-sdk/client-cost-explorer";import{SSOAdminClient as z}from"@aws-sdk/client-sso-admin";import{ensureOrganisationExists as G}from"../aws/organisations/organisation.js";import{enablePolicyTypes as K}from"../aws/organisations/policies.js";import{enableServiceAccess as q}from"../aws/organisations/serviceAccess.js";import{enableRamSharing as H}from"../aws/organisations/ram.js";import{activateTrustedAccess as J}from"../aws/organisations/trustedAccess.js";import{enableIpamDelegatedAdmin as Q}from"../aws/organisations/ipam.js";import{updateBackupGlobalSettings as V}from"../aws/organisations/backup.js";import{listAccounts as E,createAccount as X}from"../aws/organisations/accounts.js";import{ensureOrganisationalUnitsExist as Y,placeAccountsInOUs as Z,buildAccountToOUMap as _}from"../aws/organisations/organisationalUnits.js";import{activateCostAllocationTags as $}from"../aws/organisations/costAllocation.js";import{checkIdentityCentreStatus as b}from"../aws/organisations/identityCentre.js";import{registerSecurityDelegates as k}from"../aws/organisations/delegatedAdmin.js";import{isOULeaf as ee}from"../aws/organisations/types.js";async function Ee(r,o,e){const n=[],i=[],t=[],s=[];let u;const p=r.getClient(L),O=r.getClient(D),U=r.getClient(F),R=r.getClient(j),N=r.getClient(B),T=r.getClient(W),x=r.getClient(z);e?.onPhaseStart?.("create-organisation"),e?.onProgress?.("Ensuring AWS Organisation exists");const g=await G(p);if(!g.success)return e?.onError?.("create-organisation",g.error),e?.onPhaseComplete?.("create-organisation","error"),l(g.error);const{orgId:M,rootId:P}=g.data;if(e?.onPhaseComplete?.("create-organisation","completed"),n.push("create-organisation"),await c("enable-policies",()=>(e?.onProgress?.("Enabling organisation policy types"),K(p,P)),n,t,e),await c("enable-service-access",()=>(e?.onProgress?.("Enabling AWS service access"),q(p)),n,t,e),await c("enable-ram-sharing",()=>(e?.onProgress?.("Enabling RAM sharing"),H(O)),n,t,e),await c("activate-trusted-access",()=>(e?.onProgress?.("Activating CloudFormation trusted access"),J(U)),n,t,e),o.platformAccountId){const a=o.platformAccountId;await c("enable-ipam",()=>(e?.onProgress?.("Enabling IPAM delegated administrator"),Q(R,a)),n,t,e)}await c("configure-backup",()=>(e?.onProgress?.("Updating backup global settings"),V(N)),n,t,e),e?.onPhaseStart?.("create-accounts"),e?.onProgress?.("Checking for missing accounts");const d=await te(p,o.accounts,s);let y=[];d.success?(y=d.data,n.push("create-accounts"),e?.onPhaseComplete?.("create-accounts","completed")):(t.push({phase:"create-accounts",error:d.error.message}),e?.onError?.("create-accounts",d.error),e?.onPhaseComplete?.("create-accounts","error"));let h={};e?.onPhaseStart?.("create-organisational-units"),e?.onProgress?.("Ensuring organisational units exist");const f=await Y(p,P,o.organisationalUnits);f.success?(h=f.data,n.push("create-organisational-units"),e?.onPhaseComplete?.("create-organisational-units","completed")):(t.push({phase:"create-organisational-units",error:f.error.message}),e?.onError?.("create-organisational-units",f.error),e?.onPhaseComplete?.("create-organisational-units","error"));const m=Array.isArray(o.organisationalUnits)?void 0:o.organisationalUnits,A=o.accountPlacements??[],S=A.length===0&&m!==void 0?ne(m,y):A;if(Object.keys(h).length===0)i.push("place-accounts"),e?.onPhaseStart?.("place-accounts"),e?.onPhaseComplete?.("place-accounts","skipped");else if(o.accountPlacements===void 0&&m===void 0){const a=new Error("Account placements not provided despite OUs being created. Caller must populate accountPlacements (flat-list mode cannot derive placements internally).");t.push({phase:"place-accounts",error:a.message}),e?.onPhaseStart?.("place-accounts"),e?.onError?.("place-accounts",a),e?.onPhaseComplete?.("place-accounts","error")}else if(S.length===0)i.push("place-accounts"),e?.onPhaseStart?.("place-accounts"),e?.onPhaseComplete?.("place-accounts","skipped");else{const a=m?_(m,h):void 0;await c("place-accounts",()=>(e?.onProgress?.("Placing accounts in organisational units"),Z(p,h,S,a)),n,t,e)}const w=o.costAllocationTags??[];if(w.length>0?await c("activate-cost-tags",()=>(e?.onProgress?.("Activating cost allocation tags"),$(T,w.map(a=>({TagKey:a})))),n,t,e):(i.push("activate-cost-tags"),e?.onPhaseStart?.("activate-cost-tags"),e?.onPhaseComplete?.("activate-cost-tags","skipped")),o.skipIdentityCentre)i.push("check-identity-centre"),e?.onPhaseStart?.("check-identity-centre"),e?.onPhaseComplete?.("check-identity-centre","skipped");else{e?.onPhaseStart?.("check-identity-centre"),e?.onProgress?.("Checking Identity Centre status");const a=await b(x);a.success?(u=a.data.enabled?"enabled":"not-enabled",n.push("check-identity-centre"),e?.onPhaseComplete?.("check-identity-centre","completed")):(t.push({phase:"check-identity-centre",error:a.error.message}),e?.onError?.("check-identity-centre",a.error),e?.onPhaseComplete?.("check-identity-centre","error"))}const I=o.securityDelegateAccountId;return I?await c("register-security-delegates",()=>(e?.onProgress?.("Registering security service delegated administrators"),k(p,I)),n,t,e):(i.push("register-security-delegates"),e?.onPhaseStart?.("register-security-delegates"),e?.onPhaseComplete?.("register-security-delegates","skipped")),C({organisationId:M,createdAccounts:s,identityCentreStatus:u,phasesCompleted:n,phasesSkipped:i,errors:t})}async function c(r,o,e,n,i){i?.onPhaseStart?.(r);const t=await o();t.success?(e.push(r),i?.onPhaseComplete?.(r,"completed")):(n.push({phase:r,error:t.error.message}),i?.onError?.(r,t.error),i?.onPhaseComplete?.(r,"error"))}async function te(r,o,e){const n=await E(r);if(!n.success)return l(n.error);const i=new Set(n.data.map(s=>s.Name?.toLowerCase()).filter(s=>s!==void 0));for(const s of o){if(i.has(s.name.toLowerCase()))continue;const u=await X(r,s.name,s.email);if(!u.success)return l(u.error);e.push({name:u.data.accountName,accountId:u.data.accountId})}if(e.length===0)return C(n.data);const t=await E(r);return t.success?C(t.data):l(t.error)}function v(r){const o=[];for(const e of Object.values(r))ee(e)?o.push(...e):o.push(...v(e));return o}function ne(r,o){const e=v(r),n=new Map;for(const t of o){const s=t.Name?.toLowerCase();s&&t.Id&&n.set(s,t)}const i=[];for(const t of e){const s=n.get(t.toLowerCase());s?.Id&&s.Name&&i.push({id:s.Id,name:s.Name,environment:t})}return i}export{Ee as runOrganisationSetup};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { type Result } from "@fjall/generator";
|
|
2
|
+
import type { ProviderAccount } from "@fjall/util/config";
|
|
3
|
+
import type { OrgConfig } from "../types/orgConfig.js";
|
|
4
|
+
import type { DeployServices } from "./serviceFactory.js";
|
|
5
|
+
export interface ReconcileResult {
|
|
6
|
+
providerAccounts: ProviderAccount[];
|
|
7
|
+
/** Account names declared in ACCOUNTS but not yet present in AWS Organizations. */
|
|
8
|
+
missingAccountNames: string[];
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* When the orgConfig providerAccounts list is empty or root-only (first-deploy
|
|
12
|
+
* or stale sync), reconstruct the deployable account set by intersecting the
|
|
13
|
+
* live AWS Organizations account list with the ACCOUNTS configuration file.
|
|
14
|
+
* The ACCOUNTS file owns the accountName→environment mapping; AWS Organizations
|
|
15
|
+
* owns account IDs.
|
|
16
|
+
*
|
|
17
|
+
* `workingDirectory` is the prepared repository root (a populated clone in the
|
|
18
|
+
* worker). It MUST be passed through — `parseAccountsConfiguration` `tsImport`s
|
|
19
|
+
* `<workingDirectory>/fjall/organisation/infrastructure.ts`, which can only
|
|
20
|
+
* resolve `@fjall/components-infrastructure` after the clone's node_modules is
|
|
21
|
+
* installed.
|
|
22
|
+
*/
|
|
23
|
+
export declare function reconcileProviderAccounts(services: DeployServices, workingDirectory: string): Promise<Result<ReconcileResult, Error>>;
|
|
24
|
+
/**
|
|
25
|
+
* Merge a reconciled providerAccounts list into an existing orgConfig.
|
|
26
|
+
* Additive by account id — the reconciler supplies entries the config is
|
|
27
|
+
* missing, never replacing pre-existing ones.
|
|
28
|
+
*/
|
|
29
|
+
export declare function mergeReconciledProviderAccounts(existing: OrgConfig | undefined, reconciled: ProviderAccount[]): OrgConfig;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{z as u}from"zod";import{success as N,failure as c}from"@fjall/generator";import{STRUCTURAL_ENVIRONMENTS as v}from"@fjall/util";import{OrganizationsClient as w}from"@aws-sdk/client-organizations";import{listAccounts as g}from"../aws/organisations/accounts.js";import{parseAccountsConfiguration as C,flattenAccountsToEnvironments as E}from"./accountsConfig.js";const T=u.object({Id:u.string().min(1),Name:u.string().min(1)});async function L(e,s){const n=await C(s);if(!n.success)return c(new Error(`Failed to parse ACCOUNTS configuration: ${n.error.message}`));if(n.data===null)return c(new Error("ACCOUNTS configuration file not found"));const r=E(n.data),f=new Map;for(const{accountName:o,environment:t}of r)f.set(o.toLowerCase(),{environment:t,displayName:o});let m;try{m=e.awsProvider.getClient(w)}catch(o){return c(o instanceof Error?o:new Error(String(o)))}const a=await g(m);if(!a.success)return c(a.error);const d=new Map;for(const o of a.data){const t=T.safeParse(o);t.success&&d.set(t.data.Name.toLowerCase(),{id:t.data.Id,name:t.data.Name})}const p=[],l=[];for(const[o,{environment:t,displayName:A}]of f){const i=d.get(o);if(!i){l.push(A);continue}t!==v.ROOT&&p.push({id:i.id,name:i.name,environment:t})}return N({providerAccounts:p,missingAccountNames:l})}function M(e,s){const n=new Map;for(const r of e?.providerAccounts??[])n.set(r.id,r);for(const r of s)n.has(r.id)||n.set(r.id,r);return{...e??{},providerAccounts:Array.from(n.values())}}export{M as mergeReconciledProviderAccounts,L as reconcileProviderAccounts};
|
|
@@ -18,7 +18,7 @@ export declare class CdkService {
|
|
|
18
18
|
runImport(path: string, resourceMappingFile?: string, options?: CdkOptions): Promise<Result<CdkOutput, string>>;
|
|
19
19
|
synth(path: string, options?: CdkOptions): Promise<Result<CdkOutput, string>>;
|
|
20
20
|
bootstrap(accountId: string, region: string, options?: CdkOptions): Promise<Result<CdkOutput, string>>;
|
|
21
|
-
runCdkSynth(context: DeploymentContext, onOutput?: (chunk: string) => void): Promise<Result<StepOutput, string>>;
|
|
21
|
+
runCdkSynth(context: DeploymentContext, onOutput?: (chunk: string) => void, credentials?: CdkOptions["credentials"]): Promise<Result<StepOutput, string>>;
|
|
22
22
|
runCdkBootstrap(context: DeploymentContext, onOutput?: (chunk: string) => void, credentials?: CdkOptions["credentials"]): Promise<Result<StepOutput, string>>;
|
|
23
23
|
runCdkDiff(context: DeploymentContext, onOutput?: (chunk: string) => void): Promise<Result<StepOutput, string>>;
|
|
24
24
|
runCdkDeploy(context: DeploymentContext, stackPattern?: string, onOutput?: (chunk: string) => void, onResourceProgress?: (event: ResourceEvent) => void, aws?: AwsProvider, credentials?: CdkOptions["credentials"], parameters?: Record<string, string>): Promise<Result<StepOutput, string>>;
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import{existsSync as
|
|
2
|
-
`)),
|
|
3
|
-
`));const
|
|
1
|
+
import{existsSync as I}from"fs";import{join as b}from"path";import{logger as v}from"@fjall/util/logger";import{success as D,failure as s}from"@fjall/generator";import{DEFAULT_REGION as h}from"../../aws/utils/regions.js";import{getErrorMessage as k,maskSensitiveOutput as p}from"@fjall/util";import{CdkEventMonitor as R,startStackMonitoring as E}from"./CdkEventMonitoring.js";import{analyseDeployOutput as F,analyseDestroyResult as w,createEnhancedOutputCallback as O}from"./CdkOutputAnalyser.js";import{CdkCommandRunner as K}from"./CdkCommandRunner.js";import{CdkArgumentBuilder as N}from"./CdkArgumentBuilder.js";import{CdkProcessManager as T}from"./CdkProcessManager.js";import{wrapWithConstructMapEnrichment as L}from"./constructMapEnrichment.js";import{STACK_DETECTION_FALLBACK_MS as P,resolveStackName as M,getFallbackStackName as W,buildDeploymentCdkContext as C}from"./cdkServiceHelpers.js";class Z{commandRunner;eventMonitor;constructor(e){const r=e?.processManager??new T(new N);this.commandRunner=new K(r),this.eventMonitor=new R({eventLogWriterFactory:e?.eventLogWriterFactory})}dispose(){this.commandRunner.dispose()}async checkDifferences(e,r,t){return this.commandRunner.checkDifferences(e,r,t)}async deploy(e,r,t){return this.commandRunner.deploy(e,r,t)}async destroy(e,r,t){return this.commandRunner.destroy(e,r,t)}async runImport(e,r,t){return this.commandRunner.runImport(e,r,t)}async synth(e,r){return this.commandRunner.synth(e,r)}async bootstrap(e,r,t){return this.commandRunner.bootstrap(e,r,t)}async runCdkSynth(e,r,t){const n=e.callerIdentity?.Account;try{const o=await this.synth(e.path,{outputCallback:r,context:C(e,n,e.region||h),...e.assemblyDir!==void 0?{outputDir:e.assemblyDir}:{},...t!==void 0?{credentials:t}:{}});return o.success?D({message:"CloudFormation template synthesised",details:o.data.output?{synthesisTime:o.data.output}:void 0}):s(o.error||"Failed to synthesise CloudFormation template")}catch(o){return s(`CDK synth failed: ${p(k(o))}`)}}async runCdkBootstrap(e,r,t){const n=e.callerIdentity?.Account,o=e.region||h;try{if(!n)return s("No AWS account ID available");const l=b(e.path,"node_modules");if(!I(l))return s(`Dependencies not installed. Please run 'npm install' in ${e.path} before deploying.`);const u=await this.bootstrap(n,o,{outputCallback:r,credentials:t});return u.success?D({message:"AWS environment bootstrapped"}):s(u.error||"Failed to bootstrap AWS environment")}catch(l){return s(`CDK bootstrap failed: ${p(k(l))}`)}}async runCdkDiff(e,r){const t=e.callerIdentity?.Account;try{const n=await this.checkDifferences(e.path,void 0,{verbose:e.options?.verbose,outputCallback:r,context:C(e,t,e.region||h)});return n.success?D({message:"Diff check complete",details:{hasDifferences:n.data.hasDifferences,details:n.data.details}}):s(`CDK diff failed: ${n.error.message}`)}catch(n){return s(`CDK diff failed: ${p(k(n))}`)}}async runCdkDeploy(e,r,t,n,o,l,u){const f=e.callerIdentity?.Account,y=e.region||h;if(!f)return s("AWS account ID not available. Please ensure AWS credentials are properly configured.");if(!o)return s("AwsProvider is required for deployment monitoring.");const m=e.assemblyDir??b(e.path,"cdk.out"),d=L(m,n);let i=null,g;try{const c=M(r,e)??W(e);i=await this.eventMonitor.createEventMonitor("deploy",c,y,e,o),t&&(t(p(`Starting CloudFormation deployment of ${c}...
|
|
2
|
+
`)),t(`Monitoring CloudFormation events (CDK process running in background)...
|
|
3
|
+
`));const a={cdkOutput:"",actualStackName:c,stackDetected:!1,monitoringPromise:null},S=O(a,t,i,d);g=setTimeout(()=>{!a.stackDetected&&i&&!a.monitoringPromise&&(v.debug("CdkService","Fallback monitoring STARTING",{targetStackName:c,stackDetected:a.stackDetected,hasOnResourceProgress:!!n}),a.monitoringPromise=E(i,c,d))},P);const A=await this.deploy(e.path,c,{verbose:e.options?.verbose,outputCallback:S,...e.assemblyDir!==void 0?{appDir:e.assemblyDir}:{useCdkOut:!0},cdkOutputLogger:i?.getEventLogger()??void 0,context:C(e,f,y),credentials:l,...u!==void 0&&Object.keys(u).length>0&&{parameters:u}});return F(a.cdkOutput,A,a.actualStackName)}catch(c){const a=`CDK deploy failed: ${p(k(c))}`;return v.error("CdkService","CDK deployment exception",{error:a}),s(a)}finally{clearTimeout(g),i&&i.stopMonitoring()}}async runCdkDestroy(e,r,t,n,o,l,u){const f=e.callerIdentity?.Account,y=e.region||h;let m=null;try{const d=M(r,e);f&&d&&o&&(m=await this.eventMonitor.createEventMonitor("destroy",d,y,e,o),m.startMonitoring(d,g=>{n?.(g)},(g,c)=>{}));const i=await this.destroy(e.path,r,{verbose:e.options?.verbose,outputCallback:t,useCdkOut:l,cdkOutputLogger:m?.getEventLogger()??void 0,context:C(e,f,y),credentials:u});return w(i)}catch(d){return s(`CDK destroy failed: ${p(k(d))}`)}finally{m&&m.stopMonitoring()}}}export{Z as CdkService};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{getApplicationStackName as i,getOrganisationStackName as d,isApplicationStack as p}from"../../types/operations.js";const
|
|
1
|
+
import{getApplicationStackName as i,getOrganisationStackName as d,isApplicationStack as p}from"../../types/operations.js";const u=5e3;function c(o,e){if(o&&!o.includes("*"))return o;if(o){const n=o.match(/\*?(\w+)\*?/);if(n?.[1]){const a=n[1],r=e.target;return p(a)?i(r,a):`${r}${a}`}return o}}function m(o){const e=o.deployType;return e==="organisation"||e==="platform"||e==="account"?d(e):`${o.target}Network`}function g(o,e,n){return{accountId:e,region:n,environment:o.environment,managedAccount:o.isManagedAccount,accountName:o.accountName,orgId:o.orgId,rootId:o.rootId,managementAccountId:o.managementAccountId,ipamPoolId:o.ipamPoolId,fjallOrgId:o.fjallOrgId,fjallOidcConfigured:o.fjallOidcConfigured?"true":void 0,orgConfig:o.orgConfig}}export{u as STACK_DETECTION_FALLBACK_MS,g as buildDeploymentCdkContext,m as getFallbackStackName,c as resolveStackName};
|
|
@@ -4,4 +4,4 @@ import type { ResourceEvent } from "../../aws/utils/cloudformationEvents.js";
|
|
|
4
4
|
* and return a wrapped callback that enriches resource events with
|
|
5
5
|
* group and constructPath fields.
|
|
6
6
|
*/
|
|
7
|
-
export declare function wrapWithConstructMapEnrichment(
|
|
7
|
+
export declare function wrapWithConstructMapEnrichment(assemblyDir: string, onResourceProgress?: (event: ResourceEvent) => void): ((event: ResourceEvent) => void) | undefined;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{existsSync as u,readFileSync as s}from"fs";import{join as p}from"path";import{logger as i}from"@fjall/util/logger";import{enrichFromConstructMap as a,recordToConstructMap as f,FJALL_MANIFEST_FILENAME as d}from"@fjall/util/constructMap";import{getErrorMessage as m}from"@fjall/util";function C(
|
|
1
|
+
import{existsSync as u,readFileSync as s}from"fs";import{join as p}from"path";import{logger as i}from"@fjall/util/logger";import{enrichFromConstructMap as a,recordToConstructMap as f,FJALL_MANIFEST_FILENAME as d}from"@fjall/util/constructMap";import{getErrorMessage as m}from"@fjall/util";function C(o,c){if(!c)return;const t=p(o,d);let e;try{if(u(t)){const r=JSON.parse(s(t,"utf-8")),n=typeof r=="object"&&r!==null?r.resourceMap:void 0;h(n)&&(e=f(n)),e&&e.size>0&&i.debug("CdkService",`Loaded construct map with ${e.size} entries`)}}catch(r){i.debug("CdkService",`Could not read construct map: ${m(r)}`)}return!e||e.size===0?c:r=>{const n=a(r.logicalId,r.resourceType,e);c({...r,...n.group!==void 0?{group:n.group}:{},...n.constructPath!==void 0?{constructPath:n.constructPath}:{}})}}function h(o){if(typeof o!="object"||o===null||Array.isArray(o))return!1;const c=Object.values(o);if(c.length===0)return!0;const t=c[0];return typeof t=="object"&&t!==null&&"constructPath"in t&&"group"in t&&"resourceType"in t}export{C as wrapWithConstructMapEnrichment};
|
|
@@ -18,6 +18,8 @@ export declare class CdkContextBuilder {
|
|
|
18
18
|
deployType: "application" | "organisation" | "platform" | "account";
|
|
19
19
|
target: string;
|
|
20
20
|
path: string;
|
|
21
|
+
assemblyDir?: string;
|
|
22
|
+
environment?: string;
|
|
21
23
|
callerIdentity?: CallerIdentity;
|
|
22
24
|
region?: string;
|
|
23
25
|
stackOutputs?: StackOutputsRecord;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{DEFAULT_REGION as
|
|
1
|
+
import{DEFAULT_REGION as r}from"@fjall/generator";class l{static buildDeploymentContext(e,o,a){return{deployType:e.deployType,target:e.target,path:e.path,...e.assemblyDir!==void 0?{assemblyDir:e.assemblyDir}:{},...e.environment!==void 0?{environment:e.environment}:{},options:o,stackOutputs:e.stackOutputs||{},callerIdentity:e.callerIdentity,region:e.region||a?.primaryRegion||r,isManagedAccount:e.isManagedAccount,accountName:e.accountName,logPath:e.logPath,orgId:e.orgId,rootId:e.rootId,managementAccountId:e.managementAccountId,ipamPoolId:e.ipamPoolId,fjallOrgId:e.fjallOrgId,fjallOidcConfigured:e.fjallOidcConfigured,orgConfig:e.orgConfig}}static updateContext(e,o){return{...e,...o}}}export{l as CdkContextBuilder};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{APPLICATION_STACKS as n,APPLICATION_DEPLOY_ORDER as A,getApplicationStepName as
|
|
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 C(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},{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,C as getDestroyStepId};
|
|
@@ -100,6 +100,14 @@ export interface DeployCallbacks {
|
|
|
100
100
|
onCdkOutput?: (output: string, type: "synth" | "diff") => void;
|
|
101
101
|
/** @emittedBy engine */
|
|
102
102
|
onCascadeStart?: () => void;
|
|
103
|
+
/**
|
|
104
|
+
* @emittedBy engine — fired once after account reconciliation when accounts
|
|
105
|
+
* are declared in ACCOUNTS but not yet present in AWS Organizations (the
|
|
106
|
+
* cascade skips them). Carries the structured list so consumers can render a
|
|
107
|
+
* banner; the same information is also emitted as an `onProgress` warning for
|
|
108
|
+
* log surfaces.
|
|
109
|
+
*/
|
|
110
|
+
onCascadeMissingAccounts?: (accountNames: readonly string[]) => void;
|
|
103
111
|
/** @emittedBy engine */
|
|
104
112
|
onCascadePhaseStart?: (phase: CascadePhase) => void;
|
|
105
113
|
/** @emittedBy engine — fired when a cascade phase completes. */
|
|
@@ -4,6 +4,14 @@ export interface DeploymentContext {
|
|
|
4
4
|
deployType: "application" | "organisation" | "platform" | "account";
|
|
5
5
|
target: string;
|
|
6
6
|
accountName?: string;
|
|
7
|
+
/**
|
|
8
|
+
* CDK app environment selector (`root` | `platform` | <member env>). Passed
|
|
9
|
+
* to the synthesised app as `--context environment=…`, the highest-priority
|
|
10
|
+
* source in `getConfig()`. Set per cascade account so each account synths
|
|
11
|
+
* the correct `infrastructure.ts` branch deterministically, without relying
|
|
12
|
+
* on accountName→providerAccount resolution.
|
|
13
|
+
*/
|
|
14
|
+
environment?: string;
|
|
7
15
|
region?: string;
|
|
8
16
|
callerIdentity?: CallerIdentity;
|
|
9
17
|
stackOutputs?: StackOutputsRecord;
|
|
@@ -20,6 +28,14 @@ export interface DeploymentContext {
|
|
|
20
28
|
infraOnly?: boolean;
|
|
21
29
|
};
|
|
22
30
|
path: string;
|
|
31
|
+
/**
|
|
32
|
+
* Isolated cloud-assembly directory for this deployment. When set, synth
|
|
33
|
+
* writes the assembly here (`--output`) and deploy reads it (`--app`),
|
|
34
|
+
* instead of the shared `<path>/cdk.out`. The cascade gives every account
|
|
35
|
+
* its own `cdk.out.<accountId>.<region>` so parallel member synth+deploy
|
|
36
|
+
* cannot clobber one another's templates.
|
|
37
|
+
*/
|
|
38
|
+
assemblyDir?: string;
|
|
23
39
|
logPath?: string;
|
|
24
40
|
}
|
|
25
41
|
export interface StepOutput {
|
|
@@ -15,7 +15,7 @@ export declare const DEPLOYMENT_EVENT_RESOURCE_CATEGORIES: readonly ["security",
|
|
|
15
15
|
export type DeploymentEventResourceCategory = (typeof DEPLOYMENT_EVENT_RESOURCE_CATEGORIES)[number];
|
|
16
16
|
export declare const CASCADE_PHASES: readonly ["platform", "domains", "accounts"];
|
|
17
17
|
export declare const CASCADE_ACCOUNT_STATUSES: readonly ["started", "deploying", "completed", "failed"];
|
|
18
|
-
export declare const DEPLOYMENT_EVENT_TYPES: readonly ["step", "resource", "docker", "ecs", "error", "complete", "cascade_phase", "cascade_account", "parallel_phase", "detection", "log"];
|
|
18
|
+
export declare const DEPLOYMENT_EVENT_TYPES: readonly ["step", "resource", "docker", "ecs", "error", "complete", "cascade_phase", "cascade_account", "cascade_missing_accounts", "parallel_phase", "detection", "log"];
|
|
19
19
|
export type DeploymentEventType = (typeof DEPLOYMENT_EVENT_TYPES)[number];
|
|
20
20
|
export type DeploymentEventCascadePhase = (typeof CASCADE_PHASES)[number];
|
|
21
21
|
export type DeploymentEventCascadeAccountStatus = (typeof CASCADE_ACCOUNT_STATUSES)[number];
|
|
@@ -29,6 +29,7 @@ export declare const DeploymentEventSchema: z.ZodObject<{
|
|
|
29
29
|
complete: "complete";
|
|
30
30
|
cascade_phase: "cascade_phase";
|
|
31
31
|
cascade_account: "cascade_account";
|
|
32
|
+
cascade_missing_accounts: "cascade_missing_accounts";
|
|
32
33
|
parallel_phase: "parallel_phase";
|
|
33
34
|
detection: "detection";
|
|
34
35
|
log: "log";
|
|
@@ -117,6 +118,9 @@ export declare const DeploymentEventSchema: z.ZodObject<{
|
|
|
117
118
|
accounts: "accounts";
|
|
118
119
|
}>>;
|
|
119
120
|
}, z.core.$strict>>;
|
|
121
|
+
cascadeMissingAccounts: z.ZodOptional<z.ZodObject<{
|
|
122
|
+
accountNames: z.ZodArray<z.ZodString>;
|
|
123
|
+
}, z.core.$strict>>;
|
|
120
124
|
parallelPhase: z.ZodOptional<z.ZodObject<{
|
|
121
125
|
stacks: z.ZodArray<z.ZodString>;
|
|
122
126
|
status: z.ZodEnum<{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{z as e}from"zod";const r=["security","network","compute","database","storage","monitoring","dns","identity","bootstrap","events","registry","backup"],n=["platform","domains","accounts"],c=["started","deploying","completed","failed"],i=["step","resource","docker","ecs","error","complete","cascade_phase","cascade_account","parallel_phase","detection","log"],
|
|
1
|
+
import{z as e}from"zod";const r=["security","network","compute","database","storage","monitoring","dns","identity","bootstrap","events","registry","backup"],n=["platform","domains","accounts"],c=["started","deploying","completed","failed"],i=["step","resource","docker","ecs","error","complete","cascade_phase","cascade_account","cascade_missing_accounts","parallel_phase","detection","log"],m={step:"step",resource:"resource",docker:"docker",ecs:"ecs",error:"error",complete:null,cascade_phase:"cascadePhase",cascade_account:"cascadeAccount",cascade_missing_accounts:"cascadeMissingAccounts",parallel_phase:"parallelPhase",detection:"detection",log:"log"},p=e.object({type:e.enum(i),timestamp:e.string().max(64),sequence:e.number().int().nonnegative().optional(),step:e.object({id:e.string().max(256),name:e.string().max(256),index:e.number(),total:e.number(),status:e.string().max(64)}).strict().optional(),resource:e.object({logicalId:e.string().max(256),resourceType:e.string().max(256),category:e.enum(r),group:e.string().max(128).optional(),constructPath:e.string().max(512).optional(),displayName:e.string().max(256),status:e.string().max(64),statusReason:e.string().max(2048).optional(),physicalId:e.string().max(2048).optional(),expectedDurationSeconds:e.number().optional(),stack:e.string().max(256).optional()}).strict().optional(),docker:e.object({message:e.string().max(2048),percentage:e.number().optional()}).strict().optional(),ecs:e.object({status:e.string().max(64),message:e.string().max(2048).optional(),percentage:e.number().optional()}).strict().optional(),error:e.object({message:e.string().max(4096),category:e.string().max(128).optional(),remediation:e.array(e.string().max(1024)).max(10).optional()}).strict().optional(),message:e.string().max(2048).optional(),cascadePhase:e.object({phase:e.enum(n),status:e.enum(["started","completed"])}).strict().optional(),cascadeAccount:e.object({accountId:e.string().max(32),region:e.string().max(32),operationKey:e.string().max(256),status:e.enum(c),error:e.string().max(2048).optional(),phase:e.enum(["bootstrap","synth","deploy","destroy"]).optional(),cascadePhase:e.enum(n).optional()}).strict().optional(),cascadeMissingAccounts:e.object({accountNames:e.array(e.string().max(256)).max(256)}).strict().optional(),parallelPhase:e.object({stacks:e.array(e.string().max(256)).max(20),status:e.enum(["started","completed"]),results:e.array(e.object({stack:e.string().max(256),success:e.boolean(),error:e.string().max(2048).optional()}).strict()).max(20).optional()}).strict().optional(),detection:e.object({pattern:e.string().max(128).nullable(),hasDockerfile:e.boolean(),hasDifferences:e.boolean(),resources:e.object({hasNetwork:e.boolean(),hasCompute:e.boolean(),hasDatabase:e.boolean(),hasStorage:e.boolean(),hasMessaging:e.boolean(),hasCdn:e.boolean()}).strict(),requiredSecrets:e.array(e.string().max(512)).max(100).optional()}).strict().optional(),log:e.object({message:e.string().max(2048),level:e.enum(["info","debug","warn"])}).strict().optional()}).strict().superRefine((t,s)=>{const a=m[t.type],o=a?t[a]:void 0;a&&o==null&&s.addIssue({code:e.ZodIssueCode.custom,message:`"${a}" is required when type is "${t.type}"`,path:[a]}),t.type==="complete"&&!t.message&&s.addIssue({code:e.ZodIssueCode.custom,message:'"message" is required when type is "complete"',path:["message"]})});function u(t){if(t==="platform")return"platform";if(t==="account")return"accounts"}export{c as CASCADE_ACCOUNT_STATUSES,n as CASCADE_PHASES,r as DEPLOYMENT_EVENT_RESOURCE_CATEGORIES,i as DEPLOYMENT_EVENT_TYPES,p as DeploymentEventSchema,u as toCascadePhase};
|
|
@@ -73,6 +73,10 @@ export declare const STEP_NAMES: {
|
|
|
73
73
|
readonly PREPARE_DESTROY: "Preparing destruction";
|
|
74
74
|
readonly BOOTSTRAP: "Bootstrapping AWS environment";
|
|
75
75
|
readonly CHECK_INFRA_STATE: "Checking infrastructure state";
|
|
76
|
+
readonly ORG_SETUP: "Configuring organisation";
|
|
77
|
+
readonly ORG_DEPLOY: "Deploying organisation";
|
|
78
|
+
readonly CASCADE_PLATFORM: "Deploying platform";
|
|
79
|
+
readonly CASCADE_ACCOUNTS: "Deploying accounts";
|
|
76
80
|
};
|
|
77
81
|
/**
|
|
78
82
|
* Canonical step names emitted by infrastructure deployments (platform/account).
|
|
@@ -1 +1 @@
|
|
|
1
|
-
const E={AUTH:"auth",DETECT_CONFIG:"detect-config",BOOTSTRAP:"bootstrap",CFN_CHECK:"cfn-check",DIFF:"diff",DEPLOY:"deploy",DESTROY:"destroy",VERIFY:"verify",CDK_SYNTH:"cdk-synth",DOCKER_OPERATIONS:"docker-operations",ECR_INIT:"ecr-init",DOCKER_DEPLOY:"docker-deploy",TAG_ECR_IMAGES:"tag-ecr-images",ORG_SETUP:"org-setup",ORG_ENSURE:"org-ensure",ORG_POLICY_TYPES:"policy-types",ORG_SERVICE_ACCESS:"service-access",ORG_CREATE_ACCOUNTS:"create-accounts",ORG_ENSURE_OUS:"ensure-ous",ORG_PLACE_ACCOUNTS:"place-accounts",IDENTITY_CENTRE:"identity-centre",ORG_ACCOUNTS:"accounts",ORG_COST_TAGS:"cost-tags",ORG_PROFILES:"profiles",PREPARE:"prepare",PREPARE_ENVIRONMENT:"prepare-environment",PLATFORM_ACCOUNT:"platform-account",ACCOUNT_CONTEXT:"account-context",CONNECT:"connect",MONITORING:"monitoring",ORG_DEPLOY:"organisation-deploy",ORG_DESTROY:"organisation-destroy",CASCADE_PLATFORM:"cascade-platform",CASCADE_DOMAINS:"cascade-domains",CASCADE_ACCOUNTS:"cascade-accounts",NETWORK:"network",STORAGE:"storage",MESSAGING:"messaging",DATABASE:"database",COMPUTE:"compute",CDN:"cdn",NETWORK_DESTROY:"network-destroy",STORAGE_DESTROY:"storage-destroy",MESSAGING_DESTROY:"messaging-destroy",DATABASE_DESTROY:"database-destroy",COMPUTE_DESTROY:"compute-destroy",CDN_DESTROY:"cdn-destroy"},t={AUTH:"Authenticating with AWS",PREPARE_DEPLOY:"Preparing deployment",PREPARE_DESTROY:"Preparing destruction",BOOTSTRAP:"Bootstrapping AWS environment",CHECK_INFRA_STATE:"Checking infrastructure state"},e=["Connect securely","Prepare environment","Deploy infrastructure","Enable monitoring"],o={CONNECT:e[0],PREPARE:e[1],DEPLOY:e[2],MONITORING:e[3]};export{e as INFRASTRUCTURE_STEP_NAMES,o as INFRA_STEP_NAME,E as STEP_IDS,t as STEP_NAMES};
|
|
1
|
+
const E={AUTH:"auth",DETECT_CONFIG:"detect-config",BOOTSTRAP:"bootstrap",CFN_CHECK:"cfn-check",DIFF:"diff",DEPLOY:"deploy",DESTROY:"destroy",VERIFY:"verify",CDK_SYNTH:"cdk-synth",DOCKER_OPERATIONS:"docker-operations",ECR_INIT:"ecr-init",DOCKER_DEPLOY:"docker-deploy",TAG_ECR_IMAGES:"tag-ecr-images",ORG_SETUP:"org-setup",ORG_ENSURE:"org-ensure",ORG_POLICY_TYPES:"policy-types",ORG_SERVICE_ACCESS:"service-access",ORG_CREATE_ACCOUNTS:"create-accounts",ORG_ENSURE_OUS:"ensure-ous",ORG_PLACE_ACCOUNTS:"place-accounts",IDENTITY_CENTRE:"identity-centre",ORG_ACCOUNTS:"accounts",ORG_COST_TAGS:"cost-tags",ORG_PROFILES:"profiles",PREPARE:"prepare",PREPARE_ENVIRONMENT:"prepare-environment",PLATFORM_ACCOUNT:"platform-account",ACCOUNT_CONTEXT:"account-context",CONNECT:"connect",MONITORING:"monitoring",ORG_DEPLOY:"organisation-deploy",ORG_DESTROY:"organisation-destroy",CASCADE_PLATFORM:"cascade-platform",CASCADE_DOMAINS:"cascade-domains",CASCADE_ACCOUNTS:"cascade-accounts",NETWORK:"network",STORAGE:"storage",MESSAGING:"messaging",DATABASE:"database",COMPUTE:"compute",CDN:"cdn",NETWORK_DESTROY:"network-destroy",STORAGE_DESTROY:"storage-destroy",MESSAGING_DESTROY:"messaging-destroy",DATABASE_DESTROY:"database-destroy",COMPUTE_DESTROY:"compute-destroy",CDN_DESTROY:"cdn-destroy"},t={AUTH:"Authenticating with AWS",PREPARE_DEPLOY:"Preparing deployment",PREPARE_DESTROY:"Preparing destruction",BOOTSTRAP:"Bootstrapping AWS environment",CHECK_INFRA_STATE:"Checking infrastructure state",ORG_SETUP:"Configuring organisation",ORG_DEPLOY:"Deploying organisation",CASCADE_PLATFORM:"Deploying platform",CASCADE_ACCOUNTS:"Deploying accounts"},e=["Connect securely","Prepare environment","Deploy infrastructure","Enable monitoring"],o={CONNECT:e[0],PREPARE:e[1],DEPLOY:e[2],MONITORING:e[3]};export{e as INFRASTRUCTURE_STEP_NAMES,o as INFRA_STEP_NAME,E as STEP_IDS,t as STEP_NAMES};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fjall/deploy-core",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.6.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,14 +73,15 @@
|
|
|
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": "^
|
|
76
|
+
"@fjall/generator": "^2.6.0",
|
|
77
|
+
"@fjall/util": "^2.6.0",
|
|
78
78
|
"@smithy/node-http-handler": "^4.6.1",
|
|
79
|
+
"tsx": "^4.21.0",
|
|
79
80
|
"zod": "^4.4.3"
|
|
80
81
|
},
|
|
81
82
|
"devDependencies": {
|
|
82
83
|
"@types/node": "^25.6.0",
|
|
83
84
|
"vitest": "^4.1.5"
|
|
84
85
|
},
|
|
85
|
-
"gitHead": "
|
|
86
|
+
"gitHead": "93666ff94b8b1d0e360a7710e9266d275d15ee34"
|
|
86
87
|
}
|