@fjall/deploy-core 2.20.0 → 2.21.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/index.d.ts +1 -1
- package/dist/src/aws/index.js +1 -1
- package/dist/src/aws/targetReadiness.d.ts +9 -0
- package/dist/src/aws/targetReadiness.js +1 -1
- package/dist/src/index.d.ts +1 -1
- package/dist/src/index.js +1 -1
- package/dist/src/orchestration/application/applicationDeploy.js +1 -1
- package/dist/src/orchestration/application/codeOnlyDeploy.d.ts +15 -0
- package/dist/src/orchestration/application/codeOnlyDeploy.js +1 -1
- package/dist/src/orchestration/application/dockerBuildHelper.d.ts +2 -2
- package/dist/src/orchestration/application/dockerBuildHelper.js +1 -1
- package/dist/src/orchestration/dockerInterface.d.ts +9 -0
- package/package.json +4 -4
package/dist/.minified
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
153 files minified at 2026-06-
|
|
1
|
+
153 files minified at 2026-06-27T21:57:29.114Z
|
package/dist/src/aws/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export type { AwsProvider, AwsProviderCredentials, AwsSdkClientConstructor } from "./AwsProvider.js";
|
|
2
2
|
export { SimpleAwsProvider } from "./SimpleAwsProvider.js";
|
|
3
|
-
export { checkTargetReadiness, describeTargetReadinessReason, buildTargetUnreadyAdvisory } from "./targetReadiness.js";
|
|
3
|
+
export { checkTargetReadiness, describeTargetReadinessReason, buildTargetUnreadyAdvisory, probeAccountStackExists } from "./targetReadiness.js";
|
|
4
4
|
export type { TargetReadinessAccount, TargetReadinessClients, TargetReadinessReason, TargetReadinessVerdict } from "./targetReadiness.js";
|
|
5
5
|
export { buildTargetSetUnreadyAdvisory } from "./targetSetAdvisory.js";
|
|
6
6
|
export type { TargetSetUnreadyAdvisoryParams } from "./targetSetAdvisory.js";
|
package/dist/src/aws/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{SimpleAwsProvider as a}from"./SimpleAwsProvider.js";import{checkTargetReadiness as s,describeTargetReadinessReason as o,buildTargetUnreadyAdvisory as n}from"./targetReadiness.js";import{buildTargetSetUnreadyAdvisory as
|
|
1
|
+
import{SimpleAwsProvider as a}from"./SimpleAwsProvider.js";import{checkTargetReadiness as s,describeTargetReadinessReason as o,buildTargetUnreadyAdvisory as n,probeAccountStackExists as i}from"./targetReadiness.js";import{buildTargetSetUnreadyAdvisory as l}from"./targetSetAdvisory.js";import{ensureOrganisationExists as A,describeOrganisation as u,enablePolicyTypes as S,enableServiceAccess as E,SERVICE_PRINCIPALS as b,enableRamSharing as g,activateTrustedAccess as p,enableIpamDelegatedAdmin as R,updateBackupGlobalSettings as m,listAccounts as C,findAccount as T,createAccount as I,ensureOrganisationalUnitsExist as x,placeAccountsInOUs as y,buildAccountToOUMap as O,activateCostAllocationTags as U,checkIdentityCentreStatus as f,extractErrorName as v,isOULeaf as P,registerSecurityDelegates as _,SECURITY_SERVICE_PRINCIPALS as k,enableCentralisedRootAccess as h,ROOT_ACCESS_FEATURES as L,RootAccessEnablementError as N}from"./organisations/index.js";import{CloudFormationEventMonitor as F}from"./utils/cloudformationEvents.js";export{F as CloudFormationEventMonitor,L as ROOT_ACCESS_FEATURES,N as RootAccessEnablementError,k as SECURITY_SERVICE_PRINCIPALS,b as SERVICE_PRINCIPALS,a as SimpleAwsProvider,U as activateCostAllocationTags,p as activateTrustedAccess,O as buildAccountToOUMap,l as buildTargetSetUnreadyAdvisory,n as buildTargetUnreadyAdvisory,f as checkIdentityCentreStatus,s as checkTargetReadiness,I as createAccount,u as describeOrganisation,o as describeTargetReadinessReason,h as enableCentralisedRootAccess,R as enableIpamDelegatedAdmin,S as enablePolicyTypes,g as enableRamSharing,E as enableServiceAccess,A as ensureOrganisationExists,x as ensureOrganisationalUnitsExist,v as extractErrorName,T as findAccount,P as isOULeaf,C as listAccounts,y as placeAccountsInOUs,i as probeAccountStackExists,_ as registerSecurityDelegates,m as updateBackupGlobalSettings};
|
|
@@ -52,6 +52,15 @@ export declare function buildTargetUnreadyAdvisory(params: {
|
|
|
52
52
|
reasons: TargetReadinessReason[];
|
|
53
53
|
providerAccounts?: OrgConfig["providerAccounts"];
|
|
54
54
|
}): string;
|
|
55
|
+
/**
|
|
56
|
+
* Probe whether the `Account` CloudFormation stack exists and is provisioned
|
|
57
|
+
* in `region`. `success(true)` = a usable stack; `success(false)` = absent or
|
|
58
|
+
* an unprovisioned shell (failed/rolled-back create, unexecuted change-set,
|
|
59
|
+
* in-flight/failed delete); `failure` = the describe itself errored. Exported
|
|
60
|
+
* so connect-time deploy-state probes reuse the same unprovisioned-status
|
|
61
|
+
* interpretation as the deploy readiness path rather than re-deriving it.
|
|
62
|
+
*/
|
|
63
|
+
export declare function probeAccountStackExists(client: CloudFormationClient, region: string, abortSignal?: AbortSignal): Promise<Result<boolean>>;
|
|
55
64
|
/**
|
|
56
65
|
* Probe whether a deploy target is provisioned before any AWS mutation:
|
|
57
66
|
* the fixed-name Account stack must exist in the target region (both shapes),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{DescribeStacksCommand as N}from"@aws-sdk/client-cloudformation";import{DescribeIpamPoolsCommand as S}from"@aws-sdk/client-ec2";import{success as i,failure as c}from"@fjall/generator";import{accountTier as A,generateTargetName as g,getErrorMessage as E}from"@fjall/util";import{formatIpamPairTagValue as y,IPAM_OPERATIONS_POOL_TAG_KEY as O,STACK_NOT_FOUND_PATTERN as k}from"@fjall/util/aws";import{getOrganisationStackName as _,ORGANISATION_TYPES as R}from"../types/operations.js";import{hasOrganisationTierAccount as T}from"./organisations/accountGlobals.js";import{composeSdkAbortSignal as I,isAborted as P}from"./organisations/types.js";const m=_(R.ACCOUNT),w=new Set(["CREATE_FAILED","ROLLBACK_IN_PROGRESS","ROLLBACK_COMPLETE","ROLLBACK_FAILED","REVIEW_IN_PROGRESS","DELETE_IN_PROGRESS","DELETE_FAILED"]);function $(e){switch(e.kind){case"missing-account-stack":return`the ${e.stackName} stack was not found in ${e.region}`;case"missing-ipam-pool":return`no IPAM pool exists for account ${e.accountId} in ${e.region}`}}function L(e){const t=e.accountName??e.accountId,n=e.reasons.map($).join(" and "),o=e.accountName!==void 0?`run \`fjall target set ${g(e.accountName,e.region)}\` then \`fjall deploy account\`, then retry this deploy`:"run `fjall target list` to find the target name, `fjall target set <target>`, then `fjall deploy account`, then retry this deploy",r=T(e.providerAccounts)?"run `fjall deploy organisation` (the cascade provisions every account and creates the IPAM pool), then retry this deploy":o;return`Target "${t}" is not ready in ${e.region}: ${n}. Nothing has been deployed. Provision the target first: ${r}.`}function b(e){return e instanceof Error&&e.name==="ValidationError"&&e.message.includes(k)}async function C(e,t,n){try{const r=(await e.send(new N({StackName:m}),{abortSignal:I(n)})).Stacks?.[0]?.StackStatus;return i(r!==void 0&&!w.has(r))}catch(o){return b(o)?i(!1):c(new Error(`Failed to probe the ${m} stack in ${t}: ${E(o)}`))}}function
|
|
1
|
+
import{DescribeStacksCommand as N}from"@aws-sdk/client-cloudformation";import{DescribeIpamPoolsCommand as S}from"@aws-sdk/client-ec2";import{success as i,failure as c}from"@fjall/generator";import{accountTier as A,generateTargetName as g,getErrorMessage as E}from"@fjall/util";import{formatIpamPairTagValue as y,IPAM_OPERATIONS_POOL_TAG_KEY as O,STACK_NOT_FOUND_PATTERN as k}from"@fjall/util/aws";import{getOrganisationStackName as _,ORGANISATION_TYPES as R}from"../types/operations.js";import{hasOrganisationTierAccount as T}from"./organisations/accountGlobals.js";import{composeSdkAbortSignal as I,isAborted as P}from"./organisations/types.js";const m=_(R.ACCOUNT),w=new Set(["CREATE_FAILED","ROLLBACK_IN_PROGRESS","ROLLBACK_COMPLETE","ROLLBACK_FAILED","REVIEW_IN_PROGRESS","DELETE_IN_PROGRESS","DELETE_FAILED"]);function $(e){switch(e.kind){case"missing-account-stack":return`the ${e.stackName} stack was not found in ${e.region}`;case"missing-ipam-pool":return`no IPAM pool exists for account ${e.accountId} in ${e.region}`}}function L(e){const t=e.accountName??e.accountId,n=e.reasons.map($).join(" and "),o=e.accountName!==void 0?`run \`fjall target set ${g(e.accountName,e.region)}\` then \`fjall deploy account\`, then retry this deploy`:"run `fjall target list` to find the target name, `fjall target set <target>`, then `fjall deploy account`, then retry this deploy",r=T(e.providerAccounts)?"run `fjall deploy organisation` (the cascade provisions every account and creates the IPAM pool), then retry this deploy":o;return`Target "${t}" is not ready in ${e.region}: ${n}. Nothing has been deployed. Provision the target first: ${r}.`}function b(e){return e instanceof Error&&e.name==="ValidationError"&&e.message.includes(k)}async function C(e,t,n){try{const r=(await e.send(new N({StackName:m}),{abortSignal:I(n)})).Stacks?.[0]?.StackStatus;return i(r!==void 0&&!w.has(r))}catch(o){return b(o)?i(!1):c(new Error(`Failed to probe the ${m} stack in ${t}: ${E(o)}`))}}function x(e,t,n,o){return e.Locale!==n?!1:o.side==="owner"?(e.Tags??[]).some(r=>r.Key===O&&r.Value===o.pairTagValue):e.OwnerId===t?!1:o.platformAccountId===void 0||e.OwnerId===o.platformAccountId}async function v(e,t,n,o,r){try{let a;do{if(P(r))return c(new Error(`Aborted: IPAM pool probe for account ${t} in ${n} cancelled by shutdown signal`));const s=await e.send(new S({...a!==void 0&&{NextToken:a}}),{abortSignal:I(r)});if((s.IpamPools??[]).some(u=>x(u,t,n,o)))return i(!0);a=s.NextToken}while(a!==void 0);return i(!1)}catch(a){return c(new Error(`Failed to probe IPAM pools for account ${t} in ${n}: ${E(a)}`))}}async function B(e,t,n,o,r){const a=[],s=await C(e.cloudFormation,n,r);if(!s.success)return c(s.error);s.data||a.push({kind:"missing-account-stack",stackName:m,region:n});const d=o?.providerAccounts;if(T(d)){const u=(d??[]).find(l=>l.id===t.id),p=(d??[]).find(l=>A(l)==="platform")?.id,h=u!==void 0&&A(u)==="platform"?{side:"owner",pairTagValue:y(t.id,n)}:{side:"consumer",...p!==void 0&&{platformAccountId:p}},f=await v(e.ec2,t.id,n,h,r);if(!f.success)return c(f.error);f.data||a.push({kind:"missing-ipam-pool",accountId:t.id,region:n})}return a.length===0?i({ready:!0}):i({ready:!1,reasons:a,advisory:L({accountId:t.id,...t.name!==void 0&&{accountName:t.name},region:n,reasons:a,...o!==void 0&&{providerAccounts:o.providerAccounts}})})}export{m as ACCOUNT_STACK_NAME,L as buildTargetUnreadyAdvisory,B as checkTargetReadiness,$ as describeTargetReadinessReason,C as probeAccountStackExists};
|
package/dist/src/index.d.ts
CHANGED
|
@@ -16,7 +16,7 @@ export type { DeploymentEvent, DeploymentEventType, DeploymentEventResourceCateg
|
|
|
16
16
|
export type { AwsCredentials, DeployIdentity, DeployCallbacks, StepCompleteStatus, StackCleanupQuarantineDetail, StackCleanupRetainedBucketsDetail, ProgressEvent, ProgressEventType, ResourceEvent, AwsAuthResult, CascadeDeploymentResult, CascadePhase, BuildPushStartEvent, BuildPushProgressEvent, BuildPushCompleteEvent, TaskDefRegisteredEvent, ECSCompleteEvent, MigrationsStartEvent, MigrationsCompleteEvent, TrailMigrationPhase, TrailMigrationStatus, TrailMigrationPhaseEvent, ApiClientInterface, EntitlementsData, DeployParams, DeployOptions, DeploymentType, DeployResult, DestroyParams, DestroyOptions, DestroyResult, OrgConfig, ProviderAccount, RootAccessManagementMode, SSOSession, Entitlements } from "./types/index.js";
|
|
17
17
|
export type { AwsProvider, AwsProviderCredentials, AwsSdkClientConstructor } from "./aws/index.js";
|
|
18
18
|
export { SimpleAwsProvider } from "./aws/index.js";
|
|
19
|
-
export { checkTargetReadiness, describeTargetReadinessReason, buildTargetUnreadyAdvisory, buildTargetSetUnreadyAdvisory } from "./aws/index.js";
|
|
19
|
+
export { checkTargetReadiness, describeTargetReadinessReason, buildTargetUnreadyAdvisory, buildTargetSetUnreadyAdvisory, probeAccountStackExists } from "./aws/index.js";
|
|
20
20
|
export type { TargetReadinessAccount, TargetReadinessClients, TargetReadinessReason, TargetReadinessVerdict, TargetSetUnreadyAdvisoryParams } from "./aws/index.js";
|
|
21
21
|
export { ensureOrganisationExists, describeOrganisation, enablePolicyTypes, enableServiceAccess, SERVICE_PRINCIPALS, enableRamSharing, activateTrustedAccess, enableIpamDelegatedAdmin, updateBackupGlobalSettings, listAccounts, findAccount, createAccount, ensureOrganisationalUnitsExist, placeAccountsInOUs, buildAccountToOUMap, activateCostAllocationTags, checkIdentityCentreStatus, extractErrorName, isOULeaf, registerSecurityDelegates, SECURITY_SERVICE_PRINCIPALS, enableCentralisedRootAccess, ROOT_ACCESS_FEATURES, RootAccessEnablementError } from "./aws/index.js";
|
|
22
22
|
export type { OrgDetails, OUMap, OUTree, AccountPlacementResult, AccountInfo, IdentityCentreStatus, BackupGlobalSettings, CostAllocationTag, CreateAccountResult, RootAccessFeature, RootAccessEnablementSummary } from "./aws/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 i,DEPLOYMENT_EVENT_STATUS_REASON_MAX as a,DEPLOYMENT_EVENT_ERROR_MESSAGE_MAX as s,DEPLOYMENT_EVENT_TRAIL_DETAIL_MAX as n,CASCADE_PHASES as c,CASCADE_ACCOUNT_STATUSES as E}from"./types/index.js";import{SimpleAwsProvider as l}from"./aws/index.js";import{checkTargetReadiness as T,describeTargetReadinessReason as p,buildTargetUnreadyAdvisory as u,buildTargetSetUnreadyAdvisory as _}from"./aws/index.js";import{ensureOrganisationExists as R,describeOrganisation as m,enablePolicyTypes as d,enableServiceAccess as P,SERVICE_PRINCIPALS as C,enableRamSharing as N,activateTrustedAccess as f,enableIpamDelegatedAdmin as g,updateBackupGlobalSettings as x,listAccounts as D,findAccount as I,createAccount as L,ensureOrganisationalUnitsExist as U,placeAccountsInOUs as k,buildAccountToOUMap as y,activateCostAllocationTags as v,checkIdentityCentreStatus as M,extractErrorName as F,isOULeaf as Y,registerSecurityDelegates as b,SECURITY_SERVICE_PRINCIPALS as B,enableCentralisedRootAccess as h,ROOT_ACCESS_FEATURES as K,RootAccessEnablementError as G}from"./aws/index.js";import{STEP_IDS as H,STEP_NAMES as X,INFRASTRUCTURE_STEP_NAMES as w,INFRA_STEP_NAME as j}from"./types/index.js";import{ProgressReporter as q,APPLICATION_STACKS as z,ORGANISATION_TYPES as J,APPLICATION_DEPLOY_ORDER as W,APPLICATION_DESTROY_ORDER as Z,OPENNEXT_DEPLOY_ORDER as $,OPENNEXT_DESTROY_ORDER as ee,PARALLEL_DEPLOY_GROUPS as re,PARALLEL_DESTROY_GROUPS as te,OPENNEXT_PARALLEL_GROUPS as oe,PARALLEL_OPERATION_TYPES as ie,isApplicationOperation as ae,isOrganisationOperation as se,getParallelDeployGroups as ne,getParallelDestroyGroups as ce,getApplicationDeployOrder as Ee,getApplicationDestroyOrder as Se,getApplicationStackName as le,getOrganisationStackName as Ae,isApplicationStack as Te,isQuarantineDetail as pe,isRetainedBucketsDetail as ue,getApplicationStepName as _e,getApplicationStepId as Oe,toPascalCase as Re,isOpenNextPattern as me,OPENNEXT_PATTERNS as de,deriveResourcesFromManifestStacks as Pe,STACK_NOT_FOUND_PATTERN as Ce,STACK_FAILED_STATE_PATTERN as Ne,CDK_NO_STACKS_MATCH as fe,INFRASTRUCTURE_FILENAME as ge,ApplicationError as xe,wrapApplicationError as De,FjallStateFileSchema as Ie,readStateFile as Le,writeStateFile as Ue,createEmptyState as ke,deleteStateFile as ye,updateTemplateHash as ve,getStateFilePath as Me,stubCallerIdentity as Fe}from"./types/index.js";import{CloudFormationEventMonitor as be}from"./aws/index.js";import{CdkService as he,CdkArgumentBuilder as Ke,CdkProcessManager as Ge,CdkEventMonitor as Ve,startStackMonitoring as He,DEFAULT_DEPLOY_TIMEOUT_MS as Xe,isCdkError as we,formatInfrastructureError as je,getStructuralHint as Qe,getSourceContext as qe,hasCdkDifferences as ze,parseDiffOutput as Je,CloudFormationService as We,CloudFormationError as Ze,EcsService as $e,EcsError as er,EcsServiceResolver as rr,TemplateHashService as tr,TemplateHashError as or,CdkContextBuilder as ir,emitProgress as ar,PROGRESS_MESSAGES as sr,parseBuildPhase as nr,buildStepContextBuildConfig as cr,convertCloudFormationOutputsToRecord as Er,ApplicationStackService as Sr}from"./services/index.js";import{CdkError as Ar}from"./types/errors/index.js";import{BaseServiceError as pr,ValidationError as ur,AuthError as _r,AwsError as Or,DeploymentError as Rr,NetworkError as mr,FileSystemError as dr,ConfigError as Pr,toServiceError as Cr}from"./types/errors/index.js";import{filterDangerousEnvVars as fr,maskSensitiveOutput as gr,parseShellArgs as xr,sleep as Dr}from"@fjall/util";import{hasDockerfile as Lr}from"./util/dockerfileDetection.js";import{createSequencedCallbacks as kr}from"./util/sequencedCallbacks.js";import{fileExists as vr}from"@fjall/util/fsHelpers";import{success as Fr,failure as Yr,isSuccess as br,isFailure as Br}from"@fjall/generator";import{deploy as Kr}from"./orchestration/index.js";import{destroy as Vr}from"./orchestration/index.js";import{partitionAccounts as Xr}from"./orchestration/index.js";import{buildRegionList as jr,buildAccountRegionPairs as Qr,cascadeHomeRegion as qr,cascadeOperationKey as zr}from"./orchestration/index.js";import{projectScalarSummary as Wr,projectAccountRows as Zr}from"./orchestration/index.js";import{reconcileProviderAccounts as et,mergeReconciledProviderAccounts as rt}from"./orchestration/index.js";import{decideNextTransition as ot,reconcileTrailMigration as it,decommissionMemberTrailStorage as at,ORG_TRAIL_BUCKET_OUTPUT_KEY as st,TRAIL_BUCKET_OUTPUT_KEY as nt,TRAIL_KEY_ARN_OUTPUT_KEY as ct}from"./orchestration/index.js";import{unlockBucket as St,unlockQueue as lt}from"./orchestration/index.js";import{triageBucketPolicy as Tt,isEnforceSslStatement as pt,toResourcePolicyStatements as ut,restoreBucketPolicy as _t,synthesiseEnforceSslDocument as Ot,ensureEnforceSsl as Rt,restoreAndReconcileQuarantinedBucket as mt}from"./orchestration/index.js";import{verifyOrgTrailDelivery as Pt}from"./aws/cloudtrail/orgTrailDelivery.js";import{assumeRootForTask as Nt,isRootTaskPolicyArn as ft,ROOT_TASK_POLICY_ARNS as gt,MAX_ROOT_SESSION_SECONDS as xt}from"./aws/sts/assumeRoot.js";import{parseAccountsConfiguration as It,flattenAccountsToEnvironments as Lt,extractAllAccountNames as Ut,accountsConfigToOUTree as kt,isStringArray as yt,isAccountsConfig as vt,isOuOnlyAccountBucket as Mt,OU_ONLY_ACCOUNT_BUCKETS as Ft}from"./orchestration/index.js";import{runOpenNextBuild as bt}from"./orchestration/index.js";import{runOrganisationSetup as ht,ORG_SETUP_PHASES as Kt}from"./orchestration/index.js";import{FrameworkRegistry as Vt}from"./orchestration/index.js";import{openNextBuilder as Xt,dockerBuilder as wt}from"./orchestration/index.js";import{StepRegistry as Qt,getDestroyStepId as qt}from"./steps/index.js";export{W as APPLICATION_DEPLOY_ORDER,Z as APPLICATION_DESTROY_ORDER,z as APPLICATION_STACKS,xe as ApplicationError,Sr as ApplicationStackService,_r as AuthError,Or as AwsError,pr as BaseServiceError,E as CASCADE_ACCOUNT_STATUSES,c as CASCADE_PHASES,fe as CDK_NO_STACKS_MATCH,Ke as CdkArgumentBuilder,ir as CdkContextBuilder,Ar as CdkError,Ve as CdkEventMonitor,Ge as CdkProcessManager,he as CdkService,Ze as CloudFormationError,be as CloudFormationEventMonitor,We as CloudFormationService,Pr as ConfigError,Xe as DEFAULT_DEPLOY_TIMEOUT_MS,s as DEPLOYMENT_EVENT_ERROR_MESSAGE_MAX,i as DEPLOYMENT_EVENT_RESOURCE_CATEGORIES,a as DEPLOYMENT_EVENT_STATUS_REASON_MAX,n as DEPLOYMENT_EVENT_TRAIL_DETAIL_MAX,o as DEPLOYMENT_EVENT_TYPES,Rr as DeploymentError,t as DeploymentEventSchema,er as EcsError,$e as EcsService,rr as EcsServiceResolver,dr as FileSystemError,Ie as FjallStateFileSchema,Vt as FrameworkRegistry,ge as INFRASTRUCTURE_FILENAME,w as INFRASTRUCTURE_STEP_NAMES,j as INFRA_STEP_NAME,xt as MAX_ROOT_SESSION_SECONDS,mr as NetworkError,$ as OPENNEXT_DEPLOY_ORDER,ee as OPENNEXT_DESTROY_ORDER,oe as OPENNEXT_PARALLEL_GROUPS,de as OPENNEXT_PATTERNS,J as ORGANISATION_TYPES,Kt as ORG_SETUP_PHASES,st as ORG_TRAIL_BUCKET_OUTPUT_KEY,Ft as OU_ONLY_ACCOUNT_BUCKETS,re as PARALLEL_DEPLOY_GROUPS,te as PARALLEL_DESTROY_GROUPS,ie as PARALLEL_OPERATION_TYPES,sr as PROGRESS_MESSAGES,q as ProgressReporter,K as ROOT_ACCESS_FEATURES,gt as ROOT_TASK_POLICY_ARNS,G as RootAccessEnablementError,B as SECURITY_SERVICE_PRINCIPALS,C as SERVICE_PRINCIPALS,Ne as STACK_FAILED_STATE_PATTERN,Ce as STACK_NOT_FOUND_PATTERN,H as STEP_IDS,X as STEP_NAMES,l as SimpleAwsProvider,Qt as StepRegistry,nt as TRAIL_BUCKET_OUTPUT_KEY,ct as TRAIL_KEY_ARN_OUTPUT_KEY,or as TemplateHashError,tr as TemplateHashService,ur as ValidationError,kt as accountsConfigToOUTree,v as activateCostAllocationTags,f as activateTrustedAccess,Nt as assumeRootForTask,Qr as buildAccountRegionPairs,y as buildAccountToOUMap,jr as buildRegionList,cr as buildStepContextBuildConfig,_ as buildTargetSetUnreadyAdvisory,u as buildTargetUnreadyAdvisory,qr as cascadeHomeRegion,zr as cascadeOperationKey,M as checkIdentityCentreStatus,T as checkTargetReadiness,Er as convertCloudFormationOutputsToRecord,L as createAccount,ke as createEmptyState,kr as createSequencedCallbacks,ot as decideNextTransition,at as decommissionMemberTrailStorage,ye as deleteStateFile,Kr as deploy,Pe as deriveResourcesFromManifestStacks,m as describeOrganisation,p as describeTargetReadinessReason,Vr as destroy,wt as dockerBuilder,ar as emitProgress,h as enableCentralisedRootAccess,g as enableIpamDelegatedAdmin,d as enablePolicyTypes,N as enableRamSharing,P as enableServiceAccess,Rt as ensureEnforceSsl,R as ensureOrganisationExists,U as ensureOrganisationalUnitsExist,Ut as extractAllAccountNames,F as extractErrorName,Yr as failure,vr as fileExists,fr as filterDangerousEnvVars,I as findAccount,Lt as flattenAccountsToEnvironments,je as formatInfrastructureError,Ee as getApplicationDeployOrder,Se as getApplicationDestroyOrder,le as getApplicationStackName,Oe as getApplicationStepId,_e as getApplicationStepName,qt as getDestroyStepId,Ae as getOrganisationStackName,ne as getParallelDeployGroups,ce as getParallelDestroyGroups,qe as getSourceContext,Me as getStateFilePath,Qe as getStructuralHint,ze as hasCdkDifferences,Lr as hasDockerfile,vt as isAccountsConfig,ae as isApplicationOperation,Te as isApplicationStack,we as isCdkError,pt as isEnforceSslStatement,Br as isFailure,Y as isOULeaf,me as isOpenNextPattern,se as isOrganisationOperation,Mt as isOuOnlyAccountBucket,pe as isQuarantineDetail,ue as isRetainedBucketsDetail,ft as isRootTaskPolicyArn,yt as isStringArray,br as isSuccess,D as listAccounts,gr as maskSensitiveOutput,rt as mergeReconciledProviderAccounts,Xt as openNextBuilder,It as parseAccountsConfiguration,nr as parseBuildPhase,Je as parseDiffOutput,xr as parseShellArgs,Xr as partitionAccounts,k as placeAccountsInOUs,Zr as projectAccountRows,Wr as projectScalarSummary,Le as readStateFile,et as reconcileProviderAccounts,it as reconcileTrailMigration,b as registerSecurityDelegates,mt as restoreAndReconcileQuarantinedBucket,_t as restoreBucketPolicy,bt as runOpenNextBuild,ht as runOrganisationSetup,Dr as sleep,He as startStackMonitoring,Fe as stubCallerIdentity,Fr as success,Ot as synthesiseEnforceSslDocument,Re as toPascalCase,ut as toResourcePolicyStatements,Cr as toServiceError,Tt as triageBucketPolicy,St as unlockBucket,lt as unlockQueue,x as updateBackupGlobalSettings,ve as updateTemplateHash,Pt as verifyOrgTrailDelivery,De as wrapApplicationError,Ue as writeStateFile};
|
|
1
|
+
import{DeploymentEventSchema as t,DEPLOYMENT_EVENT_TYPES as o,DEPLOYMENT_EVENT_RESOURCE_CATEGORIES as i,DEPLOYMENT_EVENT_STATUS_REASON_MAX as a,DEPLOYMENT_EVENT_ERROR_MESSAGE_MAX as s,DEPLOYMENT_EVENT_TRAIL_DETAIL_MAX as c,CASCADE_PHASES as n,CASCADE_ACCOUNT_STATUSES as E}from"./types/index.js";import{SimpleAwsProvider as l}from"./aws/index.js";import{checkTargetReadiness as T,describeTargetReadinessReason as p,buildTargetUnreadyAdvisory as u,buildTargetSetUnreadyAdvisory as _,probeAccountStackExists as O}from"./aws/index.js";import{ensureOrganisationExists as m,describeOrganisation as d,enablePolicyTypes as P,enableServiceAccess as C,SERVICE_PRINCIPALS as N,enableRamSharing as f,activateTrustedAccess as g,enableIpamDelegatedAdmin as x,updateBackupGlobalSettings as D,listAccounts as I,findAccount as L,createAccount as U,ensureOrganisationalUnitsExist as k,placeAccountsInOUs as y,buildAccountToOUMap as v,activateCostAllocationTags as M,checkIdentityCentreStatus as F,extractErrorName as Y,isOULeaf as b,registerSecurityDelegates as B,SECURITY_SERVICE_PRINCIPALS as h,enableCentralisedRootAccess as K,ROOT_ACCESS_FEATURES as G,RootAccessEnablementError as V}from"./aws/index.js";import{STEP_IDS as X,STEP_NAMES as w,INFRASTRUCTURE_STEP_NAMES as j,INFRA_STEP_NAME as Q}from"./types/index.js";import{ProgressReporter as z,APPLICATION_STACKS as J,ORGANISATION_TYPES as W,APPLICATION_DEPLOY_ORDER as Z,APPLICATION_DESTROY_ORDER as $,OPENNEXT_DEPLOY_ORDER as ee,OPENNEXT_DESTROY_ORDER as re,PARALLEL_DEPLOY_GROUPS as te,PARALLEL_DESTROY_GROUPS as oe,OPENNEXT_PARALLEL_GROUPS as ie,PARALLEL_OPERATION_TYPES as ae,isApplicationOperation as se,isOrganisationOperation as ce,getParallelDeployGroups as ne,getParallelDestroyGroups as Ee,getApplicationDeployOrder as Se,getApplicationDestroyOrder as le,getApplicationStackName as Ae,getOrganisationStackName as Te,isApplicationStack as pe,isQuarantineDetail as ue,isRetainedBucketsDetail as _e,getApplicationStepName as Oe,getApplicationStepId as Re,toPascalCase as me,isOpenNextPattern as de,OPENNEXT_PATTERNS as Pe,deriveResourcesFromManifestStacks as Ce,STACK_NOT_FOUND_PATTERN as Ne,STACK_FAILED_STATE_PATTERN as fe,CDK_NO_STACKS_MATCH as ge,INFRASTRUCTURE_FILENAME as xe,ApplicationError as De,wrapApplicationError as Ie,FjallStateFileSchema as Le,readStateFile as Ue,writeStateFile as ke,createEmptyState as ye,deleteStateFile as ve,updateTemplateHash as Me,getStateFilePath as Fe,stubCallerIdentity as Ye}from"./types/index.js";import{CloudFormationEventMonitor as Be}from"./aws/index.js";import{CdkService as Ke,CdkArgumentBuilder as Ge,CdkProcessManager as Ve,CdkEventMonitor as He,startStackMonitoring as Xe,DEFAULT_DEPLOY_TIMEOUT_MS as we,isCdkError as je,formatInfrastructureError as Qe,getStructuralHint as qe,getSourceContext as ze,hasCdkDifferences as Je,parseDiffOutput as We,CloudFormationService as Ze,CloudFormationError as $e,EcsService as er,EcsError as rr,EcsServiceResolver as tr,TemplateHashService as or,TemplateHashError as ir,CdkContextBuilder as ar,emitProgress as sr,PROGRESS_MESSAGES as cr,parseBuildPhase as nr,buildStepContextBuildConfig as Er,convertCloudFormationOutputsToRecord as Sr,ApplicationStackService as lr}from"./services/index.js";import{CdkError as Tr}from"./types/errors/index.js";import{BaseServiceError as ur,ValidationError as _r,AuthError as Or,AwsError as Rr,DeploymentError as mr,NetworkError as dr,FileSystemError as Pr,ConfigError as Cr,toServiceError as Nr}from"./types/errors/index.js";import{filterDangerousEnvVars as gr,maskSensitiveOutput as xr,parseShellArgs as Dr,sleep as Ir}from"@fjall/util";import{hasDockerfile as Ur}from"./util/dockerfileDetection.js";import{createSequencedCallbacks as yr}from"./util/sequencedCallbacks.js";import{fileExists as Mr}from"@fjall/util/fsHelpers";import{success as Yr,failure as br,isSuccess as Br,isFailure as hr}from"@fjall/generator";import{deploy as Gr}from"./orchestration/index.js";import{destroy as Hr}from"./orchestration/index.js";import{partitionAccounts as wr}from"./orchestration/index.js";import{buildRegionList as Qr,buildAccountRegionPairs as qr,cascadeHomeRegion as zr,cascadeOperationKey as Jr}from"./orchestration/index.js";import{projectScalarSummary as Zr,projectAccountRows as $r}from"./orchestration/index.js";import{reconcileProviderAccounts as rt,mergeReconciledProviderAccounts as tt}from"./orchestration/index.js";import{decideNextTransition as it,reconcileTrailMigration as at,decommissionMemberTrailStorage as st,ORG_TRAIL_BUCKET_OUTPUT_KEY as ct,TRAIL_BUCKET_OUTPUT_KEY as nt,TRAIL_KEY_ARN_OUTPUT_KEY as Et}from"./orchestration/index.js";import{unlockBucket as lt,unlockQueue as At}from"./orchestration/index.js";import{triageBucketPolicy as pt,isEnforceSslStatement as ut,toResourcePolicyStatements as _t,restoreBucketPolicy as Ot,synthesiseEnforceSslDocument as Rt,ensureEnforceSsl as mt,restoreAndReconcileQuarantinedBucket as dt}from"./orchestration/index.js";import{verifyOrgTrailDelivery as Ct}from"./aws/cloudtrail/orgTrailDelivery.js";import{assumeRootForTask as ft,isRootTaskPolicyArn as gt,ROOT_TASK_POLICY_ARNS as xt,MAX_ROOT_SESSION_SECONDS as Dt}from"./aws/sts/assumeRoot.js";import{parseAccountsConfiguration as Lt,flattenAccountsToEnvironments as Ut,extractAllAccountNames as kt,accountsConfigToOUTree as yt,isStringArray as vt,isAccountsConfig as Mt,isOuOnlyAccountBucket as Ft,OU_ONLY_ACCOUNT_BUCKETS as Yt}from"./orchestration/index.js";import{runOpenNextBuild as Bt}from"./orchestration/index.js";import{runOrganisationSetup as Kt,ORG_SETUP_PHASES as Gt}from"./orchestration/index.js";import{FrameworkRegistry as Ht}from"./orchestration/index.js";import{openNextBuilder as wt,dockerBuilder as jt}from"./orchestration/index.js";import{StepRegistry as qt,getDestroyStepId as zt}from"./steps/index.js";export{Z as APPLICATION_DEPLOY_ORDER,$ as APPLICATION_DESTROY_ORDER,J as APPLICATION_STACKS,De as ApplicationError,lr as ApplicationStackService,Or as AuthError,Rr as AwsError,ur as BaseServiceError,E as CASCADE_ACCOUNT_STATUSES,n as CASCADE_PHASES,ge as CDK_NO_STACKS_MATCH,Ge as CdkArgumentBuilder,ar as CdkContextBuilder,Tr as CdkError,He as CdkEventMonitor,Ve as CdkProcessManager,Ke as CdkService,$e as CloudFormationError,Be as CloudFormationEventMonitor,Ze as CloudFormationService,Cr as ConfigError,we as DEFAULT_DEPLOY_TIMEOUT_MS,s as DEPLOYMENT_EVENT_ERROR_MESSAGE_MAX,i as DEPLOYMENT_EVENT_RESOURCE_CATEGORIES,a as DEPLOYMENT_EVENT_STATUS_REASON_MAX,c as DEPLOYMENT_EVENT_TRAIL_DETAIL_MAX,o as DEPLOYMENT_EVENT_TYPES,mr as DeploymentError,t as DeploymentEventSchema,rr as EcsError,er as EcsService,tr as EcsServiceResolver,Pr as FileSystemError,Le as FjallStateFileSchema,Ht as FrameworkRegistry,xe as INFRASTRUCTURE_FILENAME,j as INFRASTRUCTURE_STEP_NAMES,Q as INFRA_STEP_NAME,Dt as MAX_ROOT_SESSION_SECONDS,dr as NetworkError,ee as OPENNEXT_DEPLOY_ORDER,re as OPENNEXT_DESTROY_ORDER,ie as OPENNEXT_PARALLEL_GROUPS,Pe as OPENNEXT_PATTERNS,W as ORGANISATION_TYPES,Gt as ORG_SETUP_PHASES,ct as ORG_TRAIL_BUCKET_OUTPUT_KEY,Yt as OU_ONLY_ACCOUNT_BUCKETS,te as PARALLEL_DEPLOY_GROUPS,oe as PARALLEL_DESTROY_GROUPS,ae as PARALLEL_OPERATION_TYPES,cr as PROGRESS_MESSAGES,z as ProgressReporter,G as ROOT_ACCESS_FEATURES,xt as ROOT_TASK_POLICY_ARNS,V as RootAccessEnablementError,h as SECURITY_SERVICE_PRINCIPALS,N as SERVICE_PRINCIPALS,fe as STACK_FAILED_STATE_PATTERN,Ne as STACK_NOT_FOUND_PATTERN,X as STEP_IDS,w as STEP_NAMES,l as SimpleAwsProvider,qt as StepRegistry,nt as TRAIL_BUCKET_OUTPUT_KEY,Et as TRAIL_KEY_ARN_OUTPUT_KEY,ir as TemplateHashError,or as TemplateHashService,_r as ValidationError,yt as accountsConfigToOUTree,M as activateCostAllocationTags,g as activateTrustedAccess,ft as assumeRootForTask,qr as buildAccountRegionPairs,v as buildAccountToOUMap,Qr as buildRegionList,Er as buildStepContextBuildConfig,_ as buildTargetSetUnreadyAdvisory,u as buildTargetUnreadyAdvisory,zr as cascadeHomeRegion,Jr as cascadeOperationKey,F as checkIdentityCentreStatus,T as checkTargetReadiness,Sr as convertCloudFormationOutputsToRecord,U as createAccount,ye as createEmptyState,yr as createSequencedCallbacks,it as decideNextTransition,st as decommissionMemberTrailStorage,ve as deleteStateFile,Gr as deploy,Ce as deriveResourcesFromManifestStacks,d as describeOrganisation,p as describeTargetReadinessReason,Hr as destroy,jt as dockerBuilder,sr as emitProgress,K as enableCentralisedRootAccess,x as enableIpamDelegatedAdmin,P as enablePolicyTypes,f as enableRamSharing,C as enableServiceAccess,mt as ensureEnforceSsl,m as ensureOrganisationExists,k as ensureOrganisationalUnitsExist,kt as extractAllAccountNames,Y as extractErrorName,br as failure,Mr as fileExists,gr as filterDangerousEnvVars,L as findAccount,Ut as flattenAccountsToEnvironments,Qe as formatInfrastructureError,Se as getApplicationDeployOrder,le as getApplicationDestroyOrder,Ae as getApplicationStackName,Re as getApplicationStepId,Oe as getApplicationStepName,zt as getDestroyStepId,Te as getOrganisationStackName,ne as getParallelDeployGroups,Ee as getParallelDestroyGroups,ze as getSourceContext,Fe as getStateFilePath,qe as getStructuralHint,Je as hasCdkDifferences,Ur as hasDockerfile,Mt as isAccountsConfig,se as isApplicationOperation,pe as isApplicationStack,je as isCdkError,ut as isEnforceSslStatement,hr as isFailure,b as isOULeaf,de as isOpenNextPattern,ce as isOrganisationOperation,Ft as isOuOnlyAccountBucket,ue as isQuarantineDetail,_e as isRetainedBucketsDetail,gt as isRootTaskPolicyArn,vt as isStringArray,Br as isSuccess,I as listAccounts,xr as maskSensitiveOutput,tt as mergeReconciledProviderAccounts,wt as openNextBuilder,Lt as parseAccountsConfiguration,nr as parseBuildPhase,We as parseDiffOutput,Dr as parseShellArgs,wr as partitionAccounts,y as placeAccountsInOUs,O as probeAccountStackExists,$r as projectAccountRows,Zr as projectScalarSummary,Ue as readStateFile,rt as reconcileProviderAccounts,at as reconcileTrailMigration,B as registerSecurityDelegates,dt as restoreAndReconcileQuarantinedBucket,Ot as restoreBucketPolicy,Bt as runOpenNextBuild,Kt as runOrganisationSetup,Ir as sleep,Xe as startStackMonitoring,Ye as stubCallerIdentity,Yr as success,Rt as synthesiseEnforceSslDocument,me as toPascalCase,_t as toResourcePolicyStatements,Nr as toServiceError,pt as triageBucketPolicy,lt as unlockBucket,At as unlockQueue,D as updateBackupGlobalSettings,Me as updateTemplateHash,Ct as verifyOrgTrailDelivery,Ie as wrapApplicationError,ke as writeStateFile};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{join as
|
|
1
|
+
import{join as z}from"path";import{CloudFormationClient as Y}from"@aws-sdk/client-cloudformation";import{EC2Client as J}from"@aws-sdk/client-ec2";import{success as j,failure as g}from"@fjall/generator";import{logger as E}from"@fjall/util/logger";import{imageTagParameterName as Q,maskSensitiveOutput as S}from"@fjall/util";import{parseDockerServicesFromManifest as V}from"@fjall/util/manifest";import{stubCallerIdentity as X}from"../../types/deployment/index.js";import{getApplicationDeployOrder as Z,getApplicationStackName as ee,getApplicationStepName as G,getApplicationStepId as K,APPLICATION_STACKS as O}from"../../types/operations.js";import{CdkContextBuilder as te}from"../../services/supporting/CdkContextBuilder.js";import{buildParamsContext as oe,bootstrapOrFail as re}from"../contextHelpers.js";import{runDetectionPipeline as ne}from"./detectionPipeline.js";import{STEP_IDS as R,STEP_NAMES as ie}from"../../types/stepDefinitions.js";import{StepRegistry as ae}from"../../steps/stepRegistry.js";import{checkTargetReadiness as se}from"../../aws/targetReadiness.js";import{cascadeHomeRegion as ce}from"../organisation/cascadeHelpers.js";import{withStepLifecycle as le}from"../stepLifecycle.js";import{DOCKER_BUILD_STEP_NAME as W,runDockerBuild as de}from"./dockerBuildHelper.js";import{getParallelPhase2Stacks as pe,deployParallelPhase as ue,deployStackSequential as fe,runDockerPreCompute as me,createBuildCallbacks as ge}from"./applicationDeployHelpers.js";import{deployCodeOnly as ke}from"./codeOnlyDeploy.js";const T="applicationDeploy";function ye(a){const i=Object.entries(a);if(i.length===0)return;const r={};for(const[t,s]of i){const C=Q(t);r[C]=s}return r}async function Ue(a,i,r){const{callbacks:t,options:s}=a,C=Date.now(),D=te.buildDeploymentContext({deployType:"application",target:r.appName,path:r.path,region:i.awsProvider.getRegion(),callerIdentity:X(i.awsProvider.getAccountId()),...oe({orgConfig:a.orgConfig,identity:a.identity,skipOidc:a.options?.skipOidc})},{verbose:s?.verbose,infraOnly:s?.infraOnly},a.orgConfig),p=i.frameworkRegistry.resolve({appPath:r.path});let w;const k=!!s?.imageTag;if(p&&!k){w=p.builder.plan({appPath:r.path},p.detection);const e=ge(t),o=await p.builder.build(r.path,w,e,{skipBuild:s?.skipBuild,infraOnly:s?.infraOnly});if(!o.success){const n=new Error(S(o.error.message));return t.onError?.(n),g(n)}}if(s?.deployOnly||k){t.onLog?.(k?`Rollback mode \u2014 rolling to image tag ${s?.imageTag}`:"Deploy-only mode \u2014 skipping infrastructure pipeline","info");const e=z(r.path,"cdk.out"),o=V(e),n=o.length>0,d=p?.detection.hasDockerfile===!0,l=n||d;E.debug(T,"Deploy-only branch entered",{isRollback:k,imageTag:s?.imageTag,appName:r.appName,appPath:r.path,cdkOutPath:e,dockerProviderAvailable:a.dockerProvider!==void 0,builderName:p?.builder.name,hasDockerfileFromManifest:n,hasDockerfileFromDisk:d,hasDockerfile:l,manifestDockerServiceCount:o.length,manifestDockerPaths:o.map(m=>m.docker.path)}),!n&&!d&&t.onLog?.("No Dockerfile detected via manifest or appPath \u2014 skipping Docker build. If this app uses a cross-repo Dockerfile, ensure a full deploy has run first to populate cdk.out/fjall-manifest.json.","warn");let u={};if(!k&&a.dockerProvider!==void 0&&l){E.debug(T,"Running Docker build before code-only deploy",{source:n?"manifest":"disk"});const m=await de(a,i,r,t);if(!m.success)return g(m.error);u=m.data}else E.debug(T,"Skipping Docker build",{reason:k?"rollback":a.dockerProvider===void 0?"no dockerProvider":"no Dockerfile detected"});return ke(a,i,r,u)}t.onLog?.("Analysing infrastructure\u2026","info");const N=await ne(r,i,D,t);if(!N.success){const e=new Error(S(N.error.message));return t.onError?.(e),g(e)}const c=N.data;try{await t.onDetectionComplete?.({...c,builderName:p?.builder.name??"unknown"})}catch(e){const o=e instanceof Error?e.message:String(e),n=new Error(S(o));return t.onError?.(n),g(n)}const $={deploymentType:"application",operation:"deploy",deployOnly:!1,infraOnly:s?.infraOnly??!1,hasDockerfile:c.hasDockerfile,pattern:c.pattern,resources:c.resources,...p&&{builderName:p.builder.name}},I=ae.getSteps($),v=I.findIndex(e=>e.id===R.TARGET_READINESS),L=await le(t,{stepId:R.TARGET_READINESS,stepName:ie.TARGET_READINESS,...v>=0&&{stepIndex:v,totalSteps:I.length}},async()=>{if(s?.skipReadinessCheck)return t.onLog?.("Skipping target readiness check (--skip-readiness-check)","warn"),{kind:"skipped",data:void 0};const e=i.awsProvider.getAccountId(),o=a.orgConfig?.providerAccounts.find(u=>u.id===e)?.name,n=i.awsProvider.getCredentials(),d=ce(a.orgConfig),l=await se({cloudFormation:i.awsProvider.getClient(Y),ec2:new J({region:d,...n!==void 0&&{credentials:n}})},{id:e,...o!==void 0&&{name:o}},i.awsProvider.getRegion(),a.orgConfig,a.abortSignal);return l.success?l.data.ready?{kind:"completed",data:void 0}:{kind:"error",error:new Error(S(l.data.advisory))}:{kind:"error",error:new Error(S(l.error.message))}});if(!L.success)return L;const f=w?w.deployOrder:Z({pattern:c.pattern,resources:c.resources}),y=f.length,M=c.hasDockerfile&&a.dockerProvider!==void 0&&f.includes(O.COMPUTE)&&s?.infraOnly!==!0;if(!c.hasDifferences&&!s?.force&&!M){t.onLog?.("No infrastructure changes detected","info"),t.onLog?.(s?.infraOnly===!0?"Infrastructure-only mode \u2014 application code was not rebuilt. Run with --deploy-only to deploy code changes.":"Nothing to deploy. Run with --deploy-only to redeploy application code.","info");const e=c.hasDockerfile&&a.dockerProvider!==void 0&&f.includes(O.COMPUTE);for(let n=0;n<f.length;n++){const d=f[n];e&&d===O.COMPUTE&&(t.onStepStart?.(R.DOCKER_OPERATIONS,W),t.onStepComplete?.(R.DOCKER_OPERATIONS,W,"skipped"));const l=K(d,"deploy"),u=G(d,"deploy");t.onStepStart?.(l,u,n,y),t.onStepComplete?.(l,u,"skipped",n,y)}const o=await i.stackService.resolveWebsiteUrl(r.appName);return j({target:r.appName,deploymentType:"application",outputs:o?{websiteUrl:o}:void 0,noChanges:!0,durationMs:Date.now()-C})}const _=await re(i,D,t);if(!_.success)return _;const b={},P=new Map;for(let e=0;e<f.length;e++){const o=f[e],n=ee(r.appName,o),d=K(o,"deploy"),l=G(o,"deploy"),u=c.stackChanges.get(n)??!0,m=o===O.COMPUTE&&M;if(!u&&!s?.force&&!m){t.onStepStart?.(d,l,e,y),t.onLog?.(`Skipping ${o} \u2014 no changes detected`,"info"),t.onStepComplete?.(d,l,"skipped",e,y);continue}const A=pe(f,e,c.stackChanges,r.appName,s?.force);if(A.length>=2){const H=await ue(A,r,i,D,t,e,y,c,b,P);if(!H.success)return g(H.error);e+=A.length-1;continue}const h=await me(o,a,i,r,t,c.hasDockerfile);if(h!==null&&!h.success)return g(h.error);const q=h!==null&&h.success?h.data.contentHashTagsByService:{},B=ye(q),U=await fe(o,i,D,t,e,y,b,B!==void 0?{parameters:B}:void 0);if(!U.success)return g(U.error);const F=c.currentHashes.get(n);F&&P.set(n,F)}if(P.size>0){const e=await i.hashService.updateStateAfterDeploy(r.path,P);if(!e.success){const o=S(e.error.message);E.debug(T,"Failed to update state file",{error:o}),t.onLog?.(`Warning: failed to update state file \u2014 next deploy may re-deploy unchanged stacks: ${o}`,"warn")}}const x=await i.stackService.resolveWebsiteUrl(r.appName);return x&&(b.websiteUrl=x),j({target:r.appName,deploymentType:"application",outputs:Object.keys(b).length>0?b:void 0,durationMs:Date.now()-C})}export{Ue as deployApplication};
|
|
@@ -1,7 +1,22 @@
|
|
|
1
1
|
import { type Result } from "@fjall/generator";
|
|
2
|
+
import { type ManifestDockerService } from "@fjall/util/manifest";
|
|
2
3
|
import type { DeployParams, DeployResult } from "../../types/params.js";
|
|
3
4
|
import type { ApplicationOperation } from "../../types/operations.js";
|
|
4
5
|
import type { DeployServices } from "../serviceFactory.js";
|
|
6
|
+
/**
|
|
7
|
+
* Resolve the ECR image tag the rollout should pin against for a given
|
|
8
|
+
* manifest service. Keys the content-hash tag map by the bare `<service>`
|
|
9
|
+
* name — the SAME key `dockerBuildHelper.buildGroup` writes and the
|
|
10
|
+
* `<ServiceName>ImageTag` CfnParameter consumes. The build target is folded
|
|
11
|
+
* into the tag VALUE (`<service>(-<target>)?-sha-<id>`), never the key; a
|
|
12
|
+
* `<service>-<target>` key would miss the freshly-pushed tag for every
|
|
13
|
+
* multi-stage service (e.g. the webapp `deploy-worker`/`scan-worker`).
|
|
14
|
+
* Returns `undefined` when the caller did not push a build for this service
|
|
15
|
+
* (e.g. rollback path, or a service with no manifest entry); the caller then
|
|
16
|
+
* uses an explicit `--image-tag` if supplied, else fails — no moving
|
|
17
|
+
* `-latest` tag exists to guess against.
|
|
18
|
+
*/
|
|
19
|
+
export declare function lookupContentHashTag(manifestService: ManifestDockerService | undefined, contentHashTagsByService: Record<string, string>): string | undefined;
|
|
5
20
|
/**
|
|
6
21
|
* Re-base an operator-supplied `--image-tag` onto a specific ECS service.
|
|
7
22
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{join as
|
|
1
|
+
import{join as _}from"path";import{success as j,failure as S}from"@fjall/generator";import{logger as L}from"@fjall/util/logger";import{maskSensitiveOutput as v}from"@fjall/util";import{parseDockerServicesFromManifest as N}from"@fjall/util/manifest";import{APPLICATION_STACKS as F,getApplicationStepId as W,getApplicationStepName as q}from"../../types/operations.js";import{extractEcsServiceName as O}from"../../aws/utils/arnParser.js";import{stripTagOrDigest as U}from"../../services/infrastructure/EcrImageInspectorService.js";import{withStepLifecycle as z}from"../stepLifecycle.js";const I="codeOnlyDeploy";function G(n,a){if(n)return a[n.name]}function H(n,a){const o=a.toLowerCase();return n.find(t=>t.name.toLowerCase()===o)??n.find(t=>o.includes(t.name.toLowerCase()))}function K(n,a,o){const t=a.toLowerCase(),c=n.toLowerCase(),g=o.map(p=>p.toLowerCase()).filter(p=>p!==""&&c.startsWith(`${p}-`)).sort((p,T)=>T.length-p.length)[0];if(g===void 0||g===t)return n;const E=n.slice(g.length);return`${t}${E}`}const X=0,Y=1;async function ae(n,a,o,t){const{callbacks:c,options:g}=n,E=Date.now(),p=W(F.COMPUTE,"deploy"),T=q(F.COMPUTE,"deploy");return z(c,{stepId:p,stepName:T,stepIndex:X,totalSteps:Y},async()=>{const d=g?.imageTag;d&&c.onLog?.(`Rolling to supplied image tag ${d}`,"info");const D=N(_(o.path,"cdk.out"));L.debug(I,"Code-only rollout starting",{explicitRollbackTag:d,workingDirectory:n.workingDirectory,contentHashTagsByService:t,manifestServiceCount:D.length,manifestServices:D.map(e=>({name:e.name,target:e.docker.target}))}),c.onLog?.("Discovering ECS cluster and services\u2026","info");const h=await a.ecsResolver.getDeployableClusterAndServices(o.appName);if(!h.success)return L.debug(I,"ECS discovery failed",{appName:o.appName,error:v(h.error.message)}),{kind:"error",error:h.error};const{clusterArn:r,serviceArns:m}=h.data;if(L.debug(I,"ECS discovery complete",{appName:o.appName,clusterArn:r,serviceCount:m.length,serviceArns:m}),!r||m.length===0){const e=`No deployable ECS services found for ${o.appName}`;return c.onLog?.(e,"warn"),{kind:"skipped",data:{target:o.appName,deploymentType:"application",durationMs:Date.now()-E,noChanges:!0}}}const R=g?.serviceName?.toLowerCase(),$=R===""?void 0:R,f=$?m.filter(e=>{const l=(O(e)??"").toLowerCase();return l===$||l.endsWith($)}):m;if($&&f.length===0){const e=m.map(l=>O(l)??"unknown").join(", ");return{kind:"error",error:new Error(`Service '${$}' not found. Available: ${e||"none"}`)}}const w={},k=[],b=m.map(e=>O(e)).filter(e=>e!==void 0&&e!=="");for(let e=0;e<f.length;e++){const l=f[e];if(!l)continue;const i=O(l)??`service-${e+1}`,A=`[${e+1}/${f.length}] ${i}`,y=H(D,i),x=G(y,t);let C,s;if(d!==void 0)C=K(d,i,b),C!==d?(s=d,c.onLog?.(`Re-based image tag ${d} \u2192 ${C} for ${i}`,"info")):b.length>1&&!d.toLowerCase().startsWith(`${i.toLowerCase()}-`)&&c.onLog?.(`Image tag ${d} has no recognised service prefix; applying verbatim to ${i} \u2014 confirm it points at this service's image`,"warn");else if(x!==void 0)C=x;else{const P=`No image available to deploy for ${i}: no content-hash tag was produced this run. Run a full deploy to build the image, or pass --image-tag <tag> to roll a specific existing image.`;c.onLog?.(P,"warn"),k.push(`${i}: ${P}`);continue}L.debug(I,"Starting service rollout",{serviceName:i,serviceArn:l,imageTag:C,explicitRollbackTag:d,contentHashTag:x,manifestServiceMatched:y?.name,dockerTarget:y?.docker.target,index:e,total:f.length}),c.onECSUpdate?.(`${A}: rolling out image tag ${C}`,i);const u=await J(a,r,l,i,C,c,s);if(L.debug(I,"Service rollout completed",{serviceName:i,success:u.success,...u.success?{taskDefinitionArn:u.data.taskDefinitionArn,imageDigest:u.data.imageDigest,durationMs:u.data.durationMs}:{error:v(u.error.message)}}),!u.success){k.push(`${i}: ${v(u.error.message)}`);continue}w[`${i}TaskDefinition`]=u.data.taskDefinitionArn,w[`${i}PreviousTaskDefinition`]=u.data.previousTaskDefinitionArn,w[`${i}ImageTag`]=C,w[`${i}ImageUri`]=u.data.imageUri,w[`${i}EcrRepositoryArn`]=u.data.ecrRepositoryArn,u.data.imageDigest!==void 0&&(w[`${i}ImageDigest`]=u.data.imageDigest)}const M=Date.now()-E;if(k.length>0){const e=f.length-k.length,l=f.length,i=k.length===l?`All ${l} ECS service rollouts failed: ${k.join("; ")}`:`Partial rollout: ${e}/${l} services succeeded. Failures: ${k.join("; ")}`;return{kind:"error",error:new Error(i)}}return{kind:"completed",data:{target:o.appName,deploymentType:"application",outputs:Object.keys(w).length>0?w:void 0,durationMs:M}}})}async function J(n,a,o,t,c,g,E){const p=Date.now(),T=await n.ecsService.describeServices(a,[o]);if(!T.success)return S(new Error(`Failed to describe service ${t}: ${T.error.message}`));const d=T.data[0];if(!d?.taskDefinition)return S(new Error(`Service ${t} has no task definition`));const D=d.taskDefinition,h=await n.ecsService.getLatestTaskDefinition(D);if(!h.success)return S(new Error(`Failed to fetch task definition for ${t}: ${h.error.message}`));const r=h.data;if(!r||!r.containerDefinitions||r.containerDefinitions.length===0)return S(new Error(`Task definition for ${t} has no container definitions`));const m=r.containerDefinitions.find(s=>s.image)?.image;if(!m)return S(new Error(`Task definition for ${t} has no container image \u2014 cannot derive repository URI`));const R=U(m),$=await n.ecrImageInspector.getImageDigest(R,c);let f;if($.success)f=$.data;else{if(E!==void 0)return S(new Error(`Re-based image tag '${c}' (from supplied '${E}') was not found in ECR for ${t}: ${v($.error.message)}. The supplied tag's build has no image for ${t} \u2014 supply that service's own tag, or pass --service to roll a single service.`));g.onLog?.(`Could not resolve image digest for ${t} (${v($.error.message)}); falling back to tag pinning`,"warn")}const w=s=>f!==void 0?`${s}@${f}`:`${s}:${c}`,k=r.containerDefinitions.map(s=>s.image?{...s,image:w(U(s.image))}:s),b=r.family;if(!b)return S(new Error(`Task definition for ${t} has no family`));const M=await n.ecsService.registerTaskDefinition({family:b,taskRoleArn:r.taskRoleArn,executionRoleArn:r.executionRoleArn,networkMode:r.networkMode,containerDefinitions:k,volumes:r.volumes,placementConstraints:r.placementConstraints,requiresCompatibilities:r.requiresCompatibilities,cpu:r.cpu,memory:r.memory,runtimePlatform:r.runtimePlatform,proxyConfiguration:r.proxyConfiguration,inferenceAccelerators:r.inferenceAccelerators,pidMode:r.pidMode,ipcMode:r.ipcMode,ephemeralStorage:r.ephemeralStorage});if(!M.success)return S(new Error(`Failed to register task definition for ${t}: ${M.error.message}`));const e=M.data;g.onTaskDefRegistered?.({serviceName:t,taskDefinitionArn:e,previousTaskDefinitionArn:D,revision:V(e),family:b,...f!==void 0?{imageDigest:f}:{}});const l=await n.ecsService.updateService(a,o,{taskDefinition:e,forceNewDeployment:!0});if(!l.success)return S(new Error(`Failed to update service ${t}: ${l.error.message}`));const i=await n.ecsService.pollDeployment({clusterArn:a,serviceArn:o,waitForCompletion:!0,progressCallback:s=>{const u=s.latestEvent?` \u2014 ${v(s.latestEvent)}`:"";g.onECSProgress?.(`${t}: ${v(s.message)}${u}`,s.percentComplete)}}),A=Date.now()-p;if(!i.success){const s=v(i.error.message);return g.onECSComplete?.({serviceName:t,serviceArn:o,success:!1,taskDefinitionArn:e,durationMs:A,reason:s}),S(new Error(`ECS rollout failed for ${t}: ${s}`))}const y=await n.ecsService.getDeploymentStatus(a,o);g.onECSComplete?.({serviceName:t,serviceArn:o,success:!0,taskDefinitionArn:e,durationMs:A,finalRunningCount:y.success?y.data.runningCount:void 0,finalDesiredCount:y.success?y.data.desiredCount:void 0}),L.debug(I,`Rolled out ${t} successfully`,{serviceArn:o,newTaskDefArn:e,previousTaskDefArn:D,durationMs:A});const x=`${R}:${c}`,C=Q(R);return j({taskDefinitionArn:e,previousTaskDefinitionArn:D,imageUri:x,ecrRepositoryArn:C,imageDigest:f,durationMs:A})}function Q(n){const a=n.match(/^(\d{12})\.dkr\.ecr\.([a-z0-9-]+)\.amazonaws\.com\/(.+)$/);if(!a)return"";const[,o,t,c]=a;return`arn:aws:ecr:${t}:${o}:repository/${c}`}function V(n){const a=n.lastIndexOf(":");if(a<0)return 0;const o=n.slice(a+1);return/^\d+$/.test(o)?parseInt(o,10):0}export{ae as deployCodeOnly,G as lookupContentHashTag,K as rebaseRollbackTagForService};
|
|
@@ -10,8 +10,8 @@ export declare const DOCKER_BUILD_STEP_NAME = "Building and pushing Docker image
|
|
|
10
10
|
* Reads the post-synth `cdk.out/fjall-manifest.json` to discover the set of
|
|
11
11
|
* services that need container images, resolves each service's
|
|
12
12
|
* `(buildContext, dockerfilePath, target)` tuple, groups identical builds,
|
|
13
|
-
* and pushes
|
|
14
|
-
* the
|
|
13
|
+
* and pushes each group to ECR addressed by digest only (no moving tag), so
|
|
14
|
+
* the repository can stay IMMUTABLE.
|
|
15
15
|
*
|
|
16
16
|
* After each group's push, the helper derives a content-hash tag from the
|
|
17
17
|
* digest (`deriveContentHashTag` at `@fjall/util`) and applies it via
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{isAbsolute as
|
|
1
|
+
import{isAbsolute as O,join as E,dirname as A,resolve as N}from"node:path";import{existsSync as R}from"node:fs";import{success as b,failure as h}from"@fjall/generator";import{deriveContentHashTag as w,maskSensitiveOutput as l,toKebab as x}from"@fjall/util";import{logger as C}from"@fjall/util/logger";import{parseDockerServicesFromManifest as I}from"@fjall/util/manifest";import{STEP_IDS as v}from"../../types/stepDefinitions.js";const S="dockerBuildHelper",P="Building and pushing Docker image";function T(e,n,r){const t=E(n,e);if(R(t))return{path:t,base:n};if(r!==""&&r!==n){const i=E(r,e);if(R(i))return{path:i,base:r}}return{path:t,base:n}}function B(e,n,r){const t=e.docker.path,i=e.docker.context;let o,s;if(O(t))o=t,s=n;else{const u=T(t,n,r);o=u.path,s=u.base}return{buildContext:i?O(i)?N(i):N(s,i):N(A(o)),dockerfilePath:o,target:e.docker.target,resolvedViaFallback:s!==n}}function L(e){return`${e.buildContext}|${e.dockerfilePath}|${e.target??""}`}function _(e,n){return`Dockerfile not found for service '${e}' at ${n}. Checked the synth directory and the working directory. If your Dockerfile lives at the project root with the CDK app in a subdirectory, set an absolute docker.context (the build context must share the Dockerfile's directory).`}function K(e){return e.startsWith("cn-")?"amazonaws.com.cn":"amazonaws.com"}function H(e,n,r){return`${e}.dkr.ecr.${n}.${K(n)}/${x(r)}`}function j(e,n,r){const t=new Map;for(const i of e){const o=B(i,n,r),s=L(o),c=t.get(s);c?c.members.push(i):t.set(s,{identity:o,members:[i]})}return Array.from(t.values())}async function F(e,n,r,t,i){const o=await e.buildAndPush({appName:n.appName,appPath:n.path,region:r,accountId:t},(s,c,u,y)=>{i.onDockerProgress?.(l(s),c,u,y)});return o.success?b({imageUri:o.data.imageUri,imageTag:o.data.imageTag}):h(o.error)}async function U(e,n,r,t,i,o,s){const c=e.members[0];if(!R(e.identity.dockerfilePath))return h(new Error(_(c.name,e.identity.dockerfilePath)));e.identity.resolvedViaFallback&&s.onLog?.(`Building service '${c.name}' from ${e.identity.dockerfilePath} (resolved at the project root).`,"info");const u={appName:r.appName,appPath:e.identity.buildContext,region:t,accountId:i,buildContext:e.identity.buildContext,dockerfilePath:e.identity.dockerfilePath,pushByDigest:!0,serviceName:c.name,...e.identity.target!==void 0&&{target:e.identity.target},...c.docker.buildArgs!==void 0&&{buildArgs:c.docker.buildArgs}},y=await n.buildAndPush(u,(p,f,d,D)=>{s.onDockerProgress?.(l(p),f,d,D)});if(!y.success)return h(y.error);const m=y.data.imageDigest;if(typeof m!="string"||!m.startsWith("sha256:"))return h(new Error(`Buildx push succeeded but returned an invalid digest: ${l(String(m))}`));const k={},a=[];for(const p of e.members){const f=p.docker.target,d=w(m,p.name,f);if(!d.success)return h(d.error);const D=d.data,$=p.name;k[$]=D,a.push(`${o}:${D}`)}const g=await n.tagByDigest({sourceImage:o,digest:m,tags:a});return g.success?b({contentHashTagsByService:k}):h(g.error)}async function J(e,n,r,t){const i=e.dockerProvider;if(!i)return b({});const o=n.awsProvider.getAccountId();if(!o)return t.onLog?.("Skipping Docker build \u2014 account ID not available","warn"),b({});const s=n.awsProvider.getRegion(),c=E(r.path,"cdk.out"),u=I(c),y=H(o,s,r.appName);if(C.debug(S,"runDockerBuild starting",{appName:r.appName,appPath:r.path,accountId:o,region:s,cdkOutPath:c,manifestServiceCount:u.length,manifestServices:u.map(a=>({name:a.name,path:a.docker.path,context:a.docker.context,target:a.docker.target})),stepId:v.DOCKER_OPERATIONS,stepName:P}),t.onStepStart?.(v.DOCKER_OPERATIONS,P),u.length===0){t.onLog?.("Initialising ECR repository\u2026","info");const a=await i.initialiseECR({appName:r.appName,region:s,accountId:o});a.success||t.onLog?.(`ECR initialisation failed: ${l(a.error.message)}`,"warn"),t.onLog?.("Building and pushing Docker image\u2026","info");const g=await F(i,r,s,o,t);if(!g.success){C.debug(S,"Docker buildAndPush (legacy) failed",{appName:r.appName,error:l(g.error.message)}),t.onStepComplete?.(v.DOCKER_OPERATIONS,P,"error");const p=new Error(l(g.error.message));return t.onError?.(p),h(p)}return t.onStepComplete?.(v.DOCKER_OPERATIONS,P,"completed"),t.onLog?.(`Docker image pushed: ${g.data.imageUri}`,"info"),b({})}const m=j(u,r.path,e.workingDirectory);t.onLog?.(`Building and pushing ${u.length} service image(s) in ${m.length} group(s)\u2026`,"info");const k={};for(let a=0;a<m.length;a++){const g=m[a],p=g.members.map(d=>d.name).join(", ");t.onLog?.(`Building group ${a+1}/${m.length} (${p})\u2026`,"info");const f=await U(g,i,r,s,o,y,t);if(!f.success){C.debug(S,"Docker buildAndPush failed",{appName:r.appName,leader:g.members[0]?.name,members:p,error:l(f.error.message)}),t.onStepComplete?.(v.DOCKER_OPERATIONS,P,"error");const d=new Error(l(f.error.message));return t.onError?.(d),h(d)}for(const[d,D]of Object.entries(f.data.contentHashTagsByService))k[d]=D}return C.debug(S,"Docker buildAndPush succeeded",{appName:r.appName,services:u.map(a=>a.name),groupCount:m.length,contentHashTags:k}),t.onStepComplete?.(v.DOCKER_OPERATIONS,P,"completed"),t.onLog?.(`Docker images pushed for ${u.length} service(s)`,"info"),b(k)}export{P as DOCKER_BUILD_STEP_NAME,J as runDockerBuild};
|
|
@@ -41,6 +41,15 @@ export interface DockerBuildParams {
|
|
|
41
41
|
* from `imageTag` / `appName`.
|
|
42
42
|
*/
|
|
43
43
|
imageTags?: readonly string[];
|
|
44
|
+
/**
|
|
45
|
+
* Push the image to ECR addressed by digest only, creating no tag. The
|
|
46
|
+
* caller derives a content-hash tag from `DockerBuildResult.imageDigest` and
|
|
47
|
+
* applies it via `tagByDigest`. This is the deploy path's default: it keeps
|
|
48
|
+
* the ECR repo IMMUTABLE (a moving `-latest` tag would collide on the second
|
|
49
|
+
* push; digest-only pushes and unique content-hash tags never do). When set,
|
|
50
|
+
* `imageTags` is ignored.
|
|
51
|
+
*/
|
|
52
|
+
pushByDigest?: boolean;
|
|
44
53
|
/** Optional buildArgs forwarded to buildx. */
|
|
45
54
|
buildArgs?: Readonly<Record<string, string>>;
|
|
46
55
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fjall/deploy-core",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.21.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",
|
|
@@ -78,8 +78,8 @@
|
|
|
78
78
|
"@aws-sdk/client-sqs": "^3.1067.0",
|
|
79
79
|
"@aws-sdk/client-sso-admin": "^3.1038.0",
|
|
80
80
|
"@aws-sdk/client-sts": "^3.1038.0",
|
|
81
|
-
"@fjall/generator": "^2.
|
|
82
|
-
"@fjall/util": "^2.
|
|
81
|
+
"@fjall/generator": "^2.21.0",
|
|
82
|
+
"@fjall/util": "^2.21.0",
|
|
83
83
|
"@smithy/node-http-handler": "^4.6.1",
|
|
84
84
|
"tsx": "^4.21.0",
|
|
85
85
|
"zod": "^4.4.3"
|
|
@@ -89,5 +89,5 @@
|
|
|
89
89
|
"typescript": "^6.0.3",
|
|
90
90
|
"vitest": "^4.1.5"
|
|
91
91
|
},
|
|
92
|
-
"gitHead": "
|
|
92
|
+
"gitHead": "1b8902876c40697183ce8cbc25a0556b11d0ec38"
|
|
93
93
|
}
|