@fjall/deploy-core 2.29.0 → 2.30.3
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 -2
- package/dist/src/index.js +1 -1
- package/dist/src/orchestration/application/applicationDeploy.js +1 -1
- package/dist/src/orchestration/application/applicationDeployHelpers.d.ts +18 -0
- package/dist/src/orchestration/application/applicationDeployHelpers.js +3 -3
- package/dist/src/orchestration/application/artefactCollection.d.ts +8 -1
- package/dist/src/orchestration/application/artefactCollection.js +1 -1
- package/dist/src/orchestration/application/codeOnlyDeploy.d.ts +32 -1
- package/dist/src/orchestration/application/codeOnlyDeploy.js +1 -1
- package/dist/src/orchestration/application/dockerBuildHelper.d.ts +1 -0
- package/dist/src/orchestration/application/dockerBuildHelper.js +1 -1
- package/dist/src/orchestration/application/lambdaCodeOnlyRollout.d.ts +58 -0
- package/dist/src/orchestration/application/lambdaCodeOnlyRollout.js +1 -0
- package/dist/src/orchestration/application/openNextBuild.d.ts +1 -1
- package/dist/src/orchestration/application/openNextBuild.js +3 -3
- package/dist/src/orchestration/builders/frameworkRegistry.js +1 -1
- package/dist/src/orchestration/builders/index.d.ts +1 -0
- package/dist/src/orchestration/builders/index.js +1 -1
- package/dist/src/orchestration/builders/openNextBuilder.js +1 -1
- package/dist/src/orchestration/builders/staticSiteBuilder.d.ts +27 -0
- package/dist/src/orchestration/builders/staticSiteBuilder.js +2 -0
- package/dist/src/orchestration/serviceFactory.d.ts +2 -0
- package/dist/src/orchestration/serviceFactory.js +1 -1
- package/dist/src/orchestration/spawnHelpers.d.ts +9 -0
- package/dist/src/orchestration/spawnHelpers.js +1 -1
- package/dist/src/services/infrastructure/LambdaService.d.ts +45 -0
- package/dist/src/services/infrastructure/LambdaService.js +1 -0
- package/dist/src/types/deployEvent.d.ts +10 -3
- package/dist/src/types/deployEvent.js +1 -1
- package/dist/src/types/detection/patternTypes.d.ts +9 -4
- package/dist/src/types/detection/patternTypes.js +1 -1
- package/dist/src/types/frameworkBuilder.d.ts +14 -0
- package/dist/src/types/index.d.ts +36 -2
- package/dist/src/types/index.js +1 -1
- package/dist/src/types/operations.d.ts +2 -1
- package/package.json +6 -4
package/dist/.minified
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
216 files minified at 2026-07-16T01:01:46.559Z
|
package/dist/src/index.d.ts
CHANGED
|
@@ -32,7 +32,9 @@ export type { RemediationVerbWithSurgery } from "./types/index.js";
|
|
|
32
32
|
export type { DriftVerdict, DriftSuspect, DriftFinding, DriftJournalRecord } from "./types/index.js";
|
|
33
33
|
export type { StepId, InfrastructureStepName, StepDefinition, StepDeploymentType, Operation, StepContext } from "./types/index.js";
|
|
34
34
|
export type { DeploymentContext, StepOutput, ApplicationDeploymentContext, CallerIdentity, StackOutput, StackOutputsRecord, ValidationResult, DetectionResult, ProgressCallbacks, ApplicationStack, OrganisationType, DeploymentOperation, ApplicationOperation, OrganisationOperation, DeployOrderOptions, ParallelDeployGroupKey, ParallelDestroyGroupKey, OpenNextPattern, AppResourceFlags, ParallelDeploymentResult, ParallelDeployGroup, ParallelOperation, ParallelOperationType, ParallelOperationState, ApplicationErrorType, StackDeploymentData, StackDeploymentOptions, ApplicationDeploymentData, ApplicationDestructionData, FjallStateFile, TemplateHashEntry } from "./types/index.js";
|
|
35
|
-
export { ProgressReporter, APPLICATION_STACKS, ORGANISATION_TYPES, APPLICATION_DEPLOY_ORDER, APPLICATION_DESTROY_ORDER, OPENNEXT_DEPLOY_ORDER, OPENNEXT_DESTROY_ORDER, PARALLEL_DEPLOY_GROUPS, PARALLEL_DESTROY_GROUPS, OPENNEXT_PARALLEL_GROUPS, PARALLEL_OPERATION_TYPES, isApplicationOperation, isOrganisationOperation, getParallelDeployGroups, getParallelDestroyGroups, getApplicationDeployOrder, getApplicationDestroyOrder, getApplicationStackName, getOrganisationStackName, isApplicationStack, isQuarantineDetail, isRetainedBucketsDetail, getApplicationStepName, getApplicationStepId, toPascalCase, isOpenNextPattern, OPENNEXT_PATTERNS,
|
|
35
|
+
export { ProgressReporter, APPLICATION_STACKS, ORGANISATION_TYPES, APPLICATION_DEPLOY_ORDER, APPLICATION_DESTROY_ORDER, OPENNEXT_DEPLOY_ORDER, OPENNEXT_DESTROY_ORDER, PARALLEL_DEPLOY_GROUPS, PARALLEL_DESTROY_GROUPS, OPENNEXT_PARALLEL_GROUPS, PARALLEL_OPERATION_TYPES, isApplicationOperation, isOrganisationOperation, getParallelDeployGroups, getParallelDestroyGroups, getApplicationDeployOrder, getApplicationDestroyOrder, getApplicationStackName, getOrganisationStackName, isApplicationStack, isQuarantineDetail, isRetainedBucketsDetail, getApplicationStepName, getApplicationStepId, toPascalCase, isOpenNextPattern, OPENNEXT_PATTERNS, STACK_NOT_FOUND_PATTERN, STACK_FAILED_STATE_PATTERN, CDK_NO_STACKS_MATCH, INFRASTRUCTURE_FILENAME, ApplicationError, wrapApplicationError, stubCallerIdentity } from "./types/index.js";
|
|
36
|
+
export { deriveResourcesFromManifestStacks } from "./types/detection/patternDetection.js";
|
|
37
|
+
export { FjallStateFileSchema, readStateFile, writeStateFile, createEmptyState, deleteStateFile, updateTemplateHash, getStateFilePath, regionSuffix } from "./types/config/FjallState.js";
|
|
36
38
|
export { CloudFormationEventMonitor } from "./aws/index.js";
|
|
37
39
|
export type { AwsClientLike, EventLogWriter, EventFailureAnalyser, EventLogWriterFactory, EventMonitorDeps } from "./aws/index.js";
|
|
38
40
|
export { CdkService, CdkArgumentBuilder, CdkProcessManager, CdkEventMonitor, startStackMonitoring, DEFAULT_DEPLOY_TIMEOUT_MS, isCdkError, formatInfrastructureError, getStructuralHint, getSourceContext, hasCdkDifferences, parseDiffOutput, CloudFormationService, CloudFormationError, EcsService, EcsError, EcsServiceResolver, type CfnExportsClient, TemplateHashService, TemplateHashError, type TemplateComparisonResult, CdkContextBuilder, emitProgress, PROGRESS_MESSAGES, parseBuildPhase, buildStepContextBuildConfig, convertCloudFormationOutputsToRecord, type CloudFormationOutput, ApplicationStackService } from "./services/index.js";
|
|
@@ -81,7 +83,7 @@ export { resolveBuildSecrets, resolveSecretRefValue, resolveBuildSecretSessionPr
|
|
|
81
83
|
export type { ResolveBuildArgsContext, ResolveBuildArgsResult, SourcedBuildArgResolver, BuildSecretRef, BuildSecretSessionContext, DeploySessionContext, ValidateBuildGroupInput, BuildGroupMemberInputs } from "./orchestration/index.js";
|
|
82
84
|
export type { FrameworkBuilder, FrameworkDetection, BuildPlan, BuildCommand, BuildCallbacks, DetectionContext, BuildOptions } from "./types/index.js";
|
|
83
85
|
export { FrameworkRegistry, type ResolvedBuilder } from "./orchestration/index.js";
|
|
84
|
-
export { openNextBuilder, dockerBuilder } from "./orchestration/index.js";
|
|
86
|
+
export { openNextBuilder, staticSiteBuilder, dockerBuilder } from "./orchestration/index.js";
|
|
85
87
|
export { StepRegistry, getDestroyStepId } from "./steps/index.js";
|
|
86
88
|
export { buildDeployPlan, computeDeployPlan, computeAssemblyDigest, digestHead, classifyImpact, derivePropertyChanges, isDataLoss, isStatefulResourceType, renderPlanSummary, renderPlanLines, toWirePlanChanges, signApprovalToken, verifyApprovalToken, DEFAULT_APPROVAL_TTL_MS, APPROVAL_TOKEN_PATTERN, runApprovalGate } from "./orchestration/index.js";
|
|
87
89
|
export type { DeployPlan, DeployPlanResourceChange, StackTemplatePair, CfnTemplateReader, ComputeDeployPlanParams, ComputeDeployPlanStack, ImpactClassification, SignApprovalTokenOptions, SignedApprovalToken, VerifyApprovalTokenOptions, VerifyApprovalTokenResult, RunApprovalGateParams, ApprovalGateOutcome } 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 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{DeployEventSchema as l,StepProgressSchema as T,DeployResourceSchema as p,PlanChangeSchema as A,PlanPropertyChangeSchema as R,ChangeCountsSchema as _,DEPLOY_EVENT_CONTRACT as m,DEPLOY_EVENT_CONTRACT_MAJOR as u,DEPLOY_EVENT_CONTRACT_MINOR as d,SUPPORTED_CONTRACT_MARKERS as P,DEPLOY_EVENT_ID_MAX as O,DEPLOY_EVENT_NAME_MAX as D,DEPLOY_EVENT_MESSAGE_MAX as f,DEPLOY_EVENT_STATUS_REASON_MAX as C,DEPLOY_EVENT_ERROR_MESSAGE_MAX as N,DEPLOY_EVENT_TRAIL_DETAIL_MAX as g,DEPLOY_EVENT_OUTPUT_MAX as L,DEPLOY_EVENT_ARN_MAX as I,DEPLOY_EVENT_URL_MAX as y,STEP_COMPLETE_STATUSES as h,LOG_LEVELS as v,STACK_CLEANUP_PHASES as x,CASCADE_ACCOUNT_PHASES as F,DEPLOY_RESULTS as M,DETECTION_PHASES as U,PHASE_STATUSES as k,BUILDERS as b,CASCADE_OUTCOME_RESULTS as V,PLAN_ACTIONS as Y,REPLACEMENT_MODES as B,APPROVAL_KINDS as G,APPROVAL_DECISIONS as K,ALL_PROGRESS_KINDS as X,createDeployEmitter as H,parseDeployEvent as J,parseDeployContractVersion as w}from"./types/index.js";import{SimpleAwsProvider as Q}from"./aws/index.js";import{checkTargetReadiness as q,describeTargetReadinessReason as Z,buildTargetUnreadyAdvisory as z,buildTargetSetUnreadyAdvisory as $,probeAccountStackExists as ee}from"./aws/index.js";import{checkRegionEnabled as te,regionDisabledMessage as oe}from"./aws/index.js";import{ensureOrganisationExists as ae,describeOrganisation as se,enablePolicyTypes as ne,enableServiceAccess as ce,SERVICE_PRINCIPALS as Ee,enableRamSharing as Se,activateTrustedAccess as le,enableIpamDelegatedAdmin as Te,updateBackupGlobalSettings as pe,listAccounts as Ae,findAccount as Re,createAccount as _e,ensureOrganisationalUnitsExist as me,placeAccountsInOUs as ue,buildAccountToOUMap as de,findDevelopmentOuId as Pe,activateCostAllocationTags as Oe,checkIdentityCentreStatus as De,snapshotIdentityCentre as fe,composeSdkAbortSignal as Ce,extractErrorName as Ne,isAborted as ge,DEFAULT_MAX_PAGES as Le,IDENTITY_STORE_PAGE_SIZE as Ie,drainPages as ye,isOULeaf as he,registerSecurityDelegates as ve,SECURITY_SERVICE_PRINCIPALS as xe,enableCentralisedRootAccess as Fe,ROOT_ACCESS_FEATURES as Me,RootAccessEnablementError as Ue}from"./aws/index.js";import{STEP_IDS as be,STEP_NAMES as Ve,INFRASTRUCTURE_STEP_NAMES as Ye,INFRA_STEP_NAME as Be}from"./types/index.js";import{RemediationVerbSchema as Ke,REMEDIATION_VERB_GLOSS as Xe,DestructionFindingSchema as He,DestructionTicketResourceSchema as Je,DestructionTicketSchema as we,DestructionConsentSchema as je,DestructionOutcomeSchema as Qe,DESTRUCTION_FINDINGS as We,CONSENT_VERDICTS as qe,TICKET_VERDICT_SOURCES as Ze,TicketVerdictSourceSchema as ze}from"./types/index.js";import{DRIFT_VERDICTS as er,DriftVerdictSchema as rr,DriftSuspectSchema as tr,DriftFindingSchema as or,DriftJournalRecordSchema as ir,verbsForDriftVerdict as ar}from"./types/index.js";import{REMEDIATION_PHASES as nr,RemediationPhaseSchema as cr,RemediationTargetSchema as Er,RemediationFlipProofSchema as Sr,RemediationJournalRecordSchema as lr,RemediationVerbWithSurgerySchema as Tr,remediationPhaseRank as pr,remediationPlaceholderPhysicalId as Ar,isRemediationPlaceholderPhysicalId as Rr}from"./types/index.js";import{ProgressReporter as mr,APPLICATION_STACKS as ur,ORGANISATION_TYPES as dr,APPLICATION_DEPLOY_ORDER as Pr,APPLICATION_DESTROY_ORDER as Or,OPENNEXT_DEPLOY_ORDER as Dr,OPENNEXT_DESTROY_ORDER as fr,PARALLEL_DEPLOY_GROUPS as Cr,PARALLEL_DESTROY_GROUPS as Nr,OPENNEXT_PARALLEL_GROUPS as gr,PARALLEL_OPERATION_TYPES as Lr,isApplicationOperation as Ir,isOrganisationOperation as yr,getParallelDeployGroups as hr,getParallelDestroyGroups as vr,getApplicationDeployOrder as xr,getApplicationDestroyOrder as Fr,getApplicationStackName as Mr,getOrganisationStackName as Ur,isApplicationStack as kr,isQuarantineDetail as br,isRetainedBucketsDetail as Vr,getApplicationStepName as Yr,getApplicationStepId as Br,toPascalCase as Gr,isOpenNextPattern as Kr,OPENNEXT_PATTERNS as Xr,deriveResourcesFromManifestStacks as Hr,STACK_NOT_FOUND_PATTERN as Jr,STACK_FAILED_STATE_PATTERN as wr,CDK_NO_STACKS_MATCH as jr,INFRASTRUCTURE_FILENAME as Qr,ApplicationError as Wr,wrapApplicationError as qr,FjallStateFileSchema as Zr,readStateFile as zr,writeStateFile as $r,createEmptyState as et,deleteStateFile as rt,updateTemplateHash as tt,getStateFilePath as ot,stubCallerIdentity as it}from"./types/index.js";import{CloudFormationEventMonitor as st}from"./aws/index.js";import{CdkService as ct,CdkArgumentBuilder as Et,CdkProcessManager as St,CdkEventMonitor as lt,startStackMonitoring as Tt,DEFAULT_DEPLOY_TIMEOUT_MS as pt,isCdkError as At,formatInfrastructureError as Rt,getStructuralHint as _t,getSourceContext as mt,hasCdkDifferences as ut,parseDiffOutput as dt,CloudFormationService as Pt,CloudFormationError as Ot,EcsService as Dt,EcsError as ft,EcsServiceResolver as Ct,TemplateHashService as Nt,TemplateHashError as gt,CdkContextBuilder as Lt,emitProgress as It,PROGRESS_MESSAGES as yt,parseBuildPhase as ht,buildStepContextBuildConfig as vt,convertCloudFormationOutputsToRecord as xt,ApplicationStackService as Ft}from"./services/index.js";import{CdkError as Ut}from"./types/errors/index.js";import{BaseServiceError as bt,ValidationError as Vt,AuthError as Yt,AwsError as Bt,DeploymentError as Gt,NetworkError as Kt,FileSystemError as Xt,ConfigError as Ht,toServiceError as Jt}from"./types/errors/index.js";import{filterDangerousEnvVars as jt,maskSensitiveOutput as Qt,parseShellArgs as Wt,sleep as qt}from"@fjall/util";import{artefactOutputKey as zt,DeployModeSchema as $t,ServiceArtefactSchema as eo,ServiceArtefactsSchema as ro}from"@fjall/util";import{hasDockerfile as oo}from"./util/dockerfileDetection.js";import{sleepAbortable as ao}from"./util/sleepAbortable.js";import{createSequencedCallbacks as no}from"./util/sequencedCallbacks.js";import{fileExists as Eo}from"@fjall/util/fsHelpers";import{success as lo,failure as To,isSuccess as po,isFailure as Ao}from"@fjall/generator";import{deploy as _o}from"./orchestration/index.js";import{destroy as uo}from"./orchestration/index.js";import{restart as Oo,probeSecretsDrift as Do,computeSecretsDrift as fo,deriveStaleParameters as Co,extractConsumedSsmParameters as No}from"./orchestration/index.js";import{partitionAccounts as Lo}from"./orchestration/index.js";import{buildRegionList as yo,buildAccountRegionPairs as ho,cascadeHomeRegion as vo,cascadeOperationKey as xo}from"./orchestration/index.js";import{projectScalarSummary as Mo,projectAccountRows as Uo}from"./orchestration/index.js";import{reconcileProviderAccounts as bo,mergeReconciledProviderAccounts as Vo}from"./orchestration/index.js";import{decideNextTransition as Bo,reconcileTrailMigration as Go,decommissionMemberTrailStorage as Ko,ORG_TRAIL_BUCKET_OUTPUT_KEY as Xo,TRAIL_BUCKET_OUTPUT_KEY as Ho,TRAIL_KEY_ARN_OUTPUT_KEY as Jo}from"./orchestration/index.js";import{classifyDriftFailure as jo,fetchLastOperationEvents as Qo,DriftProbe as Wo,clearDriftSuspects as qo,defaultDriftJournalDir as Zo,readDriftSuspects as zo,recordDriftSuspects as $o,detectStackDrift as ei,runDriftPreFlight as ri,formatDriftPreFlightBlock as ti,DRIFT_PREFLIGHT_TRIGGER_STATUSES as oi,DRIFT_PREFLIGHT_BUDGET_MS as ii}from"./orchestration/index.js";import{archiveCompletedRemediationJournal as si,computeRemediationOpId as ni,defaultRemediationJournalDir as ci,findRemediationJournalByOpId as Ei,listActiveRemediationOps as Si,listActiveRemediationOpsForContext as li,readRemediationJournal as Ti,sweepExpiredRemediationJournals as pi,writeRemediationJournal as Ai,REMEDIATION_JOURNAL_RETENTION_DAYS as Ri,captureRemediationForensics as _i,defaultRemediationForensicsDir as mi,applyRetainFlip as ui,composeFlipCapabilities as di,computeTemplateDigest as Pi,deriveRetainFlipTemplate as Oi,findMarkedResourcesByOpId as Di,proveFlipMetadataOnly as fi,readRetainFlipState as Ci,verifyRetainFlipDiff as Ni,FORGET_MARKER_KEY as gi,FORGET_FLIP_SPEC as Li,RECREATE_MARKER_KEY as Ii,assessForgetResumability as yi,completeForgetAfterConverge as hi,completeForgetAfterDeploy as vi,formatRecreateInFlightCures as xi,partitionPreFlightByRemediation as Fi,runForgetSurgery as Mi,synthTemplateDeclaresResource as Ui,runRecreateSurgery as ki,snapshotPolicyForType as bi,SNAPSHOT_CAPABLE_RESOURCE_TYPES as Vi,runRecreatePreFlight as Yi,deriveRemovalTemplate as Bi,findRemovalDeletionFailures as Gi,proveRemovalTargetsOnly as Ki,advertisableDriftVerbs as Xi,buildDriftRepairTicket as Hi,runDriftRepairGate as Ji,formatPinOutcomeDetail as wi,pinProbeCoverage as ji,renderPinReportLines as Qi,runPinRemediation as Wi,composePostFailureRepairOffer as qi,formatDriftRepairOffer as Zi,DriftRepairAvailableError as zi,checkDeployPathStackAvailability as $i,isDeployPathBlockingStatus as ea,StackUnavailableError as ra,assertLeaseBeforeStack as ta,LeaseDeniedError as oa}from"./orchestration/index.js";import{unlockBucket as aa,unlockQueue as sa}from"./orchestration/index.js";import{triageBucketPolicy as ca,isEnforceSslStatement as Ea,toResourcePolicyStatements as Sa,restoreBucketPolicy as la,synthesiseEnforceSslDocument as Ta,ensureEnforceSsl as pa,restoreAndReconcileQuarantinedBucket as Aa}from"./orchestration/index.js";import{verifyOrgTrailDelivery as _a}from"./aws/cloudtrail/orgTrailDelivery.js";import{assumeRootForTask as ua,isRootTaskPolicyArn as da,ROOT_TASK_POLICY_ARNS as Pa,MAX_ROOT_SESSION_SECONDS as Oa}from"./aws/sts/assumeRoot.js";import{parseAccountsConfiguration as fa,flattenAccountsToEnvironments as Ca,extractAllAccountNames as Na,accountsConfigToOUTree as ga,isStringArray as La,isAccountsConfig as Ia,isOuOnlyAccountBucket as ya,OU_ONLY_ACCOUNT_BUCKETS as ha}from"./orchestration/index.js";import{runOpenNextBuild as xa}from"./orchestration/index.js";import{runOrganisationSetup as Ma,ORG_SETUP_PHASES as Ua}from"./orchestration/index.js";import{resolveBuildSecrets as ba,resolveSecretRefValue as Va,resolveBuildSecretSessionProvider as Ya,sourcedRefsFromBuildArgs as Ba,resolveBuildArgs as Ga,buildBuildSecretSessionPolicy as Ka,buildDeploySessionPolicy as Xa,partitionForRegion as Ha,validateBuildGroup as Ja}from"./orchestration/index.js";import{FrameworkRegistry as ja}from"./orchestration/index.js";import{openNextBuilder as Wa,dockerBuilder as qa}from"./orchestration/index.js";import{StepRegistry as za,getDestroyStepId as $a}from"./steps/index.js";import{buildDeployPlan as rs,computeDeployPlan as ts,computeAssemblyDigest as os,digestHead as is,classifyImpact as as,derivePropertyChanges as ss,isDataLoss as ns,isStatefulResourceType as cs,renderPlanSummary as Es,renderPlanLines as Ss,toWirePlanChanges as ls,signApprovalToken as Ts,verifyApprovalToken as ps,DEFAULT_APPROVAL_TTL_MS as As,APPROVAL_TOKEN_PATTERN as Rs,runApprovalGate as _s}from"./orchestration/index.js";import{runDestructionGate as us,buildDestructionTicket as ds,evaluateDestructionConsents as Ps,computeTicketDigest as Os,legalRemediationVerbs as Ds,executableRemediationVerbs as fs,advertisableRemediationVerbs as Cs,renderDestructionTicketLines as Ns,renderConsentVerdictLines as gs,loadDeployPlan as Ls,createMemoisedPlanLoader as Is}from"./orchestration/index.js";export{X as ALL_PROGRESS_KINDS,Pr as APPLICATION_DEPLOY_ORDER,Or as APPLICATION_DESTROY_ORDER,ur as APPLICATION_STACKS,K as APPROVAL_DECISIONS,G as APPROVAL_KINDS,Rs as APPROVAL_TOKEN_PATTERN,Wr as ApplicationError,Ft as ApplicationStackService,Yt as AuthError,Bt as AwsError,b as BUILDERS,bt as BaseServiceError,F as CASCADE_ACCOUNT_PHASES,E as CASCADE_ACCOUNT_STATUSES,V as CASCADE_OUTCOME_RESULTS,c as CASCADE_PHASES,jr as CDK_NO_STACKS_MATCH,qe as CONSENT_VERDICTS,Et as CdkArgumentBuilder,Lt as CdkContextBuilder,Ut as CdkError,lt as CdkEventMonitor,St as CdkProcessManager,ct as CdkService,_ as ChangeCountsSchema,Ot as CloudFormationError,st as CloudFormationEventMonitor,Pt as CloudFormationService,Ht as ConfigError,As as DEFAULT_APPROVAL_TTL_MS,pt as DEFAULT_DEPLOY_TIMEOUT_MS,Le as DEFAULT_MAX_PAGES,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,I as DEPLOY_EVENT_ARN_MAX,m as DEPLOY_EVENT_CONTRACT,u as DEPLOY_EVENT_CONTRACT_MAJOR,d as DEPLOY_EVENT_CONTRACT_MINOR,N as DEPLOY_EVENT_ERROR_MESSAGE_MAX,O as DEPLOY_EVENT_ID_MAX,f as DEPLOY_EVENT_MESSAGE_MAX,D as DEPLOY_EVENT_NAME_MAX,L as DEPLOY_EVENT_OUTPUT_MAX,C as DEPLOY_EVENT_STATUS_REASON_MAX,g as DEPLOY_EVENT_TRAIL_DETAIL_MAX,y as DEPLOY_EVENT_URL_MAX,M as DEPLOY_RESULTS,We as DESTRUCTION_FINDINGS,U as DETECTION_PHASES,ii as DRIFT_PREFLIGHT_BUDGET_MS,oi as DRIFT_PREFLIGHT_TRIGGER_STATUSES,er as DRIFT_VERDICTS,l as DeployEventSchema,$t as DeployModeSchema,p as DeployResourceSchema,Gt as DeploymentError,t as DeploymentEventSchema,je as DestructionConsentSchema,He as DestructionFindingSchema,Qe as DestructionOutcomeSchema,Je as DestructionTicketResourceSchema,we as DestructionTicketSchema,or as DriftFindingSchema,ir as DriftJournalRecordSchema,Wo as DriftProbe,zi as DriftRepairAvailableError,tr as DriftSuspectSchema,rr as DriftVerdictSchema,ft as EcsError,Dt as EcsService,Ct as EcsServiceResolver,Li as FORGET_FLIP_SPEC,gi as FORGET_MARKER_KEY,Xt as FileSystemError,Zr as FjallStateFileSchema,ja as FrameworkRegistry,Ie as IDENTITY_STORE_PAGE_SIZE,Qr as INFRASTRUCTURE_FILENAME,Ye as INFRASTRUCTURE_STEP_NAMES,Be as INFRA_STEP_NAME,v as LOG_LEVELS,oa as LeaseDeniedError,Oa as MAX_ROOT_SESSION_SECONDS,Kt as NetworkError,Dr as OPENNEXT_DEPLOY_ORDER,fr as OPENNEXT_DESTROY_ORDER,gr as OPENNEXT_PARALLEL_GROUPS,Xr as OPENNEXT_PATTERNS,dr as ORGANISATION_TYPES,Ua as ORG_SETUP_PHASES,Xo as ORG_TRAIL_BUCKET_OUTPUT_KEY,ha as OU_ONLY_ACCOUNT_BUCKETS,Cr as PARALLEL_DEPLOY_GROUPS,Nr as PARALLEL_DESTROY_GROUPS,Lr as PARALLEL_OPERATION_TYPES,k as PHASE_STATUSES,Y as PLAN_ACTIONS,yt as PROGRESS_MESSAGES,A as PlanChangeSchema,R as PlanPropertyChangeSchema,mr as ProgressReporter,Ii as RECREATE_MARKER_KEY,Ri as REMEDIATION_JOURNAL_RETENTION_DAYS,nr as REMEDIATION_PHASES,Xe as REMEDIATION_VERB_GLOSS,B as REPLACEMENT_MODES,Me as ROOT_ACCESS_FEATURES,Pa as ROOT_TASK_POLICY_ARNS,Sr as RemediationFlipProofSchema,lr as RemediationJournalRecordSchema,cr as RemediationPhaseSchema,Er as RemediationTargetSchema,Ke as RemediationVerbSchema,Tr as RemediationVerbWithSurgerySchema,Ue as RootAccessEnablementError,xe as SECURITY_SERVICE_PRINCIPALS,Ee as SERVICE_PRINCIPALS,Vi as SNAPSHOT_CAPABLE_RESOURCE_TYPES,x as STACK_CLEANUP_PHASES,wr as STACK_FAILED_STATE_PATTERN,Jr as STACK_NOT_FOUND_PATTERN,h as STEP_COMPLETE_STATUSES,be as STEP_IDS,Ve as STEP_NAMES,P as SUPPORTED_CONTRACT_MARKERS,eo as ServiceArtefactSchema,ro as ServiceArtefactsSchema,Q as SimpleAwsProvider,ra as StackUnavailableError,T as StepProgressSchema,za as StepRegistry,Ze as TICKET_VERDICT_SOURCES,Ho as TRAIL_BUCKET_OUTPUT_KEY,Jo as TRAIL_KEY_ARN_OUTPUT_KEY,gt as TemplateHashError,Nt as TemplateHashService,ze as TicketVerdictSourceSchema,Vt as ValidationError,ga as accountsConfigToOUTree,Oe as activateCostAllocationTags,le as activateTrustedAccess,Xi as advertisableDriftVerbs,Cs as advertisableRemediationVerbs,ui as applyRetainFlip,si as archiveCompletedRemediationJournal,zt as artefactOutputKey,ta as assertLeaseBeforeStack,yi as assessForgetResumability,ua as assumeRootForTask,ho as buildAccountRegionPairs,de as buildAccountToOUMap,Ka as buildBuildSecretSessionPolicy,rs as buildDeployPlan,Xa as buildDeploySessionPolicy,ds as buildDestructionTicket,Hi as buildDriftRepairTicket,yo as buildRegionList,vt as buildStepContextBuildConfig,$ as buildTargetSetUnreadyAdvisory,z as buildTargetUnreadyAdvisory,_i as captureRemediationForensics,vo as cascadeHomeRegion,xo as cascadeOperationKey,$i as checkDeployPathStackAvailability,De as checkIdentityCentreStatus,te as checkRegionEnabled,q as checkTargetReadiness,jo as classifyDriftFailure,as as classifyImpact,qo as clearDriftSuspects,hi as completeForgetAfterConverge,vi as completeForgetAfterDeploy,di as composeFlipCapabilities,qi as composePostFailureRepairOffer,Ce as composeSdkAbortSignal,os as computeAssemblyDigest,ts as computeDeployPlan,ni as computeRemediationOpId,fo as computeSecretsDrift,Pi as computeTemplateDigest,Os as computeTicketDigest,xt as convertCloudFormationOutputsToRecord,_e as createAccount,H as createDeployEmitter,et as createEmptyState,Is as createMemoisedPlanLoader,no as createSequencedCallbacks,Bo as decideNextTransition,Ko as decommissionMemberTrailStorage,Zo as defaultDriftJournalDir,mi as defaultRemediationForensicsDir,ci as defaultRemediationJournalDir,rt as deleteStateFile,_o as deploy,ss as derivePropertyChanges,Bi as deriveRemovalTemplate,Hr as deriveResourcesFromManifestStacks,Oi as deriveRetainFlipTemplate,Co as deriveStaleParameters,se as describeOrganisation,Z as describeTargetReadinessReason,uo as destroy,ei as detectStackDrift,is as digestHead,qa as dockerBuilder,ye as drainPages,It as emitProgress,Fe as enableCentralisedRootAccess,Te as enableIpamDelegatedAdmin,ne as enablePolicyTypes,Se as enableRamSharing,ce as enableServiceAccess,pa as ensureEnforceSsl,ae as ensureOrganisationExists,me as ensureOrganisationalUnitsExist,Ps as evaluateDestructionConsents,fs as executableRemediationVerbs,Na as extractAllAccountNames,No as extractConsumedSsmParameters,Ne as extractErrorName,To as failure,Qo as fetchLastOperationEvents,Eo as fileExists,jt as filterDangerousEnvVars,Re as findAccount,Pe as findDevelopmentOuId,Di as findMarkedResourcesByOpId,Ei as findRemediationJournalByOpId,Gi as findRemovalDeletionFailures,Ca as flattenAccountsToEnvironments,ti as formatDriftPreFlightBlock,Zi as formatDriftRepairOffer,Rt as formatInfrastructureError,wi as formatPinOutcomeDetail,xi as formatRecreateInFlightCures,xr as getApplicationDeployOrder,Fr as getApplicationDestroyOrder,Mr as getApplicationStackName,Br as getApplicationStepId,Yr as getApplicationStepName,$a as getDestroyStepId,Ur as getOrganisationStackName,hr as getParallelDeployGroups,vr as getParallelDestroyGroups,mt as getSourceContext,ot as getStateFilePath,_t as getStructuralHint,ut as hasCdkDifferences,oo as hasDockerfile,ge as isAborted,Ia as isAccountsConfig,Ir as isApplicationOperation,kr as isApplicationStack,At as isCdkError,ns as isDataLoss,ea as isDeployPathBlockingStatus,Ea as isEnforceSslStatement,Ao as isFailure,he as isOULeaf,Kr as isOpenNextPattern,yr as isOrganisationOperation,ya as isOuOnlyAccountBucket,br as isQuarantineDetail,Rr as isRemediationPlaceholderPhysicalId,Vr as isRetainedBucketsDetail,da as isRootTaskPolicyArn,cs as isStatefulResourceType,La as isStringArray,po as isSuccess,Ds as legalRemediationVerbs,Ae as listAccounts,Si as listActiveRemediationOps,li as listActiveRemediationOpsForContext,Ls as loadDeployPlan,Qt as maskSensitiveOutput,Vo as mergeReconciledProviderAccounts,Wa as openNextBuilder,fa as parseAccountsConfiguration,ht as parseBuildPhase,w as parseDeployContractVersion,J as parseDeployEvent,dt as parseDiffOutput,Wt as parseShellArgs,Lo as partitionAccounts,Ha as partitionForRegion,Fi as partitionPreFlightByRemediation,ji as pinProbeCoverage,ue as placeAccountsInOUs,ee as probeAccountStackExists,Do as probeSecretsDrift,Uo as projectAccountRows,Mo as projectScalarSummary,fi as proveFlipMetadataOnly,Ki as proveRemovalTargetsOnly,zo as readDriftSuspects,Ti as readRemediationJournal,Ci as readRetainFlipState,zr as readStateFile,bo as reconcileProviderAccounts,Go as reconcileTrailMigration,$o as recordDriftSuspects,oe as regionDisabledMessage,ve as registerSecurityDelegates,pr as remediationPhaseRank,Ar as remediationPlaceholderPhysicalId,gs as renderConsentVerdictLines,Ns as renderDestructionTicketLines,Qi as renderPinReportLines,Ss as renderPlanLines,Es as renderPlanSummary,Ga as resolveBuildArgs,Ya as resolveBuildSecretSessionProvider,ba as resolveBuildSecrets,Va as resolveSecretRefValue,Oo as restart,Aa as restoreAndReconcileQuarantinedBucket,la as restoreBucketPolicy,_s as runApprovalGate,us as runDestructionGate,ri as runDriftPreFlight,Ji as runDriftRepairGate,Mi as runForgetSurgery,xa as runOpenNextBuild,Ma as runOrganisationSetup,Wi as runPinRemediation,Yi as runRecreatePreFlight,ki as runRecreateSurgery,Ts as signApprovalToken,qt as sleep,ao as sleepAbortable,fe as snapshotIdentityCentre,bi as snapshotPolicyForType,Ba as sourcedRefsFromBuildArgs,Tt as startStackMonitoring,it as stubCallerIdentity,lo as success,pi as sweepExpiredRemediationJournals,Ui as synthTemplateDeclaresResource,Ta as synthesiseEnforceSslDocument,Gr as toPascalCase,Sa as toResourcePolicyStatements,Jt as toServiceError,ls as toWirePlanChanges,ca as triageBucketPolicy,aa as unlockBucket,sa as unlockQueue,pe as updateBackupGlobalSettings,tt as updateTemplateHash,Ja as validateBuildGroup,ar as verbsForDriftVerdict,ps as verifyApprovalToken,_a as verifyOrgTrailDelivery,Ni as verifyRetainFlipDiff,qr as wrapApplicationError,Ai as writeRemediationJournal,$r 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 n,CASCADE_PHASES as c,CASCADE_ACCOUNT_STATUSES as E}from"./types/index.js";import{DeployEventSchema as l,StepProgressSchema as p,DeployResourceSchema as T,PlanChangeSchema as A,PlanPropertyChangeSchema as R,ChangeCountsSchema as _,DEPLOY_EVENT_CONTRACT as m,DEPLOY_EVENT_CONTRACT_MAJOR as u,DEPLOY_EVENT_CONTRACT_MINOR as d,SUPPORTED_CONTRACT_MARKERS as P,DEPLOY_EVENT_ID_MAX as O,DEPLOY_EVENT_NAME_MAX as D,DEPLOY_EVENT_MESSAGE_MAX as f,DEPLOY_EVENT_STATUS_REASON_MAX as C,DEPLOY_EVENT_ERROR_MESSAGE_MAX as N,DEPLOY_EVENT_TRAIL_DETAIL_MAX as g,DEPLOY_EVENT_OUTPUT_MAX as L,DEPLOY_EVENT_ARN_MAX as I,DEPLOY_EVENT_URL_MAX as y,STEP_COMPLETE_STATUSES as h,LOG_LEVELS as v,STACK_CLEANUP_PHASES as x,CASCADE_ACCOUNT_PHASES as F,DEPLOY_RESULTS as M,DETECTION_PHASES as U,PHASE_STATUSES as k,BUILDERS as b,CASCADE_OUTCOME_RESULTS as V,PLAN_ACTIONS as Y,REPLACEMENT_MODES as B,APPROVAL_KINDS as G,APPROVAL_DECISIONS as K,ALL_PROGRESS_KINDS as X,createDeployEmitter as H,parseDeployEvent as J,parseDeployContractVersion as w}from"./types/index.js";import{SimpleAwsProvider as Q}from"./aws/index.js";import{checkTargetReadiness as q,describeTargetReadinessReason as Z,buildTargetUnreadyAdvisory as z,buildTargetSetUnreadyAdvisory as $,probeAccountStackExists as ee}from"./aws/index.js";import{checkRegionEnabled as te,regionDisabledMessage as oe}from"./aws/index.js";import{ensureOrganisationExists as ae,describeOrganisation as se,enablePolicyTypes as ne,enableServiceAccess as ce,SERVICE_PRINCIPALS as Ee,enableRamSharing as Se,activateTrustedAccess as le,enableIpamDelegatedAdmin as pe,updateBackupGlobalSettings as Te,listAccounts as Ae,findAccount as Re,createAccount as _e,ensureOrganisationalUnitsExist as me,placeAccountsInOUs as ue,buildAccountToOUMap as de,findDevelopmentOuId as Pe,activateCostAllocationTags as Oe,checkIdentityCentreStatus as De,snapshotIdentityCentre as fe,composeSdkAbortSignal as Ce,extractErrorName as Ne,isAborted as ge,DEFAULT_MAX_PAGES as Le,IDENTITY_STORE_PAGE_SIZE as Ie,drainPages as ye,isOULeaf as he,registerSecurityDelegates as ve,SECURITY_SERVICE_PRINCIPALS as xe,enableCentralisedRootAccess as Fe,ROOT_ACCESS_FEATURES as Me,RootAccessEnablementError as Ue}from"./aws/index.js";import{STEP_IDS as be,STEP_NAMES as Ve,INFRASTRUCTURE_STEP_NAMES as Ye,INFRA_STEP_NAME as Be}from"./types/index.js";import{RemediationVerbSchema as Ke,REMEDIATION_VERB_GLOSS as Xe,DestructionFindingSchema as He,DestructionTicketResourceSchema as Je,DestructionTicketSchema as we,DestructionConsentSchema as je,DestructionOutcomeSchema as Qe,DESTRUCTION_FINDINGS as We,CONSENT_VERDICTS as qe,TICKET_VERDICT_SOURCES as Ze,TicketVerdictSourceSchema as ze}from"./types/index.js";import{DRIFT_VERDICTS as er,DriftVerdictSchema as rr,DriftSuspectSchema as tr,DriftFindingSchema as or,DriftJournalRecordSchema as ir,verbsForDriftVerdict as ar}from"./types/index.js";import{REMEDIATION_PHASES as nr,RemediationPhaseSchema as cr,RemediationTargetSchema as Er,RemediationFlipProofSchema as Sr,RemediationJournalRecordSchema as lr,RemediationVerbWithSurgerySchema as pr,remediationPhaseRank as Tr,remediationPlaceholderPhysicalId as Ar,isRemediationPlaceholderPhysicalId as Rr}from"./types/index.js";import{ProgressReporter as mr,APPLICATION_STACKS as ur,ORGANISATION_TYPES as dr,APPLICATION_DEPLOY_ORDER as Pr,APPLICATION_DESTROY_ORDER as Or,OPENNEXT_DEPLOY_ORDER as Dr,OPENNEXT_DESTROY_ORDER as fr,PARALLEL_DEPLOY_GROUPS as Cr,PARALLEL_DESTROY_GROUPS as Nr,OPENNEXT_PARALLEL_GROUPS as gr,PARALLEL_OPERATION_TYPES as Lr,isApplicationOperation as Ir,isOrganisationOperation as yr,getParallelDeployGroups as hr,getParallelDestroyGroups as vr,getApplicationDeployOrder as xr,getApplicationDestroyOrder as Fr,getApplicationStackName as Mr,getOrganisationStackName as Ur,isApplicationStack as kr,isQuarantineDetail as br,isRetainedBucketsDetail as Vr,getApplicationStepName as Yr,getApplicationStepId as Br,toPascalCase as Gr,isOpenNextPattern as Kr,OPENNEXT_PATTERNS as Xr,STACK_NOT_FOUND_PATTERN as Hr,STACK_FAILED_STATE_PATTERN as Jr,CDK_NO_STACKS_MATCH as wr,INFRASTRUCTURE_FILENAME as jr,ApplicationError as Qr,wrapApplicationError as Wr,stubCallerIdentity as qr}from"./types/index.js";import{deriveResourcesFromManifestStacks as zr}from"./types/detection/patternDetection.js";import{FjallStateFileSchema as et,readStateFile as rt,writeStateFile as tt,createEmptyState as ot,deleteStateFile as it,updateTemplateHash as at,getStateFilePath as st,regionSuffix as nt}from"./types/config/FjallState.js";import{CloudFormationEventMonitor as Et}from"./aws/index.js";import{CdkService as lt,CdkArgumentBuilder as pt,CdkProcessManager as Tt,CdkEventMonitor as At,startStackMonitoring as Rt,DEFAULT_DEPLOY_TIMEOUT_MS as _t,isCdkError as mt,formatInfrastructureError as ut,getStructuralHint as dt,getSourceContext as Pt,hasCdkDifferences as Ot,parseDiffOutput as Dt,CloudFormationService as ft,CloudFormationError as Ct,EcsService as Nt,EcsError as gt,EcsServiceResolver as Lt,TemplateHashService as It,TemplateHashError as yt,CdkContextBuilder as ht,emitProgress as vt,PROGRESS_MESSAGES as xt,parseBuildPhase as Ft,buildStepContextBuildConfig as Mt,convertCloudFormationOutputsToRecord as Ut,ApplicationStackService as kt}from"./services/index.js";import{CdkError as Vt}from"./types/errors/index.js";import{BaseServiceError as Bt,ValidationError as Gt,AuthError as Kt,AwsError as Xt,DeploymentError as Ht,NetworkError as Jt,FileSystemError as wt,ConfigError as jt,toServiceError as Qt}from"./types/errors/index.js";import{filterDangerousEnvVars as qt,maskSensitiveOutput as Zt,parseShellArgs as zt,sleep as $t}from"@fjall/util";import{artefactOutputKey as ro,DeployModeSchema as to,ServiceArtefactSchema as oo,ServiceArtefactsSchema as io}from"@fjall/util";import{hasDockerfile as so}from"./util/dockerfileDetection.js";import{sleepAbortable as co}from"./util/sleepAbortable.js";import{createSequencedCallbacks as So}from"./util/sequencedCallbacks.js";import{fileExists as po}from"@fjall/util/fsHelpers";import{success as Ao,failure as Ro,isSuccess as _o,isFailure as mo}from"@fjall/generator";import{deploy as Po}from"./orchestration/index.js";import{destroy as Do}from"./orchestration/index.js";import{restart as Co,probeSecretsDrift as No,computeSecretsDrift as go,deriveStaleParameters as Lo,extractConsumedSsmParameters as Io}from"./orchestration/index.js";import{partitionAccounts as ho}from"./orchestration/index.js";import{buildRegionList as xo,buildAccountRegionPairs as Fo,cascadeHomeRegion as Mo,cascadeOperationKey as Uo}from"./orchestration/index.js";import{projectScalarSummary as bo,projectAccountRows as Vo}from"./orchestration/index.js";import{reconcileProviderAccounts as Bo,mergeReconciledProviderAccounts as Go}from"./orchestration/index.js";import{decideNextTransition as Xo,reconcileTrailMigration as Ho,decommissionMemberTrailStorage as Jo,ORG_TRAIL_BUCKET_OUTPUT_KEY as wo,TRAIL_BUCKET_OUTPUT_KEY as jo,TRAIL_KEY_ARN_OUTPUT_KEY as Qo}from"./orchestration/index.js";import{classifyDriftFailure as qo,fetchLastOperationEvents as Zo,DriftProbe as zo,clearDriftSuspects as $o,defaultDriftJournalDir as ei,readDriftSuspects as ri,recordDriftSuspects as ti,detectStackDrift as oi,runDriftPreFlight as ii,formatDriftPreFlightBlock as ai,DRIFT_PREFLIGHT_TRIGGER_STATUSES as si,DRIFT_PREFLIGHT_BUDGET_MS as ni}from"./orchestration/index.js";import{archiveCompletedRemediationJournal as Ei,computeRemediationOpId as Si,defaultRemediationJournalDir as li,findRemediationJournalByOpId as pi,listActiveRemediationOps as Ti,listActiveRemediationOpsForContext as Ai,readRemediationJournal as Ri,sweepExpiredRemediationJournals as _i,writeRemediationJournal as mi,REMEDIATION_JOURNAL_RETENTION_DAYS as ui,captureRemediationForensics as di,defaultRemediationForensicsDir as Pi,applyRetainFlip as Oi,composeFlipCapabilities as Di,computeTemplateDigest as fi,deriveRetainFlipTemplate as Ci,findMarkedResourcesByOpId as Ni,proveFlipMetadataOnly as gi,readRetainFlipState as Li,verifyRetainFlipDiff as Ii,FORGET_MARKER_KEY as yi,FORGET_FLIP_SPEC as hi,RECREATE_MARKER_KEY as vi,assessForgetResumability as xi,completeForgetAfterConverge as Fi,completeForgetAfterDeploy as Mi,formatRecreateInFlightCures as Ui,partitionPreFlightByRemediation as ki,runForgetSurgery as bi,synthTemplateDeclaresResource as Vi,runRecreateSurgery as Yi,snapshotPolicyForType as Bi,SNAPSHOT_CAPABLE_RESOURCE_TYPES as Gi,runRecreatePreFlight as Ki,deriveRemovalTemplate as Xi,findRemovalDeletionFailures as Hi,proveRemovalTargetsOnly as Ji,advertisableDriftVerbs as wi,buildDriftRepairTicket as ji,runDriftRepairGate as Qi,formatPinOutcomeDetail as Wi,pinProbeCoverage as qi,renderPinReportLines as Zi,runPinRemediation as zi,composePostFailureRepairOffer as $i,formatDriftRepairOffer as ea,DriftRepairAvailableError as ra,checkDeployPathStackAvailability as ta,isDeployPathBlockingStatus as oa,StackUnavailableError as ia,assertLeaseBeforeStack as aa,LeaseDeniedError as sa}from"./orchestration/index.js";import{unlockBucket as ca,unlockQueue as Ea}from"./orchestration/index.js";import{triageBucketPolicy as la,isEnforceSslStatement as pa,toResourcePolicyStatements as Ta,restoreBucketPolicy as Aa,synthesiseEnforceSslDocument as Ra,ensureEnforceSsl as _a,restoreAndReconcileQuarantinedBucket as ma}from"./orchestration/index.js";import{verifyOrgTrailDelivery as da}from"./aws/cloudtrail/orgTrailDelivery.js";import{assumeRootForTask as Oa,isRootTaskPolicyArn as Da,ROOT_TASK_POLICY_ARNS as fa,MAX_ROOT_SESSION_SECONDS as Ca}from"./aws/sts/assumeRoot.js";import{parseAccountsConfiguration as ga,flattenAccountsToEnvironments as La,extractAllAccountNames as Ia,accountsConfigToOUTree as ya,isStringArray as ha,isAccountsConfig as va,isOuOnlyAccountBucket as xa,OU_ONLY_ACCOUNT_BUCKETS as Fa}from"./orchestration/index.js";import{runOpenNextBuild as Ua}from"./orchestration/index.js";import{runOrganisationSetup as ba,ORG_SETUP_PHASES as Va}from"./orchestration/index.js";import{resolveBuildSecrets as Ba,resolveSecretRefValue as Ga,resolveBuildSecretSessionProvider as Ka,sourcedRefsFromBuildArgs as Xa,resolveBuildArgs as Ha,buildBuildSecretSessionPolicy as Ja,buildDeploySessionPolicy as wa,partitionForRegion as ja,validateBuildGroup as Qa}from"./orchestration/index.js";import{FrameworkRegistry as qa}from"./orchestration/index.js";import{openNextBuilder as za,staticSiteBuilder as $a,dockerBuilder as es}from"./orchestration/index.js";import{StepRegistry as ts,getDestroyStepId as os}from"./steps/index.js";import{buildDeployPlan as as,computeDeployPlan as ss,computeAssemblyDigest as ns,digestHead as cs,classifyImpact as Es,derivePropertyChanges as Ss,isDataLoss as ls,isStatefulResourceType as ps,renderPlanSummary as Ts,renderPlanLines as As,toWirePlanChanges as Rs,signApprovalToken as _s,verifyApprovalToken as ms,DEFAULT_APPROVAL_TTL_MS as us,APPROVAL_TOKEN_PATTERN as ds,runApprovalGate as Ps}from"./orchestration/index.js";import{runDestructionGate as Ds,buildDestructionTicket as fs,evaluateDestructionConsents as Cs,computeTicketDigest as Ns,legalRemediationVerbs as gs,executableRemediationVerbs as Ls,advertisableRemediationVerbs as Is,renderDestructionTicketLines as ys,renderConsentVerdictLines as hs,loadDeployPlan as vs,createMemoisedPlanLoader as xs}from"./orchestration/index.js";export{X as ALL_PROGRESS_KINDS,Pr as APPLICATION_DEPLOY_ORDER,Or as APPLICATION_DESTROY_ORDER,ur as APPLICATION_STACKS,K as APPROVAL_DECISIONS,G as APPROVAL_KINDS,ds as APPROVAL_TOKEN_PATTERN,Qr as ApplicationError,kt as ApplicationStackService,Kt as AuthError,Xt as AwsError,b as BUILDERS,Bt as BaseServiceError,F as CASCADE_ACCOUNT_PHASES,E as CASCADE_ACCOUNT_STATUSES,V as CASCADE_OUTCOME_RESULTS,c as CASCADE_PHASES,wr as CDK_NO_STACKS_MATCH,qe as CONSENT_VERDICTS,pt as CdkArgumentBuilder,ht as CdkContextBuilder,Vt as CdkError,At as CdkEventMonitor,Tt as CdkProcessManager,lt as CdkService,_ as ChangeCountsSchema,Ct as CloudFormationError,Et as CloudFormationEventMonitor,ft as CloudFormationService,jt as ConfigError,us as DEFAULT_APPROVAL_TTL_MS,_t as DEFAULT_DEPLOY_TIMEOUT_MS,Le as DEFAULT_MAX_PAGES,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,I as DEPLOY_EVENT_ARN_MAX,m as DEPLOY_EVENT_CONTRACT,u as DEPLOY_EVENT_CONTRACT_MAJOR,d as DEPLOY_EVENT_CONTRACT_MINOR,N as DEPLOY_EVENT_ERROR_MESSAGE_MAX,O as DEPLOY_EVENT_ID_MAX,f as DEPLOY_EVENT_MESSAGE_MAX,D as DEPLOY_EVENT_NAME_MAX,L as DEPLOY_EVENT_OUTPUT_MAX,C as DEPLOY_EVENT_STATUS_REASON_MAX,g as DEPLOY_EVENT_TRAIL_DETAIL_MAX,y as DEPLOY_EVENT_URL_MAX,M as DEPLOY_RESULTS,We as DESTRUCTION_FINDINGS,U as DETECTION_PHASES,ni as DRIFT_PREFLIGHT_BUDGET_MS,si as DRIFT_PREFLIGHT_TRIGGER_STATUSES,er as DRIFT_VERDICTS,l as DeployEventSchema,to as DeployModeSchema,T as DeployResourceSchema,Ht as DeploymentError,t as DeploymentEventSchema,je as DestructionConsentSchema,He as DestructionFindingSchema,Qe as DestructionOutcomeSchema,Je as DestructionTicketResourceSchema,we as DestructionTicketSchema,or as DriftFindingSchema,ir as DriftJournalRecordSchema,zo as DriftProbe,ra as DriftRepairAvailableError,tr as DriftSuspectSchema,rr as DriftVerdictSchema,gt as EcsError,Nt as EcsService,Lt as EcsServiceResolver,hi as FORGET_FLIP_SPEC,yi as FORGET_MARKER_KEY,wt as FileSystemError,et as FjallStateFileSchema,qa as FrameworkRegistry,Ie as IDENTITY_STORE_PAGE_SIZE,jr as INFRASTRUCTURE_FILENAME,Ye as INFRASTRUCTURE_STEP_NAMES,Be as INFRA_STEP_NAME,v as LOG_LEVELS,sa as LeaseDeniedError,Ca as MAX_ROOT_SESSION_SECONDS,Jt as NetworkError,Dr as OPENNEXT_DEPLOY_ORDER,fr as OPENNEXT_DESTROY_ORDER,gr as OPENNEXT_PARALLEL_GROUPS,Xr as OPENNEXT_PATTERNS,dr as ORGANISATION_TYPES,Va as ORG_SETUP_PHASES,wo as ORG_TRAIL_BUCKET_OUTPUT_KEY,Fa as OU_ONLY_ACCOUNT_BUCKETS,Cr as PARALLEL_DEPLOY_GROUPS,Nr as PARALLEL_DESTROY_GROUPS,Lr as PARALLEL_OPERATION_TYPES,k as PHASE_STATUSES,Y as PLAN_ACTIONS,xt as PROGRESS_MESSAGES,A as PlanChangeSchema,R as PlanPropertyChangeSchema,mr as ProgressReporter,vi as RECREATE_MARKER_KEY,ui as REMEDIATION_JOURNAL_RETENTION_DAYS,nr as REMEDIATION_PHASES,Xe as REMEDIATION_VERB_GLOSS,B as REPLACEMENT_MODES,Me as ROOT_ACCESS_FEATURES,fa as ROOT_TASK_POLICY_ARNS,Sr as RemediationFlipProofSchema,lr as RemediationJournalRecordSchema,cr as RemediationPhaseSchema,Er as RemediationTargetSchema,Ke as RemediationVerbSchema,pr as RemediationVerbWithSurgerySchema,Ue as RootAccessEnablementError,xe as SECURITY_SERVICE_PRINCIPALS,Ee as SERVICE_PRINCIPALS,Gi as SNAPSHOT_CAPABLE_RESOURCE_TYPES,x as STACK_CLEANUP_PHASES,Jr as STACK_FAILED_STATE_PATTERN,Hr as STACK_NOT_FOUND_PATTERN,h as STEP_COMPLETE_STATUSES,be as STEP_IDS,Ve as STEP_NAMES,P as SUPPORTED_CONTRACT_MARKERS,oo as ServiceArtefactSchema,io as ServiceArtefactsSchema,Q as SimpleAwsProvider,ia as StackUnavailableError,p as StepProgressSchema,ts as StepRegistry,Ze as TICKET_VERDICT_SOURCES,jo as TRAIL_BUCKET_OUTPUT_KEY,Qo as TRAIL_KEY_ARN_OUTPUT_KEY,yt as TemplateHashError,It as TemplateHashService,ze as TicketVerdictSourceSchema,Gt as ValidationError,ya as accountsConfigToOUTree,Oe as activateCostAllocationTags,le as activateTrustedAccess,wi as advertisableDriftVerbs,Is as advertisableRemediationVerbs,Oi as applyRetainFlip,Ei as archiveCompletedRemediationJournal,ro as artefactOutputKey,aa as assertLeaseBeforeStack,xi as assessForgetResumability,Oa as assumeRootForTask,Fo as buildAccountRegionPairs,de as buildAccountToOUMap,Ja as buildBuildSecretSessionPolicy,as as buildDeployPlan,wa as buildDeploySessionPolicy,fs as buildDestructionTicket,ji as buildDriftRepairTicket,xo as buildRegionList,Mt as buildStepContextBuildConfig,$ as buildTargetSetUnreadyAdvisory,z as buildTargetUnreadyAdvisory,di as captureRemediationForensics,Mo as cascadeHomeRegion,Uo as cascadeOperationKey,ta as checkDeployPathStackAvailability,De as checkIdentityCentreStatus,te as checkRegionEnabled,q as checkTargetReadiness,qo as classifyDriftFailure,Es as classifyImpact,$o as clearDriftSuspects,Fi as completeForgetAfterConverge,Mi as completeForgetAfterDeploy,Di as composeFlipCapabilities,$i as composePostFailureRepairOffer,Ce as composeSdkAbortSignal,ns as computeAssemblyDigest,ss as computeDeployPlan,Si as computeRemediationOpId,go as computeSecretsDrift,fi as computeTemplateDigest,Ns as computeTicketDigest,Ut as convertCloudFormationOutputsToRecord,_e as createAccount,H as createDeployEmitter,ot as createEmptyState,xs as createMemoisedPlanLoader,So as createSequencedCallbacks,Xo as decideNextTransition,Jo as decommissionMemberTrailStorage,ei as defaultDriftJournalDir,Pi as defaultRemediationForensicsDir,li as defaultRemediationJournalDir,it as deleteStateFile,Po as deploy,Ss as derivePropertyChanges,Xi as deriveRemovalTemplate,zr as deriveResourcesFromManifestStacks,Ci as deriveRetainFlipTemplate,Lo as deriveStaleParameters,se as describeOrganisation,Z as describeTargetReadinessReason,Do as destroy,oi as detectStackDrift,cs as digestHead,es as dockerBuilder,ye as drainPages,vt as emitProgress,Fe as enableCentralisedRootAccess,pe as enableIpamDelegatedAdmin,ne as enablePolicyTypes,Se as enableRamSharing,ce as enableServiceAccess,_a as ensureEnforceSsl,ae as ensureOrganisationExists,me as ensureOrganisationalUnitsExist,Cs as evaluateDestructionConsents,Ls as executableRemediationVerbs,Ia as extractAllAccountNames,Io as extractConsumedSsmParameters,Ne as extractErrorName,Ro as failure,Zo as fetchLastOperationEvents,po as fileExists,qt as filterDangerousEnvVars,Re as findAccount,Pe as findDevelopmentOuId,Ni as findMarkedResourcesByOpId,pi as findRemediationJournalByOpId,Hi as findRemovalDeletionFailures,La as flattenAccountsToEnvironments,ai as formatDriftPreFlightBlock,ea as formatDriftRepairOffer,ut as formatInfrastructureError,Wi as formatPinOutcomeDetail,Ui as formatRecreateInFlightCures,xr as getApplicationDeployOrder,Fr as getApplicationDestroyOrder,Mr as getApplicationStackName,Br as getApplicationStepId,Yr as getApplicationStepName,os as getDestroyStepId,Ur as getOrganisationStackName,hr as getParallelDeployGroups,vr as getParallelDestroyGroups,Pt as getSourceContext,st as getStateFilePath,dt as getStructuralHint,Ot as hasCdkDifferences,so as hasDockerfile,ge as isAborted,va as isAccountsConfig,Ir as isApplicationOperation,kr as isApplicationStack,mt as isCdkError,ls as isDataLoss,oa as isDeployPathBlockingStatus,pa as isEnforceSslStatement,mo as isFailure,he as isOULeaf,Kr as isOpenNextPattern,yr as isOrganisationOperation,xa as isOuOnlyAccountBucket,br as isQuarantineDetail,Rr as isRemediationPlaceholderPhysicalId,Vr as isRetainedBucketsDetail,Da as isRootTaskPolicyArn,ps as isStatefulResourceType,ha as isStringArray,_o as isSuccess,gs as legalRemediationVerbs,Ae as listAccounts,Ti as listActiveRemediationOps,Ai as listActiveRemediationOpsForContext,vs as loadDeployPlan,Zt as maskSensitiveOutput,Go as mergeReconciledProviderAccounts,za as openNextBuilder,ga as parseAccountsConfiguration,Ft as parseBuildPhase,w as parseDeployContractVersion,J as parseDeployEvent,Dt as parseDiffOutput,zt as parseShellArgs,ho as partitionAccounts,ja as partitionForRegion,ki as partitionPreFlightByRemediation,qi as pinProbeCoverage,ue as placeAccountsInOUs,ee as probeAccountStackExists,No as probeSecretsDrift,Vo as projectAccountRows,bo as projectScalarSummary,gi as proveFlipMetadataOnly,Ji as proveRemovalTargetsOnly,ri as readDriftSuspects,Ri as readRemediationJournal,Li as readRetainFlipState,rt as readStateFile,Bo as reconcileProviderAccounts,Ho as reconcileTrailMigration,ti as recordDriftSuspects,oe as regionDisabledMessage,nt as regionSuffix,ve as registerSecurityDelegates,Tr as remediationPhaseRank,Ar as remediationPlaceholderPhysicalId,hs as renderConsentVerdictLines,ys as renderDestructionTicketLines,Zi as renderPinReportLines,As as renderPlanLines,Ts as renderPlanSummary,Ha as resolveBuildArgs,Ka as resolveBuildSecretSessionProvider,Ba as resolveBuildSecrets,Ga as resolveSecretRefValue,Co as restart,ma as restoreAndReconcileQuarantinedBucket,Aa as restoreBucketPolicy,Ps as runApprovalGate,Ds as runDestructionGate,ii as runDriftPreFlight,Qi as runDriftRepairGate,bi as runForgetSurgery,Ua as runOpenNextBuild,ba as runOrganisationSetup,zi as runPinRemediation,Ki as runRecreatePreFlight,Yi as runRecreateSurgery,_s as signApprovalToken,$t as sleep,co as sleepAbortable,fe as snapshotIdentityCentre,Bi as snapshotPolicyForType,Xa as sourcedRefsFromBuildArgs,Rt as startStackMonitoring,$a as staticSiteBuilder,qr as stubCallerIdentity,Ao as success,_i as sweepExpiredRemediationJournals,Vi as synthTemplateDeclaresResource,Ra as synthesiseEnforceSslDocument,Gr as toPascalCase,Ta as toResourcePolicyStatements,Qt as toServiceError,Rs as toWirePlanChanges,la as triageBucketPolicy,ca as unlockBucket,Ea as unlockQueue,Te as updateBackupGlobalSettings,at as updateTemplateHash,Qa as validateBuildGroup,ar as verbsForDriftVerdict,ms as verifyApprovalToken,da as verifyOrgTrailDelivery,Ii as verifyRetainFlipDiff,Wr as wrapApplicationError,mi as writeRemediationJournal,tt as writeStateFile};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{join as ie}from"path";import{CloudFormationClient as he}from"@aws-sdk/client-cloudformation";import{EC2Client as Se}from"@aws-sdk/client-ec2";import{success as E,failure as d}from"@fjall/generator";import{logger as M}from"@fjall/util/logger";import{ImageTagSchema as be,getErrorMessage as se,imageTagParameterName as we,maskSensitiveOutput as y}from"@fjall/util";import{parseDockerServicesFromManifest as Te}from"@fjall/util/manifest";import{stubCallerIdentity as Ne}from"../../types/deployment/index.js";import{getApplicationDeployOrder as Ee,getApplicationStackName as I,getApplicationStepName as ce,getApplicationStepId as le,APPLICATION_STACKS as P}from"../../types/operations.js";import{CdkContextBuilder as Pe}from"../../services/supporting/CdkContextBuilder.js";import{buildParamsContext as ve,bootstrapOrFail as De}from"../contextHelpers.js";import{runDetectionPipeline as Re}from"./detectionPipeline.js";import{STEP_IDS as G,STEP_NAMES as Ie}from"../../types/stepDefinitions.js";import{StepRegistry as Ce}from"../../steps/stepRegistry.js";import{checkTargetReadiness as Oe}from"../../aws/targetReadiness.js";import{cascadeHomeRegion as Ae}from"../organisation/cascadeHelpers.js";import{withStepLifecycle as Le}from"../stepLifecycle.js";import{DOCKER_BUILD_STEP_NAME as de,runDockerBuild as Me}from"./dockerBuildHelper.js";import{getParallelPhase2Stacks as Fe,getStackNamesToDeploy as $e,deployParallelPhase as xe,deployStackSequential as Be,runDockerPreCompute as je,createBuildCallbacks as Ge}from"./applicationDeployHelpers.js";import{captureDatabaseConnectionOutputs as _e,captureServiceTaskDefinitions as Ue,reconcileDatabaseEndpointChange as He}from"./databaseEndpointReconcile.js";import{deployCodeOnly as Ke}from"./codeOnlyDeploy.js";import{checkDeployPathStackAvailability as ue}from"../activeDeploymentGuard.js";import{assertLeaseBeforeStack as _}from"./leaseGate.js";import{collectFullDeployArtefacts as We}from"./artefactCollection.js";import{CfnRegistryService as qe}from"../../services/infrastructure/CfnRegistryService.js";import{ChangeSetProbe as ze}from"../../services/infrastructure/changeSetProbe.js";import{runApprovalGate as Je}from"./approvalGate.js";import{buildDestructionTicket as Ye,runDestructionGate as Qe}from"./destructionGate.js";import{DESTRUCTION_RESUME_TTL_MS as Ve,signApprovalToken as Xe}from"./plan/approvalToken.js";import{computeAssemblyDigest as Ze}from"./plan/assemblyDigest.js";import{createMemoisedPlanLoader as et}from"./plan/loadDeployPlan.js";import{runDriftPreFlightGate as tt}from"../drift/preFlightGate.js";import{completeForgetAfterDeploy as rt}from"../remediation/forgetResource.js";import{composePostFailureRepairOffer as pe}from"../remediation/postFailureRepairOffer.js";import{renderPinReportLines as at,runPinRemediation as nt}from"../remediation/pinRemediation.js";import{runRecreatePreFlight as ot}from"../remediation/recreatePreFlight.js";import{runRecreateSurgery as it}from"../remediation/recreateResource.js";const F="applicationDeploy";function st(e){const a=Object.entries(e);if(a.length===0)return;const n={};for(const[t,i]of a){const h=we(t);n[h]=i}return n}function ct(e){if(e?.mode!==void 0&&e.mode!=="rollback"){if(e.imageTag)return d(new Error(`--image-tag requires rollback mode (explicit mode '${e.mode}' was supplied alongside an image tag)`));if(e.serviceImageTags)return d(new Error(`serviceImageTags requires rollback mode (explicit mode '${e.mode}' was supplied alongside serviceImageTags)`))}if(e?.mode==="rollback"){if(e.imageTag&&e.serviceImageTags)return d(new Error("rollback mode accepts exactly one of --image-tag or serviceImageTags \u2014 they are mutually exclusive"));if(!e.imageTag&&!e.serviceImageTags)return d(new Error("rollback mode requires --image-tag <tag> or serviceImageTags naming the image(s) to roll to"))}if(e?.serviceImageTags!==void 0){if(e.serviceName)return d(new Error("serviceImageTags already names its services \u2014 --service cannot be combined with it"));const a=Object.entries(e.serviceImageTags);if(a.length===0)return d(new Error("serviceImageTags must name at least one service"));for(const[n,t]of a){const i=be.safeParse(t);if(!i.success)return d(new Error(`serviceImageTags.${n} is not a valid image tag: ${i.error.message}`))}}return E(void 0)}function ge(e,a,n){if(e.onStackTargets===void 0||n.length===0)return;const t=a.awsProvider.getAccountId(),i=a.awsProvider.getRegion();e.onStackTargets(n.map(h=>({stackName:h,...t!==""?{accountId:t}:{},...i!==""?{region:i}:{}})))}async function qt(e,a,n){const{callbacks:t,options:i}=e,h=Date.now(),$=i?.mode??(i?.imageTag||i?.serviceImageTags?"rollback":i?.deployOnly?"code-only":"full");if($==="restart"){const r=new Error('mode "restart" is not a deploy \u2014 call the restart() entry point (fjall rollout) instead');return t.onError?.(r),d(r)}const J=ct(i);if(!J.success){const r=y(J.error.message),o=new Error(r);return t.onError?.(o),d(o)}const x=Pe.buildDeploymentContext({deployType:"application",target:n.appName,path:n.path,region:a.awsProvider.getRegion(),callerIdentity:Ne(a.awsProvider.getAccountId()),...ve({orgConfig:e.orgConfig,identity:e.identity,skipOidc:e.options?.skipOidc})},{verbose:i?.verbose,infraOnly:i?.infraOnly},e.orgConfig),b=a.frameworkRegistry.resolve({appPath:n.path});let B;const C=$==="rollback";if(b&&!C){B=b.builder.plan({appPath:n.path},b.detection);const r=Ge(t),o=await b.builder.build(n.path,B,r,{skipBuild:i?.skipBuild,infraOnly:i?.infraOnly});if(!o.success){const s=new Error(y(o.error.message));return t.onError?.(s),d(s)}}if($==="code-only"||C){t.onLog?.(C?i?.serviceImageTags?`Rollback mode \u2014 rolling ${Object.keys(i.serviceImageTags).length} service(s) to their release-recorded image tags`:`Rollback mode \u2014 rolling to image tag ${i?.imageTag}`:"Deploy-only mode \u2014 skipping infrastructure pipeline","info");const r=ie(n.path,"cdk.out"),o=Te(r),s=o.length>0,p=b?.detection.hasDockerfile===!0,l=s||p;M.debug(F,"Deploy-only branch entered",{mode:$,imageTag:i?.imageTag,serviceImageTags:i?.serviceImageTags,appName:n.appName,appPath:n.path,cdkOutPath:r,dockerProviderAvailable:e.dockerProvider!==void 0,builderName:b?.builder.name,hasDockerfileFromManifest:s,hasDockerfileFromDisk:p,hasDockerfile:l,manifestDockerServiceCount:o.length,manifestDockerPaths:o.map(m=>m.docker.path)}),!s&&!p&&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");const u=await ue([I(n.appName,P.COMPUTE)],a.cfnService,e.abortSignal);if(!u.success)return t.onError?.(u.error),d(u.error);ge(t,a,[I(n.appName,P.COMPUTE)]);let k={};if(!C&&e.dockerProvider!==void 0&&l){M.debug(F,"Running Docker build before code-only deploy",{source:s?"manifest":"disk"});const m=await Me(e,a,n,t);if(!m.success)return d(m.error);k=m.data.contentHashTagsByService}else M.debug(F,"Skipping Docker build",{reason:C?"rollback":e.dockerProvider===void 0?"no dockerProvider":"no Dockerfile detected"});const c=await _(e.assertLeaseForStack,I(n.appName,P.COMPUTE),e.abortSignal);return c!==void 0?(t.onError?.(c),d(c)):Ke(e,a,n,k)}t.onLog?.("Analysing infrastructure\u2026","info");const U=await Re(n,a,x,t);if(!U.success){const r=new Error(y(U.error.message));return t.onError?.(r),d(r)}const f=U.data;try{await t.onDetectionComplete?.({...f,builderName:b?.builder.name??"unknown"})}catch(r){const o=new Error(y(se(r)));return t.onError?.(o),d(o)}const fe={deploymentType:"application",operation:"deploy",deployOnly:!1,infraOnly:i?.infraOnly??!1,hasDockerfile:f.hasDockerfile,pattern:f.pattern,resources:f.resources,...b&&{builderName:b.builder.name}},Y=Ce.getSteps(fe),Q=Y.findIndex(r=>r.id===G.TARGET_READINESS),V=await Le(t,{stepId:G.TARGET_READINESS,stepName:Ie.TARGET_READINESS,...Q>=0&&{stepIndex:Q,totalSteps:Y.length}},async()=>{if(i?.skipReadinessCheck)return t.onLog?.("Skipping target readiness check (--skip-readiness-check)","warn"),{kind:"skipped",data:void 0};const r=a.awsProvider.getAccountId(),o=e.orgConfig?.providerAccounts.find(u=>u.id===r)?.name,s=a.awsProvider.getCredentials(),p=Ae(e.orgConfig),l=await Oe({cloudFormation:a.awsProvider.getClient(he),ec2:new Se({region:p,...s!==void 0&&{credentials:s}})},{id:r,...o!==void 0&&{name:o}},a.awsProvider.getRegion(),e.orgConfig,e.abortSignal);return l.success?l.data.ready?{kind:"completed",data:void 0}:{kind:"error",error:new Error(y(l.data.advisory))}:{kind:"error",error:new Error(y(l.error.message))}},e.abortSignal);if(!V.success)return V;const w=B?B.deployOrder:Ee({pattern:f.pattern,resources:f.resources}),v=w.length,X=f.hasDockerfile&&e.dockerProvider!==void 0&&w.includes(P.COMPUTE)&&i?.infraOnly!==!0;if(!f.hasDifferences&&!i?.force&&!X){t.onLog?.("No infrastructure changes detected","info"),t.onLog?.(i?.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 r=f.hasDockerfile&&e.dockerProvider!==void 0&&w.includes(P.COMPUTE);for(let s=0;s<w.length;s++){const p=w[s];r&&p===P.COMPUTE&&(t.onStepStart?.(G.DOCKER_OPERATIONS,de),t.onStepComplete?.(G.DOCKER_OPERATIONS,de,"skipped"));const l=le(p,"deploy"),u=ce(p,"deploy");t.onStepStart?.(l,u,s,v),t.onStepComplete?.(l,u,"skipped",s,v)}const o=await a.stackService.resolveWebsiteUrl(n.appName);return E({target:n.appName,deploymentType:"application",outputs:o?{websiteUrl:o}:void 0,noChanges:!0,artefacts:[],durationMs:Date.now()-h})}const H=ie(n.path,"cdk.out"),N=$e({deployOrder:w,stackChanges:f.stackChanges,appName:n.appName,force:i?.force===!0,computeBuildPossible:X});ge(t,a,[...N]);const K=await ue([...N],a.cfnService,e.abortSignal);if(!K.success)return t.onError?.(K.error),d(K.error);const Z=Ze(f.currentHashes),W=et({cfnService:a.cfnService,cdkOutPath:H,changedStacks:[...N],assemblyDigest:Z,registry:new qe(a.awsProvider),changeSetProbe:new ze(a.awsProvider),...e.abortSignal!==void 0?{abortSignal:e.abortSignal}:{}}),q=await tt(a.awsProvider,{stackNames:[...N],cdkOutPath:H,cfnService:a.cfnService,callbacks:t,...e.abortSignal!==void 0?{abortSignal:e.abortSignal}:{}});if(q.kind==="blocked"){const r=new Error(q.message);return t.onError?.(r),d(r)}const ee=q.verifiedAbsentByNecessity??[],te=e.approvalGate!==void 0||i?.approvalToken!==void 0&&i.approvalToken!==""||i?.planOnly===!0;if(te){const r=await Je({approvalGate:e.approvalGate,options:i??{},target:n.appName,cdkOutPath:H,detection:{stackChanges:f.stackChanges,currentHashes:f.currentHashes},cfnService:a.cfnService,loadPlan:W,callbacks:t,...e.abortSignal!==void 0?{abortSignal:e.abortSignal}:{}});if(!r.success)return r;const o=r.data;if(o.kind==="rejected")return t.onLog?.(`Deploy rejected: ${y(o.reason)}`,"warn"),E({target:n.appName,deploymentType:"application",rejected:!0,artefacts:[],...o.planDetail!==void 0?{planDetail:o.planDetail}:{},durationMs:Date.now()-h});if(o.kind==="awaiting"){const s=await W(),p=s.success?Ye(s.data,ee):void 0;return E({target:n.appName,deploymentType:"application",awaitingApproval:!0,approvalToken:o.approvalToken,assemblyDigest:o.assemblyDigest,planDetail:o.planDetail,...p!==void 0?{destructionTicket:p}:{},artefacts:[],durationMs:Date.now()-h})}}const T=await Qe({loadPlan:W,options:i??{},target:n.appName,callbacks:t,verifiedAbsentByNecessity:ee,...e.destructionConsentGate!==void 0?{consentGate:e.destructionConsentGate}:{}});if(!T.success)return t.onError?.(T.error),d(T.error);if(T.data.kind==="withheld"){const r=te?Xe({assemblyDigest:Z,ttlMs:Ve}):void 0;return E({target:n.appName,deploymentType:"application",destructionPending:!0,destructionTicket:T.data.ticket,destructionOutcome:T.data.outcome,destructionHaltReasons:T.data.reasons,...r!==void 0?{destructionResumeToken:r.token,destructionResumeExpiresAt:r.expiresAt}:{},artefacts:[],durationMs:Date.now()-h})}if(T.data.kind==="proceed"){const{ticket:r,verbsByPhysicalName:o}=T.data,s=r.resources.filter(l=>o.get(l.physicalName)==="pin");if(s.length>0){t.onLog?.(`Pin remediation for ${n.appName}: probing live value(s) for ${s.length} consented pin(s) \u2014 deploy stopped for config write-back; nothing was applied`,"warn");const l=await nt(a.awsProvider,{targets:s.map(u=>({stack:u.stack,logicalId:u.logicalId,resourceType:u.resourceType,physicalName:u.physicalName,offendingProperties:u.offendingProperties})),...e.infrastructureSource!==void 0?{infrastructureContent:e.infrastructureSource.content,infrastructureFilePath:e.infrastructureSource.filePath}:{},...e.abortSignal!==void 0?{abortSignal:e.abortSignal}:{}});for(const u of at(l))t.onLog?.(u,"warn");return s.length<r.resources.length&&t.onLog?.("Remaining consented change(s) were not applied \u2014 the config write-back changes the plan, so they re-consent on the next deploy","warn"),E({target:n.appName,deploymentType:"application",pinPending:!0,pinReport:l,pinDeferredConsents:r.resources.length-s.length,destructionTicket:r,artefacts:[],durationMs:Date.now()-h})}const p=r.resources.filter(l=>l.finding==="pinned-name-replacement"&&o.get(l.physicalName)==="recreate");if(p.length>0){const l=new Map;for(const c of p){const m=l.get(c.stack)??[];m.push(c),l.set(c.stack,m)}const u=await ot(a.awsProvider,{stacks:[...l].map(([c,m])=>({stackName:c,targets:m.map(S=>({logicalId:S.logicalId,resourceType:S.resourceType,physicalId:S.physicalName}))})),...e.abortSignal!==void 0?{abortSignal:e.abortSignal}:{}});if(!u.success){const c=new Error(y(u.error.message));return t.onError?.(c),d(c)}const k=[];for(const[c,m]of l){const S=await _(e.assertLeaseForStack,c,e.abortSignal,"destroy");if(S!==void 0)return k.length>0&&t.onLog?.(`Surgery already completed on ${k.join("; ")} \u2014 those resources were removed and re-running the deploy resumes their journals and re-creates them.`,"warn"),t.onError?.(S),d(S);const R=m.map(g=>g.physicalName);t.onLog?.(`Recreate remediation for ${n.appName}: orchestrating the pinned-name replacement of ${R.join(", ")} on ${c}`,"warn");const L=await it(a.awsProvider,{stackName:c,targets:m.map(g=>({logicalId:g.logicalId,resourceType:g.resourceType,physicalId:g.physicalName})),appName:n.appName,consentedPhysicalNames:R,doneJournalPolicy:"archive-and-restart",plannedReplacementLogicalIds:m.map(g=>g.logicalId),onProgress:g=>t.onLog?.(g,"info"),...e.abortSignal!==void 0?{abortSignal:e.abortSignal}:{}});if(!L.success){const g=k.length>0?` Surgery already completed on ${k.join("; ")} \u2014 those resources were removed and re-running the deploy resumes their journals and re-creates them.`:"",j=new Error(y(`${L.error.message}${g}`));return t.onError?.(j),d(j)}k.push(`${c} (${R.join(", ")})`)}}}const re=await De(a,x,t);if(!re.success)return re;const ae=I(n.appName,P.DATABASE),z=N.has(ae),me=z?await _e(a.cfnService,n.appName):void 0,ke=z?await Ue(a.ecsResolver,a.ecsService,n.appName):void 0,O={},A=new Map;let ne;const D=async()=>{!z||!A.has(ae)||await He({appName:n.appName,connectionOutputsBefore:me,serviceTaskDefinitionsBefore:ke,services:{cfnService:a.cfnService,ecsService:a.ecsService,ecsResolver:a.ecsResolver},callbacks:t,...e.confirmDatabaseEndpointRestart!==void 0?{confirmRestart:e.confirmDatabaseEndpointRestart}:{},...e.abortSignal!==void 0?{abortSignal:e.abortSignal}:{}})};for(let r=0;r<w.length;r++){const o=w[r],s=I(n.appName,o),p=le(o,"deploy"),l=ce(o,"deploy");if(!N.has(s)){t.onStepStart?.(p,l,r,v),t.onLog?.(`Skipping ${o} \u2014 no changes detected`,"info"),t.onStepComplete?.(p,l,"skipped",r,v);continue}const u=await _(e.assertLeaseForStack,s,e.abortSignal);if(u!==void 0)return await D(),t.onError?.(u),d(u);const k=Fe(w,r,n.appName,N);if(k.length>=2){const g=await xe(k,n,a,x,t,r,v,f,O,A);if(!g.success)return await D(),d(await pe(a.awsProvider,{stackNames:k.map(j=>I(n.appName,j)),appName:n.appName,originalError:g.error,cfnService:a.cfnService,...e.abortSignal!==void 0?{abortSignal:e.abortSignal}:{}}));r+=k.length-1;continue}const c=await je(o,e,a,n,t,f.hasDockerfile);if(c!==null&&!c.success)return await D(),d(c.error);c!==null&&c.success&&(ne=c.data);const m=c!==null&&c.success?c.data.contentHashTagsByService:{},S=st(m);if(c!==null){const g=await _(e.assertLeaseForStack,s,e.abortSignal);if(g!==void 0)return await D(),t.onError?.(g),d(g)}const R=await Be(o,a,x,t,r,v,O,S!==void 0?{parameters:S}:void 0);if(!R.success)return await D(),d(await pe(a.awsProvider,{stackNames:[s],appName:n.appName,originalError:R.error,cfnService:a.cfnService,...e.abortSignal!==void 0?{abortSignal:e.abortSignal}:{}}));const L=f.currentHashes.get(s);L&&A.set(s,L)}if(await D(),A.size>0){const r=await a.hashService.updateStateAfterDeploy(n.path,A);if(!r.success){const o=y(r.error.message);M.debug(F,"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 oe=await a.stackService.resolveWebsiteUrl(n.appName);oe&&(O.websiteUrl=oe);const ye=await We(a,n.appName,ne,t);return await rt(a.awsProvider,{stackNames:[...N],appName:n.appName,onLog:(r,o)=>t.onLog?.(r,o),...e.abortSignal!==void 0?{abortSignal:e.abortSignal}:{}}).catch(r=>{M.debug(F,"Forget completion sweep failed",{error:y(se(r))})}),E({target:n.appName,deploymentType:"application",outputs:Object.keys(O).length>0?O:void 0,artefacts:ye,durationMs:Date.now()-h})}export{qt as deployApplication,ge as emitStackTargets};
|
|
1
|
+
import{join as z}from"path";import{CloudFormationClient as Se}from"@aws-sdk/client-cloudformation";import{EC2Client as be}from"@aws-sdk/client-ec2";import{success as D,failure as d}from"@fjall/generator";import{logger as M}from"@fjall/util/logger";import{ImageTagSchema as we,getErrorMessage as ce,imageTagParameterName as Te,maskSensitiveOutput as S}from"@fjall/util";import{parseDockerServicesFromManifest as Ee,parseLambdaDockerEntriesFromManifest as Ne}from"@fjall/util/manifest";import{stubCallerIdentity as Pe}from"../../types/deployment/index.js";import{getApplicationDeployOrder as De,getApplicationStackName as C,getApplicationStepName as le,getApplicationStepId as de,APPLICATION_STACKS as P}from"../../types/operations.js";import{CdkContextBuilder as ve}from"../../services/supporting/CdkContextBuilder.js";import{buildParamsContext as Re,bootstrapOrFail as Ce}from"../contextHelpers.js";import{runDetectionPipeline as Ie}from"./detectionPipeline.js";import{STEP_IDS as G,STEP_NAMES as Oe}from"../../types/stepDefinitions.js";import{StepRegistry as Ae}from"../../steps/stepRegistry.js";import{checkTargetReadiness as Le}from"../../aws/targetReadiness.js";import{cascadeHomeRegion as Me}from"../organisation/cascadeHelpers.js";import{withStepLifecycle as Fe}from"../stepLifecycle.js";import{DOCKER_BUILD_STEP_NAME as ue,runDockerBuild as $e}from"./dockerBuildHelper.js";import{getParallelPhase2Stacks as Be,getStackNamesToDeploy as xe,deployParallelPhase as je,deployStackSequential as Ge,runDockerPreCompute as Ue,runLambdaVersionPublish as _e,createBuildCallbacks as He}from"./applicationDeployHelpers.js";import{captureDatabaseConnectionOutputs as Ke,captureServiceTaskDefinitions as We,reconcileDatabaseEndpointChange as qe}from"./databaseEndpointReconcile.js";import{deployCodeOnly as Ve}from"./codeOnlyDeploy.js";import{checkDeployPathStackAvailability as ge}from"../activeDeploymentGuard.js";import{assertLeaseBeforeStack as U}from"./leaseGate.js";import{collectFullDeployArtefacts as ze}from"./artefactCollection.js";import{CfnRegistryService as Je}from"../../services/infrastructure/CfnRegistryService.js";import{ChangeSetProbe as Ye}from"../../services/infrastructure/changeSetProbe.js";import{runApprovalGate as Qe}from"./approvalGate.js";import{buildDestructionTicket as Xe,runDestructionGate as Ze}from"./destructionGate.js";import{DESTRUCTION_RESUME_TTL_MS as et,signApprovalToken as tt}from"./plan/approvalToken.js";import{computeAssemblyDigest as rt}from"./plan/assemblyDigest.js";import{createMemoisedPlanLoader as at}from"./plan/loadDeployPlan.js";import{runDriftPreFlightGate as nt}from"../drift/preFlightGate.js";import{completeForgetAfterDeploy as ot}from"../remediation/forgetResource.js";import{composePostFailureRepairOffer as pe}from"../remediation/postFailureRepairOffer.js";import{renderPinReportLines as it,runPinRemediation as st}from"../remediation/pinRemediation.js";import{runRecreatePreFlight as ct}from"../remediation/recreatePreFlight.js";import{runRecreateSurgery as lt}from"../remediation/recreateResource.js";const F="applicationDeploy";function dt(e){const n=Object.entries(e);if(n.length===0)return;const a={};for(const[t,i]of n){const b=Te(t);a[b]=i}return a}function ut(e){if(e?.mode!==void 0&&e.mode!=="rollback"){if(e.imageTag)return d(new Error(`--image-tag requires rollback mode (explicit mode '${e.mode}' was supplied alongside an image tag)`));if(e.serviceImageTags)return d(new Error(`serviceImageTags requires rollback mode (explicit mode '${e.mode}' was supplied alongside serviceImageTags)`))}if(e?.mode==="rollback"){if(e.imageTag&&e.serviceImageTags)return d(new Error("rollback mode accepts exactly one of --image-tag or serviceImageTags \u2014 they are mutually exclusive"));if(!e.imageTag&&!e.serviceImageTags)return d(new Error("rollback mode requires --image-tag <tag> or serviceImageTags naming the image(s) to roll to"))}if(e?.serviceImageTags!==void 0){if(e.serviceName)return d(new Error("serviceImageTags already names its services \u2014 --service cannot be combined with it"));const n=Object.entries(e.serviceImageTags);if(n.length===0)return d(new Error("serviceImageTags must name at least one service"));for(const[a,t]of n){const i=we.safeParse(t);if(!i.success)return d(new Error(`serviceImageTags.${a} is not a valid image tag: ${i.error.message}`))}}return D(void 0)}function fe(e,n,a){if(e.onStackTargets===void 0||a.length===0)return;const t=n.awsProvider.getAccountId(),i=n.awsProvider.getRegion();e.onStackTargets(a.map(b=>({stackName:b,...t!==""?{accountId:t}:{},...i!==""?{region:i}:{}})))}async function Jt(e,n,a){const{callbacks:t,options:i}=e,b=Date.now(),$=i?.mode??(i?.imageTag||i?.serviceImageTags?"rollback":i?.deployOnly?"code-only":"full");if($==="restart"){const r=new Error('mode "restart" is not a deploy \u2014 call the restart() entry point (fjall rollout) instead');return t.onError?.(r),d(r)}const J=ut(i);if(!J.success){const r=S(J.error.message),o=new Error(r);return t.onError?.(o),d(o)}const B=ve.buildDeploymentContext({deployType:"application",target:a.appName,path:a.path,region:n.awsProvider.getRegion(),callerIdentity:Pe(n.awsProvider.getAccountId()),...Re({orgConfig:e.orgConfig,identity:e.identity,skipOidc:e.options?.skipOidc})},{verbose:i?.verbose,infraOnly:i?.infraOnly},e.orgConfig),w=n.frameworkRegistry.resolve({appPath:a.path});let x;const I=$==="rollback";if(w&&!I){x=w.builder.plan({appPath:a.path},w.detection);const r=He(t),o=await w.builder.build(a.path,x,r,{skipBuild:i?.skipBuild,infraOnly:i?.infraOnly,...e.abortSignal!==void 0&&{abortSignal:e.abortSignal}});if(!o.success){const s=new Error(S(o.error.message));return t.onError?.(s),d(s)}}if($==="code-only"||I){t.onLog?.(I?i?.serviceImageTags?`Rollback mode \u2014 rolling ${Object.keys(i.serviceImageTags).length} service(s) to their release-recorded image tags`:`Rollback mode \u2014 rolling to image tag ${i?.imageTag}`:"Deploy-only mode \u2014 skipping infrastructure pipeline","info");const r=z(a.path,"cdk.out"),o=Ee(r),s=Ne(r),g=o.length>0||s.length>0,c=w?.detection.hasDockerfile===!0,u=g||c;M.debug(F,"Deploy-only branch entered",{mode:$,imageTag:i?.imageTag,serviceImageTags:i?.serviceImageTags,appName:a.appName,appPath:a.path,cdkOutPath:r,dockerProviderAvailable:e.dockerProvider!==void 0,builderName:w?.builder.name,hasDockerfileFromManifest:g,hasDockerfileFromDisk:c,hasDockerfile:u,manifestDockerServiceCount:o.length,manifestDockerPaths:o.map(h=>h.docker.path),manifestLambdaDockerEntryCount:s.length}),!g&&!c&&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");const m=await ge([C(a.appName,P.COMPUTE)],n.cfnService,e.abortSignal);if(!m.success)return t.onError?.(m.error),d(m.error);fe(t,n,[C(a.appName,P.COMPUTE)]);let l={},k;if(!I&&e.dockerProvider!==void 0&&u){M.debug(F,"Running Docker build before code-only deploy",{source:g?"manifest":"disk"});const h=await $e(e,n,a,t);if(!h.success)return d(h.error);l=h.data.contentHashTagsByService,k=h.data}else M.debug(F,"Skipping Docker build",{reason:I?"rollback":e.dockerProvider===void 0?"no dockerProvider":"no Dockerfile detected"});const y=await U(e.assertLeaseForStack,C(a.appName,P.COMPUTE),e.abortSignal);return y!==void 0?(t.onError?.(y),d(y)):Ve(e,n,a,l,k)}t.onLog?.("Analysing infrastructure\u2026","info");const _=await Ie(a,n,B,t);if(!_.success){const r=new Error(S(_.error.message));return t.onError?.(r),d(r)}const f=_.data;try{await t.onDetectionComplete?.({...f,builderName:w?.builder.name??"unknown"})}catch(r){const o=new Error(S(ce(r)));return t.onError?.(o),d(o)}const me={deploymentType:"application",operation:"deploy",deployOnly:!1,infraOnly:i?.infraOnly??!1,hasDockerfile:f.hasDockerfile,pattern:f.pattern,resources:f.resources,...w&&{builderName:w.builder.name}},Y=Ae.getSteps(me),Q=Y.findIndex(r=>r.id===G.TARGET_READINESS),X=await Fe(t,{stepId:G.TARGET_READINESS,stepName:Oe.TARGET_READINESS,...Q>=0&&{stepIndex:Q,totalSteps:Y.length}},async()=>{if(i?.skipReadinessCheck)return t.onLog?.("Skipping target readiness check (--skip-readiness-check)","warn"),{kind:"skipped",data:void 0};const r=n.awsProvider.getAccountId(),o=e.orgConfig?.providerAccounts.find(u=>u.id===r)?.name,s=n.awsProvider.getCredentials(),g=Me(e.orgConfig),c=await Le({cloudFormation:n.awsProvider.getClient(Se),ec2:new be({region:g,...s!==void 0&&{credentials:s}})},{id:r,...o!==void 0&&{name:o}},n.awsProvider.getRegion(),e.orgConfig,e.abortSignal);return c.success?c.data.ready?{kind:"completed",data:void 0}:{kind:"error",error:new Error(S(c.data.advisory))}:{kind:"error",error:new Error(S(c.error.message))}},e.abortSignal);if(!X.success)return X;const T=x?x.deployOrder:De({pattern:f.pattern,resources:f.resources}),v=T.length,Z=f.hasDockerfile&&e.dockerProvider!==void 0&&T.includes(P.COMPUTE)&&i?.infraOnly!==!0;if(!f.hasDifferences&&!i?.force&&!Z){t.onLog?.("No infrastructure changes detected","info"),t.onLog?.(i?.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 r=f.hasDockerfile&&e.dockerProvider!==void 0&&T.includes(P.COMPUTE);for(let s=0;s<T.length;s++){const g=T[s];r&&g===P.COMPUTE&&(t.onStepStart?.(G.DOCKER_OPERATIONS,ue),t.onStepComplete?.(G.DOCKER_OPERATIONS,ue,"skipped"));const c=de(g,"deploy"),u=le(g,"deploy");t.onStepStart?.(c,u,s,v),t.onStepComplete?.(c,u,"skipped",s,v)}const o=await n.stackService.resolveWebsiteUrl(a.appName);return D({target:a.appName,deploymentType:"application",outputs:o?{websiteUrl:o}:void 0,noChanges:!0,artefacts:[],durationMs:Date.now()-b})}const H=z(a.path,"cdk.out"),N=xe({deployOrder:T,stackChanges:f.stackChanges,appName:a.appName,force:i?.force===!0,computeBuildPossible:Z});fe(t,n,[...N]);const K=await ge([...N],n.cfnService,e.abortSignal);if(!K.success)return t.onError?.(K.error),d(K.error);const ee=rt(f.currentHashes),W=at({cfnService:n.cfnService,cdkOutPath:H,changedStacks:[...N],assemblyDigest:ee,registry:new Je(n.awsProvider),changeSetProbe:new Ye(n.awsProvider),...e.abortSignal!==void 0?{abortSignal:e.abortSignal}:{}}),q=await nt(n.awsProvider,{stackNames:[...N],cdkOutPath:H,cfnService:n.cfnService,callbacks:t,...e.abortSignal!==void 0?{abortSignal:e.abortSignal}:{}});if(q.kind==="blocked"){const r=new Error(q.message);return t.onError?.(r),d(r)}const te=q.verifiedAbsentByNecessity??[],re=e.approvalGate!==void 0||i?.approvalToken!==void 0&&i.approvalToken!==""||i?.planOnly===!0;if(re){const r=await Qe({approvalGate:e.approvalGate,options:i??{},target:a.appName,cdkOutPath:H,detection:{stackChanges:f.stackChanges,currentHashes:f.currentHashes},cfnService:n.cfnService,loadPlan:W,callbacks:t,...e.abortSignal!==void 0?{abortSignal:e.abortSignal}:{}});if(!r.success)return r;const o=r.data;if(o.kind==="rejected")return t.onLog?.(`Deploy rejected: ${S(o.reason)}`,"warn"),D({target:a.appName,deploymentType:"application",rejected:!0,artefacts:[],...o.planDetail!==void 0?{planDetail:o.planDetail}:{},durationMs:Date.now()-b});if(o.kind==="awaiting"){const s=await W(),g=s.success?Xe(s.data,te):void 0;return D({target:a.appName,deploymentType:"application",awaitingApproval:!0,approvalToken:o.approvalToken,assemblyDigest:o.assemblyDigest,planDetail:o.planDetail,...g!==void 0?{destructionTicket:g}:{},artefacts:[],durationMs:Date.now()-b})}}const E=await Ze({loadPlan:W,options:i??{},target:a.appName,callbacks:t,verifiedAbsentByNecessity:te,...e.destructionConsentGate!==void 0?{consentGate:e.destructionConsentGate}:{}});if(!E.success)return t.onError?.(E.error),d(E.error);if(E.data.kind==="withheld"){const r=re?tt({assemblyDigest:ee,ttlMs:et}):void 0;return D({target:a.appName,deploymentType:"application",destructionPending:!0,destructionTicket:E.data.ticket,destructionOutcome:E.data.outcome,destructionHaltReasons:E.data.reasons,...r!==void 0?{destructionResumeToken:r.token,destructionResumeExpiresAt:r.expiresAt}:{},artefacts:[],durationMs:Date.now()-b})}if(E.data.kind==="proceed"){const{ticket:r,verbsByPhysicalName:o}=E.data,s=r.resources.filter(c=>o.get(c.physicalName)==="pin");if(s.length>0){t.onLog?.(`Pin remediation for ${a.appName}: probing live value(s) for ${s.length} consented pin(s) \u2014 deploy stopped for config write-back; nothing was applied`,"warn");const c=await st(n.awsProvider,{targets:s.map(u=>({stack:u.stack,logicalId:u.logicalId,resourceType:u.resourceType,physicalName:u.physicalName,offendingProperties:u.offendingProperties})),...e.infrastructureSource!==void 0?{infrastructureContent:e.infrastructureSource.content,infrastructureFilePath:e.infrastructureSource.filePath}:{},...e.abortSignal!==void 0?{abortSignal:e.abortSignal}:{}});for(const u of it(c))t.onLog?.(u,"warn");return s.length<r.resources.length&&t.onLog?.("Remaining consented change(s) were not applied \u2014 the config write-back changes the plan, so they re-consent on the next deploy","warn"),D({target:a.appName,deploymentType:"application",pinPending:!0,pinReport:c,pinDeferredConsents:r.resources.length-s.length,destructionTicket:r,artefacts:[],durationMs:Date.now()-b})}const g=r.resources.filter(c=>c.finding==="pinned-name-replacement"&&o.get(c.physicalName)==="recreate");if(g.length>0){const c=new Map;for(const l of g){const k=c.get(l.stack)??[];k.push(l),c.set(l.stack,k)}const u=await ct(n.awsProvider,{stacks:[...c].map(([l,k])=>({stackName:l,targets:k.map(y=>({logicalId:y.logicalId,resourceType:y.resourceType,physicalId:y.physicalName}))})),...e.abortSignal!==void 0?{abortSignal:e.abortSignal}:{}});if(!u.success){const l=new Error(S(u.error.message));return t.onError?.(l),d(l)}const m=[];for(const[l,k]of c){const y=await U(e.assertLeaseForStack,l,e.abortSignal,"destroy");if(y!==void 0)return m.length>0&&t.onLog?.(`Surgery already completed on ${m.join("; ")} \u2014 those resources were removed and re-running the deploy resumes their journals and re-creates them.`,"warn"),t.onError?.(y),d(y);const h=k.map(p=>p.physicalName);t.onLog?.(`Recreate remediation for ${a.appName}: orchestrating the pinned-name replacement of ${h.join(", ")} on ${l}`,"warn");const L=await lt(n.awsProvider,{stackName:l,targets:k.map(p=>({logicalId:p.logicalId,resourceType:p.resourceType,physicalId:p.physicalName})),appName:a.appName,consentedPhysicalNames:h,doneJournalPolicy:"archive-and-restart",plannedReplacementLogicalIds:k.map(p=>p.logicalId),onProgress:p=>t.onLog?.(p,"info"),...e.abortSignal!==void 0?{abortSignal:e.abortSignal}:{}});if(!L.success){const p=m.length>0?` Surgery already completed on ${m.join("; ")} \u2014 those resources were removed and re-running the deploy resumes their journals and re-creates them.`:"",j=new Error(S(`${L.error.message}${p}`));return t.onError?.(j),d(j)}m.push(`${l} (${h.join(", ")})`)}}}const ae=await Ce(n,B,t);if(!ae.success)return ae;const ne=C(a.appName,P.DATABASE),V=N.has(ne),ke=V?await Ke(n.cfnService,a.appName):void 0,ye=V?await We(n.ecsResolver,n.ecsService,a.appName):void 0,O={},A=new Map;let oe,ie={};const R=async()=>{!V||!A.has(ne)||await qe({appName:a.appName,connectionOutputsBefore:ke,serviceTaskDefinitionsBefore:ye,services:{cfnService:n.cfnService,ecsService:n.ecsService,ecsResolver:n.ecsResolver},callbacks:t,...e.confirmDatabaseEndpointRestart!==void 0?{confirmRestart:e.confirmDatabaseEndpointRestart}:{},...e.abortSignal!==void 0?{abortSignal:e.abortSignal}:{}})};for(let r=0;r<T.length;r++){const o=T[r],s=C(a.appName,o),g=de(o,"deploy"),c=le(o,"deploy");if(!N.has(s)){t.onStepStart?.(g,c,r,v),t.onLog?.(`Skipping ${o} \u2014 no changes detected`,"info"),t.onStepComplete?.(g,c,"skipped",r,v);continue}const u=await U(e.assertLeaseForStack,s,e.abortSignal);if(u!==void 0)return await R(),t.onError?.(u),d(u);const m=Be(T,r,a.appName,N);if(m.length>=2){const p=await je(m,a,n,B,t,r,v,f,O,A);if(!p.success)return await R(),d(await pe(n.awsProvider,{stackNames:m.map(j=>C(a.appName,j)),appName:a.appName,originalError:p.error,cfnService:n.cfnService,...e.abortSignal!==void 0?{abortSignal:e.abortSignal}:{}}));r+=m.length-1;continue}const l=await Ue(o,e,n,a,t,f.hasDockerfile);if(l!==null&&!l.success)return await R(),d(l.error);l!==null&&l.success&&(oe=l.data);const k=l!==null&&l.success?l.data.contentHashTagsByService:{},y=dt(k);if(l!==null){const p=await U(e.assertLeaseForStack,s,e.abortSignal);if(p!==void 0)return await R(),t.onError?.(p),d(p)}const h=await Ge(o,n,B,t,r,v,O,y!==void 0?{parameters:y}:void 0);if(!h.success)return await R(),d(await pe(n.awsProvider,{stackNames:[s],appName:a.appName,originalError:h.error,cfnService:n.cfnService,...e.abortSignal!==void 0?{abortSignal:e.abortSignal}:{}}));o===P.COMPUTE&&(ie=await _e(a,n,t,k));const L=f.currentHashes.get(s);L&&A.set(s,L)}if(await R(),A.size>0){const r=await n.hashService.updateStateAfterDeploy(a.path,A);if(!r.success){const o=S(r.error.message);M.debug(F,"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 se=await n.stackService.resolveWebsiteUrl(a.appName);se&&(O.websiteUrl=se);const he=await ze(n,a.appName,oe,t,z(a.path,"cdk.out"),ie);return await ot(n.awsProvider,{stackNames:[...N],appName:a.appName,onLog:(r,o)=>t.onLog?.(r,o),...e.abortSignal!==void 0?{abortSignal:e.abortSignal}:{}}).catch(r=>{M.debug(F,"Forget completion sweep failed",{error:S(ce(r))})}),D({target:a.appName,deploymentType:"application",outputs:Object.keys(O).length>0?O:void 0,artefacts:he,durationMs:Date.now()-b})}export{Jt as deployApplication,fe as emitStackTargets};
|
|
@@ -7,6 +7,7 @@ import type { DeployServices } from "../serviceFactory.js";
|
|
|
7
7
|
import type { DetectionResult } from "./detectionPipeline.js";
|
|
8
8
|
import { type DockerBuildProducts } from "./dockerBuildHelper.js";
|
|
9
9
|
import type { BuildCallbacks } from "../../types/frameworkBuilder.js";
|
|
10
|
+
import type { PublishedLambdaVersion } from "../../services/infrastructure/LambdaService.js";
|
|
10
11
|
/**
|
|
11
12
|
* Single source of the deploy loop's stack-inclusion predicate: a stack
|
|
12
13
|
* deploys when its template hash changed, when `--force` overrides the hash
|
|
@@ -55,6 +56,23 @@ export type DockerPreComputeOutcome = DockerBuildProducts;
|
|
|
55
56
|
* required for this stack.
|
|
56
57
|
*/
|
|
57
58
|
export declare function runDockerPreCompute(stack: ApplicationStack, params: DeployParams, services: DeployServices, operation: ApplicationOperation, callbacks: DeployCallbacks, hasDockerfileOnDisk: boolean): Promise<Result<DockerPreComputeOutcome> | null>;
|
|
59
|
+
/**
|
|
60
|
+
* Publish a fresh Lambda version for every container-image Lambda whose
|
|
61
|
+
* shared `image` build key was actually rebuilt this cycle.
|
|
62
|
+
*
|
|
63
|
+
* CDK's `Function.currentVersion` can't be trusted here: its `Version`
|
|
64
|
+
* logical-id hash is computed from synth-time template shape, and the image
|
|
65
|
+
* reference is a `CfnParameter` Ref (a constant token across synths, same
|
|
66
|
+
* mechanism ECS task-definition image tags use) — so the hash doesn't change
|
|
67
|
+
* between deploys even though the parameter's resolved value (and therefore
|
|
68
|
+
* the deployed image) does. An explicit `PublishVersion` call after the
|
|
69
|
+
* Compute stack deploy succeeds sidesteps that entirely.
|
|
70
|
+
*
|
|
71
|
+
* Best-effort by contract, matching `artefactCollection.ts`'s post-deploy
|
|
72
|
+
* discovery: a publish failure degrades (warns, omits that function from the
|
|
73
|
+
* returned map) rather than failing a deploy that already succeeded.
|
|
74
|
+
*/
|
|
75
|
+
export declare function runLambdaVersionPublish(operation: ApplicationOperation, services: DeployServices, callbacks: DeployCallbacks, contentHashTagsByService: Record<string, string>): Promise<Record<string, PublishedLambdaVersion>>;
|
|
58
76
|
/**
|
|
59
77
|
* Bridge DeployCallbacks to BuildCallbacks.
|
|
60
78
|
* Fires both legacy OpenNext-specific callbacks and generic build callbacks
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{success as h,failure as
|
|
2
|
-
`);return
|
|
1
|
+
import{join as b}from"node:path";import{success as h,failure as P}from"@fjall/generator";import{maskSensitiveOutput as y}from"@fjall/util";import{parseLambdaDockerEntriesFromManifest as A}from"@fjall/util/manifest";import{isAborted as B}from"../../aws/organisations/types.js";import{APPLICATION_STACKS as E,getApplicationStackName as N,getApplicationStepName as O,getApplicationStepId as w,PARALLEL_DEPLOY_GROUPS as R}from"../../types/operations.js";import{emitProgress as $}from"../../services/supporting/helpers.js";import{forwardOutput as k,forwardResourceProgress as j}from"../contextHelpers.js";import{emptyBuildProducts as D,runDockerBuild as I}from"./dockerBuildHelper.js";import{runWelcomeImageSetup as T}from"./welcomeImageHelper.js";import{withStepLifecycle as _}from"../stepLifecycle.js";function z(e){const r=new Set;for(const n of e.deployOrder){const i=N(e.appName,n),o=e.stackChanges.get(i)??!0,a=n===E.COMPUTE&&e.computeBuildPossible;(o||e.force||a)&&r.add(i)}return r}function J(e,r,n,i){const o=new Set(R.PHASE_2.stacks),a=[];for(let s=r;s<e.length&&o.has(e[s]);s++)a.push(e[s]);if(a.length<2)return[];const u=a.filter(s=>i.has(N(n,s)));return u.length>=2?u:[]}async function Q(e,r,n,i,o,a,u,s,p,f){for(let t=0;t<e.length;t++){const d=e[t],c=w(d,"deploy"),m=O(d,"deploy");o.onStepStart?.(c,m,a+t,u)}$(o,"Deploying infrastructure in parallel\u2026"),o.onParallelPhaseStart?.(e,"Storage and database resources (parallel)");const l=await n.stackService.deployStacksInParallel(e,i,{onOutput:k(o),onResourceProgress:(t,d)=>{o.onResourceProgress?.(t),d&&o.onParallelStackResourceProgress?.(d,t)},onStackComplete:(t,d,c,m)=>{const S=w(t,"deploy"),L=O(t,"deploy"),x=a+e.indexOf(t),C=B(n.abortSignal);o.onStepComplete?.(S,L,d?"completed":C?"cancelled":"error",x,u),!d&&m&&!C&&o.onError?.(new Error(y(m.message)))}});if(!l.success)return o.onParallelPhaseComplete?.([]),P(l.error);const g=l.data.filter(t=>!t.success);if(o.onParallelPhaseComplete?.(l.data.map(t=>({stack:t.stack,success:t.success,error:t.error}))),g.length>0){const t=g.map(c=>c.stack).join(", "),d=g.map(c=>`${c.stack}: ${y(c.error?.message??"Unknown error")}`).join(`
|
|
2
|
+
`);return P(new Error(`Failed to deploy stacks: ${t}
|
|
3
3
|
|
|
4
|
-
${
|
|
4
|
+
${d}`))}for(const t of l.data){if(t.success&&t.outputs)for(const[m,S]of Object.entries(t.outputs))p[m]=String(S);const d=N(r.appName,t.stack),c=s.currentHashes.get(d);c&&f.set(d,c)}return h(void 0)}async function X(e,r,n,i,o,a,u,s){const p=w(e,"deploy"),f=O(e,"deploy");return _(i,{stepId:p,stepName:f,stepIndex:o,totalSteps:a},async()=>{const l=await r.stackService.deployStack(e,n,{onOutput:k(i),onResourceProgress:j(i)},s?.parameters!==void 0?{parameters:s.parameters}:void 0);if(!l.success)return{kind:"error",error:l.error};if(l.data.outputs)for(const[g,t]of Object.entries(l.data.outputs))u[g]=String(t);return{kind:"completed",data:void 0}},r.abortSignal)}async function Z(e,r,n,i,o,a){if(e!==E.COMPUTE||!r.dockerProvider)return null;if(a){const s=await I(r,n,i,o);return s.success?h(s.data):P(s.error)}const u=await T(r,n,i,o);return u.success?h(D()):P(u.error)}async function ee(e,r,n,i){if(Object.keys(i).length===0)return{};const o=b(e.path,"cdk.out"),u=A(o).filter(p=>i[p.imageKey]!==void 0);if(u.length===0)return{};const s={};for(const p of u){const f=await r.lambdaService.publishVersion(p.name);if(!f.success){n.onLog?.(`Published image for Lambda '${p.name}', but publishing a new version failed: ${y(f.error.message)}. The function's $LATEST code is up to date; only the published-version record is affected.`,"warn");continue}s[p.name]=f.data}return s}function te(e){return{onBuildStart:r=>{e.onOpenNextBuildStart?.(),e.onLog?.(`${r} build started`,"info")},onBuildProgress:(r,n)=>{e.onOpenNextProgress?.(n)},onBuildComplete:r=>{e.onOpenNextBuildComplete?.(),e.onLog?.(`${r} build complete`,"info")},onBuildError:(r,n)=>{e.onOpenNextBuildError?.(n)}}}export{te as createBuildCallbacks,Q as deployParallelPhase,X as deployStackSequential,J as getParallelPhase2Stacks,z as getStackNamesToDeploy,Z as runDockerPreCompute,ee as runLambdaVersionPublish};
|
|
@@ -2,6 +2,7 @@ import { type ServiceArtefact } from "@fjall/util";
|
|
|
2
2
|
import type { DeployCallbacks } from "../../types/callbacks.js";
|
|
3
3
|
import type { DeployServices } from "../serviceFactory.js";
|
|
4
4
|
import type { DockerBuildProducts } from "./dockerBuildHelper.js";
|
|
5
|
+
import type { PublishedLambdaVersion } from "../../services/infrastructure/LambdaService.js";
|
|
5
6
|
/**
|
|
6
7
|
* Collect `ServiceArtefact[]` for a successful FULL deploy from the build
|
|
7
8
|
* step's products plus a best-effort post-deploy ECS resolution (the same
|
|
@@ -14,5 +15,11 @@ import type { DockerBuildProducts } from "./dockerBuildHelper.js";
|
|
|
14
15
|
* service name; `taskDefinitionArn` stays absent) and warns — it NEVER fails
|
|
15
16
|
* a deploy that already succeeded. Returns `[]` when the deploy pushed no
|
|
16
17
|
* images (welcome-image seed, legacy fallback, infra-only).
|
|
18
|
+
*
|
|
19
|
+
* `contentHashTagsByService` keys are a mix of ECS service names and Lambda
|
|
20
|
+
* `image` build keys (several Lambda functions may share one key). Keys that
|
|
21
|
+
* match a container-Lambda manifest entry are expanded into one artefact PER
|
|
22
|
+
* ACTUAL FUNCTION NAME (there is no ECS task-definition/live-service concept
|
|
23
|
+
* for them); every other key is treated as an ECS service, unchanged.
|
|
17
24
|
*/
|
|
18
|
-
export declare function collectFullDeployArtefacts(services: DeployServices, appName: string, build: DockerBuildProducts | undefined, callbacks: DeployCallbacks): Promise<ServiceArtefact[]>;
|
|
25
|
+
export declare function collectFullDeployArtefacts(services: DeployServices, appName: string, build: DockerBuildProducts | undefined, callbacks: DeployCallbacks, cdkOutPath: string, publishedLambdaVersions?: Record<string, PublishedLambdaVersion>): Promise<ServiceArtefact[]>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{maskSensitiveOutput as
|
|
1
|
+
import{maskSensitiveOutput as N}from"@fjall/util";import{parseLambdaDockerEntriesFromManifest as b}from"@fjall/util/manifest";import{logger as h}from"@fjall/util/logger";import{extractEcsServiceName as D}from"../../aws/utils/arnParser.js";import{deriveEcrRepositoryArn as k}from"./codeOnlyDeploy.js";const w="artefactCollection";async function O(s,t,n,r,o,c={}){if(n===void 0||n.repoUri===void 0)return[];const i=Object.entries(n.contentHashTagsByService);if(i.length===0)return[];const d=n.repoUri,e=k(d),u=await L(s,t,r),g=new Map;for(const a of b(o)){const f=g.get(a.imageKey)??[];f.push(a.name),g.set(a.imageKey,f)}const v=[];for(const[a,f]of i){const m=n.imageDigestsByService[a],A=g.get(a);if(A!==void 0){for(const S of A){const p=c[S];v.push({serviceName:S,imageTag:f,imageUri:`${d}:${f}`,...m!==void 0?{imageDigest:m}:{},...e!==""?{ecrRepositoryArn:e}:{},...p!==void 0?{functionArn:p.functionArn,publishedVersion:p.version}:{}})}continue}const l=C(a,u);v.push({serviceName:l?.ecsServiceName??a,imageTag:f,imageUri:`${d}:${f}`,...m!==void 0?{imageDigest:m}:{},...e!==""?{ecrRepositoryArn:e}:{},...l?.taskDefinitionArn!==void 0?{taskDefinitionArn:l.taskDefinitionArn}:{}})}return v}async function L(s,t,n){try{const r=await s.ecsResolver.getDeployableClusterAndServices(t);if(!r.success)return y(n,t,r.error.message),[];const{clusterArn:o,serviceArns:c}=r.data;if(!o||c.length===0)return h.debug(w,"No deployable ECS services discovered",{appName:t}),[];const i=await s.ecsService.describeServices(o,c);if(!i.success)return y(n,t,i.error.message),c.map(e=>D(e)).filter(e=>e!==void 0&&e!=="").map(e=>({ecsServiceName:e}));const d=[];for(const e of i.data){const u=e.serviceName??(e.serviceArn!==void 0?D(e.serviceArn):void 0);u===void 0||u===""||d.push({ecsServiceName:u,...e.taskDefinition!==void 0?{taskDefinitionArn:e.taskDefinition}:{}})}return d}catch(r){return y(n,t,r instanceof Error?r.message:String(r)),[]}}function C(s,t){const n=s.toLowerCase(),r=t.find(i=>i.ecsServiceName.toLowerCase()===n);if(r)return r;const o=t.filter(i=>i.ecsServiceName.toLowerCase().includes(n));if(o.length===1)return o[0];const c=o.filter(i=>i.ecsServiceName.toLowerCase().endsWith(`-${n}`));if(c.length===1)return c[0]}function y(s,t,n){const r=N(n);h.debug(w,"Post-deploy artefact resolution degraded",{appName:t,error:r}),s.onLog?.(`Deploy succeeded, but resolving live task definitions for release artefacts failed: ${r}. Artefacts are recorded without task definition ARNs.`,"warn")}export{O as collectFullDeployArtefacts};
|
|
@@ -3,6 +3,7 @@ import { type ManifestDockerService } from "@fjall/util/manifest";
|
|
|
3
3
|
import type { DeployParams, DeployResult } from "../../types/params.js";
|
|
4
4
|
import type { ApplicationOperation } from "../../types/operations.js";
|
|
5
5
|
import type { DeployServices } from "../serviceFactory.js";
|
|
6
|
+
import { type DockerBuildProducts } from "./dockerBuildHelper.js";
|
|
6
7
|
/**
|
|
7
8
|
* Resolve the ECR image tag the rollout should pin against for a given
|
|
8
9
|
* manifest service. Keys the content-hash tag map by the bare `<service>`
|
|
@@ -39,6 +40,28 @@ export declare function lookupContentHashTag(manifestService: ManifestDockerServ
|
|
|
39
40
|
* caller surfaces a warning on multi-service stacks.
|
|
40
41
|
*/
|
|
41
42
|
export declare function rebaseRollbackTagForService(suppliedTag: string, serviceName: string, allServiceNames: readonly string[]): string;
|
|
43
|
+
/**
|
|
44
|
+
* Converge the Compute stack's `<Service>ImageTag` CfnParameters to the tags
|
|
45
|
+
* actually rolled (F4b). A rollback mutates ECS (or a Lambda function)
|
|
46
|
+
* directly, bypassing CloudFormation; without this, the stack's declared
|
|
47
|
+
* parameters stay at their pre-rollback values and the next full deploy
|
|
48
|
+
* re-applies them, silently reverting the rollback. Runs for BOTH rollback
|
|
49
|
+
* surfaces (the `serviceImageTags` map and the legacy single `--image-tag`);
|
|
50
|
+
* never for a plain code-only deploy (no rollback tag exists there). Exported
|
|
51
|
+
* so `lambdaCodeOnlyRollout.ts`'s rollback paths can feed their rolled
|
|
52
|
+
* `imageKey`s into the SAME batched reconcile call as the ECS entries —
|
|
53
|
+
* `rolledServices` is keyed generically (`serviceName` doubles as `imageKey`
|
|
54
|
+
* for a Lambda entry, since that's the CfnParameter key
|
|
55
|
+
* `imageTagParameterName` derives from).
|
|
56
|
+
*
|
|
57
|
+
* Best-effort: the services are already rolled by the time this runs, so a
|
|
58
|
+
* reconcile failure must not fail the deploy — it surfaces a prominent
|
|
59
|
+
* warning instead.
|
|
60
|
+
*/
|
|
61
|
+
export declare function reconcileImageTagParameters(services: DeployServices, appName: string, rolledServices: ReadonlyArray<{
|
|
62
|
+
serviceName: string;
|
|
63
|
+
imageTag: string;
|
|
64
|
+
}>, callbacks: DeployParams["callbacks"], abortSignal?: AbortSignal): Promise<void>;
|
|
42
65
|
/**
|
|
43
66
|
* Code-only deployment orchestrator.
|
|
44
67
|
*
|
|
@@ -60,8 +83,16 @@ export declare function rebaseRollbackTagForService(suppliedTag: string, service
|
|
|
60
83
|
* Release. Unlike `imageTag`, map-supplied tags are never re-based — they
|
|
61
84
|
* already name the exact service — and never fall back to tag pinning; see
|
|
62
85
|
* `serviceImageTagsRollback.ts`.
|
|
86
|
+
*
|
|
87
|
+
* `dockerBuildProducts` is the FULL build-phase output (only present on the
|
|
88
|
+
* plain-code-only path, when a build actually ran this cycle — `undefined`
|
|
89
|
+
* for both rollback modes, which roll an existing tag and never rebuild). It
|
|
90
|
+
* carries `repoUri`/`imageDigestsByService` so `lambdaCodeOnlyRollout.ts`'s
|
|
91
|
+
* primary path can build a digest-pinned `ImageUri` without an extra ECR
|
|
92
|
+
* call — `contentHashTagsByService` alone (the historical 4th param) isn't
|
|
93
|
+
* enough for that.
|
|
63
94
|
*/
|
|
64
|
-
export declare function deployCodeOnly(params: DeployParams, services: DeployServices, operation: ApplicationOperation, contentHashTagsByService: Record<string, string
|
|
95
|
+
export declare function deployCodeOnly(params: DeployParams, services: DeployServices, operation: ApplicationOperation, contentHashTagsByService: Record<string, string>, dockerBuildProducts?: DockerBuildProducts): Promise<Result<DeployResult>>;
|
|
65
96
|
/**
|
|
66
97
|
* Derive an ECR repository ARN from an image URI of shape
|
|
67
98
|
* `<account>.dkr.ecr.<region>.amazonaws.com/<repo>`. Returns "" when the URI
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{join as W}from"path";import{success as H,failure as O}from"@fjall/generator";import{logger as A}from"@fjall/util/logger";import{artefactOutputKey as I,imageTagParameterName as X,maskSensitiveOutput as C}from"@fjall/util";import{parseDockerServicesFromManifest as Y}from"@fjall/util/manifest";import{APPLICATION_STACKS as x,getApplicationStackName as q,getApplicationStepId as J,getApplicationStepName as Q}from"../../types/operations.js";import{extractEcsServiceName as L}from"../../aws/utils/arnParser.js";import{isAborted as K}from"../../aws/organisations/types.js";import{stripTagOrDigest as V}from"../../services/infrastructure/EcrImageInspectorService.js";import{withStepLifecycle as Z}from"../stepLifecycle.js";import{registerAndRollTaskDefinition as B}from"./taskDefinitionRoll.js";import{matchesServiceName as ee,matchServiceImageTagsToLiveServices as te,preflightResolveServiceImageTagDigests as re,resolveServiceTaskDefinition as ie}from"./serviceImageTagsRollback.js";const N="codeOnlyDeploy";function ne(t,s){if(t)return s[t.name]}function ae(t,s){const e=s.toLowerCase();return t.find(i=>i.name.toLowerCase()===e)??t.find(i=>e.includes(i.name.toLowerCase()))}function oe(t,s,e){const i=s.toLowerCase(),n=t.toLowerCase(),d=e.map(c=>c.toLowerCase()).filter(c=>c!==""&&n.startsWith(`${c}-`)).sort((c,m)=>m.length-c.length)[0];if(d===void 0||d===i)return t;const f=t.slice(d.length);return`${i}${f}`}const se=0,ce=1;function j(t,s,e,i,n){i[I(t,"TaskDefinition")]=e.taskDefinitionArn,i[I(t,"PreviousTaskDefinition")]=e.previousTaskDefinitionArn,i[I(t,"ImageTag")]=s,i[I(t,"ImageUri")]=e.imageUri,i[I(t,"EcrRepositoryArn")]=e.ecrRepositoryArn,e.imageDigest!==void 0&&(i[I(t,"ImageDigest")]=e.imageDigest),n.push({serviceName:t,imageTag:s,imageUri:e.imageUri,taskDefinitionArn:e.taskDefinitionArn,previousTaskDefinitionArn:e.previousTaskDefinitionArn,...e.imageDigest!==void 0?{imageDigest:e.imageDigest}:{},...e.ecrRepositoryArn!==""?{ecrRepositoryArn:e.ecrRepositoryArn}:{}})}async function F(t,s,e,i,n){if(e.length===0)return;const d=q(s,x.COMPUTE),f={};for(const{serviceName:m,imageTag:l}of e)f[X(m)]=l;const c=await t.cfnService.updateStackParameters(d,f,n);if(!c.success){i.onLog?.(`Rollback succeeded but the CloudFormation ImageTag parameter reconcile failed \u2014 the next full deploy may revert this rollback. Reason: ${C(c.error.message)}`,"warn");return}c.data.skippedKeys.length>0&&(i.onLog?.(`Rollback succeeded but ${c.data.skippedKeys.length} ImageTag parameter(s) were not found on stack ${d} (${c.data.skippedKeys.join(", ")}) \u2014 the next full deploy may revert those services`,"warn"),A.debug(N,"ImageTag reconcile skipped stale parameters",{stackName:d,skippedKeys:c.data.skippedKeys}))}async function Se(t,s,e,i){const{callbacks:n,options:d}=t,f=Date.now(),c=J(x.COMPUTE,"deploy"),m=Q(x.COMPUTE,"deploy");return Z(n,{stepId:c,stepName:m,stepIndex:se,totalSteps:ce},async()=>{const l=d?.imageTag,$=d?.serviceImageTags;l?n.onLog?.(`Rolling to supplied image tag ${l}`,"info"):$&&n.onLog?.(`Rolling ${Object.keys($).length} service(s) to their release-recorded image tags`,"info");const k=Y(W(e.path,"cdk.out"));A.debug(N,"Code-only rollout starting",{explicitRollbackTag:l,serviceImageTags:$,workingDirectory:t.workingDirectory,contentHashTagsByService:i,manifestServiceCount:k.length,manifestServices:k.map(o=>({name:o.name,target:o.docker.target}))}),n.onLog?.("Discovering ECS cluster and services\u2026","info");const u=await s.ecsResolver.getDeployableClusterAndServices(e.appName);if(!u.success)return A.debug(N,"ECS discovery failed",{appName:e.appName,error:C(u.error.message)}),{kind:"error",error:u.error};const{clusterArn:D,serviceArns:r}=u.data;if(A.debug(N,"ECS discovery complete",{appName:e.appName,clusterArn:D,serviceCount:r.length,serviceArns:r}),!D||r.length===0){const o=`No deployable ECS services found for ${e.appName}`;return n.onLog?.(o,"warn"),{kind:"skipped",data:{target:e.appName,deploymentType:"application",durationMs:Date.now()-f,noChanges:!0,artefacts:[]}}}if($!==void 0)return ge(s,e,D,r,$,n,t.abortSignal,f);const a=d?.serviceName?.toLowerCase(),y=a===""?void 0:a,p=y?r.filter(o=>ee(L(o)??"",y)):r;if(y&&p.length===0){const o=r.map(w=>L(w)??"unknown").join(", ");return{kind:"error",error:new Error(`Service '${y}' not found. Available: ${o||"none"}`)}}const v={},E=[],g=[],b=r.map(o=>L(o)).filter(o=>o!==void 0&&o!=="");for(let o=0;o<p.length;o++){if(K(t.abortSignal))return{kind:"error",error:new Error(`Rollout cancelled after ${E.length}/${p.length} services`)};const w=p[o];if(!w)continue;const h=L(w)??`service-${o+1}`,G=`[${o+1}/${p.length}] ${h}`,R=ae(k,h),M=ne(R,i);let T,P;if(l!==void 0)T=oe(l,h,b),T!==l?(P=l,n.onLog?.(`Re-based image tag ${l} \u2192 ${T} for ${h}`,"info")):b.length>1&&!l.toLowerCase().startsWith(`${h.toLowerCase()}-`)&&n.onLog?.(`Image tag ${l} has no recognised service prefix; applying verbatim to ${h} \u2014 confirm it points at this service's image`,"warn");else if(M!==void 0)T=M;else{const U=`No image available to deploy for ${h}: 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.`;n.onLog?.(U,"warn"),g.push(`${h}: ${U}`);continue}A.debug(N,"Starting service rollout",{serviceName:h,serviceArn:w,imageTag:T,explicitRollbackTag:l,contentHashTag:M,manifestServiceMatched:R?.name,dockerTarget:R?.docker.target,index:o,total:p.length}),n.onECSUpdate?.(`${G}: rolling out image tag ${T}`,h);const S=await _(s,D,w,h,T,n,P,void 0,t.abortSignal);if(A.debug(N,"Service rollout completed",{serviceName:h,success:S.success,...S.success?{taskDefinitionArn:S.data.taskDefinitionArn,imageDigest:S.data.imageDigest,durationMs:S.data.durationMs}:{error:C(S.error.message)}}),!S.success){g.push(`${h}: ${C(S.error.message)}`);continue}j(h,T,S.data,v,E)}const z=Date.now()-f;if(g.length>0){const o=p.length-g.length,w=p.length,h=g.length===w?`All ${w} ECS service rollouts failed: ${g.join("; ")}`:`Partial rollout: ${o}/${w} services succeeded. Failures: ${g.join("; ")}`;return{kind:"error",error:new Error(h)}}return l!==void 0&&await F(s,e.appName,E.map(o=>({serviceName:o.serviceName,imageTag:o.imageTag})),n,t.abortSignal),{kind:"completed",data:{target:e.appName,deploymentType:"application",outputs:Object.keys(v).length>0?v:void 0,artefacts:E,durationMs:z}}},t.abortSignal)}async function ge(t,s,e,i,n,d,f,c){const m=te(i,n);for(const r of m.unmatchedLiveServiceNames)d.onLog?.(`Service ${r} is not part of this release \u2014 leaving it untouched`,"warn");for(const r of m.staleMapKeys)d.onLog?.(`Service ${r} from the release no longer exists in the stack`,"warn");if(m.matched.length===0)return{kind:"error",error:new Error("None of the services in serviceImageTags matched a live ECS service in this stack \u2014 nothing to roll")};const l=await re(t,e,m.matched,f);if(!l.success)return{kind:"error",error:l.error};const $={},k=[],u=[];for(let r=0;r<m.matched.length;r++){if(K(f))return{kind:"error",error:new Error(`Rollout cancelled after ${k.length}/${m.matched.length} services`)};const a=m.matched[r];if(!a)continue;const y=`[${r+1}/${m.matched.length}] ${a.serviceName}`,p=l.data.get(a.serviceArn);if(p===void 0){u.push(`${a.serviceName}: pre-flight digest missing after a successful pre-flight`);continue}A.debug(N,"Starting service rollout (serviceImageTags)",{serviceName:a.serviceName,serviceArn:a.serviceArn,imageTag:a.tag,mapKey:a.mapKey,index:r,total:m.matched.length}),d.onECSUpdate?.(`${y}: rolling out image tag ${a.tag}`,a.serviceName);const v=await _(t,e,a.serviceArn,a.serviceName,a.tag,d,void 0,p,f);if(A.debug(N,"Service rollout completed (serviceImageTags)",{serviceName:a.serviceName,success:v.success,...v.success?{taskDefinitionArn:v.data.taskDefinitionArn,imageDigest:v.data.imageDigest,durationMs:v.data.durationMs}:{error:C(v.error.message)}}),!v.success){u.push(`${a.serviceName}: ${C(v.error.message)}`);continue}j(a.serviceName,a.tag,v.data,$,k)}const D=Date.now()-c;if(u.length>0){const r=m.matched.length-u.length,a=m.matched.length,y=u.length===a?`All ${a} ECS service rollouts failed: ${u.join("; ")}`:`Partial rollout: ${r}/${a} services succeeded. Failures: ${u.join("; ")}`;return{kind:"error",error:new Error(y)}}return await F(t,s.appName,k.map(r=>({serviceName:r.serviceName,imageTag:r.imageTag})),d,f),{kind:"completed",data:{target:s.appName,deploymentType:"application",outputs:Object.keys($).length>0?$:void 0,artefacts:k,durationMs:D}}}async function _(t,s,e,i,n,d,f,c,m){const l=Date.now();let $,k,u,D,r;if(c!==void 0)$=c.taskDefinition.previousTaskDefArn,k=c.taskDefinition.taskDefinition,u=c.taskDefinition.containerDefinitions,D=c.taskDefinition.repoUri,r=c.imageDigest;else{const g=await ie(t,s,e,i);if(!g.success)return O(g.error);$=g.data.previousTaskDefArn,k=g.data.taskDefinition,u=g.data.containerDefinitions,D=g.data.repoUri;const b=await t.ecrImageInspector.getImageDigest(D,n,m);if(b.success)r=b.data;else{if(f!==void 0)return O(new Error(`Re-based image tag '${n}' (from supplied '${f}') was not found in ECR for ${i}: ${C(b.error.message)}. The supplied tag's build has no image for ${i} \u2014 supply that service's own tag, or pass --service to roll a single service.`));d.onLog?.(`Could not resolve image digest for ${i} (${C(b.error.message)}); falling back to tag pinning`,"warn")}}const a=g=>r!==void 0?`${g}@${r}`:`${g}:${n}`,y=u.map(g=>g.image?{...g,image:a(V(g.image))}:g),p=await B(t,{clusterArn:s,serviceArn:e,serviceName:i,taskDefinition:k,containerDefinitions:y,previousTaskDefArn:$,...r!==void 0?{imageDigest:r}:{},startedAtMs:l},d);if(!p.success)return O(p.error);const v=`${D}:${n}`,E=de(D);return H({taskDefinitionArn:p.data.newTaskDefArn,previousTaskDefinitionArn:$,imageUri:v,ecrRepositoryArn:E,imageDigest:r,durationMs:p.data.durationMs})}function de(t){const s=t.match(/^(\d{12})\.dkr\.ecr\.([a-z0-9-]+)\.amazonaws\.com\/(.+)$/);if(!s)return"";const[,e,i,n]=s;return`arn:aws:ecr:${i}:${e}:repository/${n}`}export{Se as deployCodeOnly,de as deriveEcrRepositoryArn,ne as lookupContentHashTag,oe as rebaseRollbackTagForService};
|
|
1
|
+
import{join as q}from"path";import{success as te,failure as z}from"@fjall/generator";import{logger as A}from"@fjall/util/logger";import{artefactOutputKey as M,imageTagParameterName as re,maskSensitiveOutput as N}from"@fjall/util";import{parseDockerServicesFromManifest as ne,parseLambdaDockerEntriesFromManifest as ae}from"@fjall/util/manifest";import{APPLICATION_STACKS as G,getApplicationStackName as ie,getApplicationStepId as oe,getApplicationStepName as se}from"../../types/operations.js";import{extractEcsServiceName as P}from"../../aws/utils/arnParser.js";import{isAborted as H}from"../../aws/organisations/types.js";import{stripTagOrDigest as ce}from"../../services/infrastructure/EcrImageInspectorService.js";import{withStepLifecycle as ge}from"../stepLifecycle.js";import{registerAndRollTaskDefinition as de}from"./taskDefinitionRoll.js";import{ecrRepoUri as le}from"./dockerBuildHelper.js";import{matchesServiceName as J,matchServiceImageTagsToLiveServices as me,preflightResolveServiceImageTagDigests as fe,resolveServiceTaskDefinition as ue}from"./serviceImageTagsRollback.js";import{rolloutLambdaImageTag as pe,rolloutLambdaPrimary as he,rolloutLambdaServiceImageTags as ve}from"./lambdaCodeOnlyRollout.js";const C="codeOnlyDeploy";function ke(t,r){if(t)return r[t.name]}function $e(t,r){const e=r.toLowerCase();return t.find(a=>a.name.toLowerCase()===e)??t.find(a=>e.includes(a.name.toLowerCase()))}function ye(t,r,e){const a=r.toLowerCase(),l=t.toLowerCase(),n=e.map(o=>o.toLowerCase()).filter(o=>o!==""&&l.startsWith(`${o}-`)).sort((o,f)=>f.length-o.length)[0];if(n===void 0||n===a)return t;const m=t.slice(n.length);return`${a}${m}`}const we=0,De=1;function Q(t,r,e,a,l){a[M(t,"TaskDefinition")]=e.taskDefinitionArn,a[M(t,"PreviousTaskDefinition")]=e.previousTaskDefinitionArn,a[M(t,"ImageTag")]=r,a[M(t,"ImageUri")]=e.imageUri,a[M(t,"EcrRepositoryArn")]=e.ecrRepositoryArn,e.imageDigest!==void 0&&(a[M(t,"ImageDigest")]=e.imageDigest),l.push({serviceName:t,imageTag:r,imageUri:e.imageUri,taskDefinitionArn:e.taskDefinitionArn,previousTaskDefinitionArn:e.previousTaskDefinitionArn,...e.imageDigest!==void 0?{imageDigest:e.imageDigest}:{},...e.ecrRepositoryArn!==""?{ecrRepositoryArn:e.ecrRepositoryArn}:{}})}async function x(t,r,e,a,l){if(e.length===0)return;const n=ie(r,G.COMPUTE),m={};for(const{serviceName:f,imageTag:w}of e)m[re(f)]=w;const o=await t.cfnService.updateStackParameters(n,m,l);if(!o.success){a.onLog?.(`Rollback succeeded but the CloudFormation ImageTag parameter reconcile failed \u2014 the next full deploy may revert this rollback. Reason: ${N(o.error.message)}`,"warn");return}o.data.skippedKeys.length>0&&(a.onLog?.(`Rollback succeeded but ${o.data.skippedKeys.length} ImageTag parameter(s) were not found on stack ${n} (${o.data.skippedKeys.join(", ")}) \u2014 the next full deploy may revert those services`,"warn"),A.debug(C,"ImageTag reconcile skipped stale parameters",{stackName:n,skippedKeys:o.data.skippedKeys}))}function V(t,r,e,a,l){if(e)return{kind:"error",error:e};if(t.kind==="error")return r.failures.length>0?{kind:"error",error:new Error(`${t.error.message}; Lambda rollout failures: ${r.failures.join("; ")}`)}:t;if(r.failures.length>0)return{kind:"error",error:new Error(`Lambda rollout failures: ${r.failures.join("; ")}`)};const n=t.data,m={...n.outputs??{},...r.outputs};return{kind:"completed",data:{target:a.appName,deploymentType:"application",outputs:Object.keys(m).length>0?m:void 0,artefacts:[...n.artefacts,...r.artefacts],durationMs:Date.now()-l}}}async function je(t,r,e,a,l){const{callbacks:n,options:m}=t,o=Date.now(),f=oe(G.COMPUTE,"deploy"),w=se(G.COMPUTE,"deploy");return ge(n,{stepId:f,stepName:w,stepIndex:we,totalSteps:De},async()=>{const g=m?.imageTag,h=m?.serviceImageTags;g?n.onLog?.(`Rolling to supplied image tag ${g}`,"info"):h&&n.onLog?.(`Rolling ${Object.keys(h).length} service(s) to their release-recorded image tags`,"info");const v=ne(q(e.path,"cdk.out"));A.debug(C,"Code-only rollout starting",{explicitRollbackTag:g,serviceImageTags:h,workingDirectory:t.workingDirectory,contentHashTagsByService:a,manifestServiceCount:v.length,manifestServices:v.map(s=>({name:s.name,target:s.docker.target}))}),n.onLog?.("Discovering ECS cluster and services\u2026","info");const $=await r.ecsResolver.getDeployableClusterAndServices(e.appName);if(!$.success)return A.debug(C,"ECS discovery failed",{appName:e.appName,error:N($.error.message)}),{kind:"error",error:$.error};const{clusterArn:c,serviceArns:i}=$.data;A.debug(C,"ECS discovery complete",{appName:e.appName,clusterArn:c,serviceCount:i.length,serviceArns:i});const y=ae(q(e.path,"cdk.out")),D=Array.from(new Set(y.map(s=>s.imageKey))),k=c!==void 0&&i.length>0,R=y.length>0;if(!k&&!R){const s=`No deployable ECS services or container-image Lambda functions found for ${e.appName}`;return n.onLog?.(s,"warn"),{kind:"skipped",data:{target:e.appName,deploymentType:"application",durationMs:Date.now()-o,noChanges:!0,artefacts:[]}}}const u=c??"";if(h!==void 0){const s=k?await Se(r,e,u,i,h,n,t.abortSignal,o):{kind:"completed",data:{target:e.appName,deploymentType:"application",artefacts:[],durationMs:Date.now()-o}},{outcome:p,error:d}=await ve(r,e,h,n,t.abortSignal);return p.rolledImageKeys.length>0&&await x(r,e.appName,p.rolledImageKeys,n,t.abortSignal),V(s,p,d,e,o)}const E=m?.serviceName?.toLowerCase(),I=E===""?void 0:E,T=I?i.filter(s=>J(P(s)??"",I)):i;if(I){const s=y.some(p=>J(p.name,I));if(T.length===0&&!s){const p=[...i.map(d=>P(d)??"unknown"),...y.map(d=>d.name)];return{kind:"error",error:new Error(`Service '${I}' not found. Available: ${p.join(", ")||"none"}`)}}}const K={},O=[],L=[],U=[...i.map(s=>P(s)).filter(s=>s!==void 0&&s!==""),...D];for(let s=0;s<T.length;s++){if(H(t.abortSignal))return{kind:"error",error:new Error(`Rollout cancelled after ${O.length}/${T.length} services`)};const p=T[s];if(!p)continue;const d=P(p)??`service-${s+1}`,ee=`[${s+1}/${T.length}] ${d}`,F=$e(v,d),_=ke(F,a);let b,X;if(g!==void 0)b=ye(g,d,U),b!==g?(X=g,n.onLog?.(`Re-based image tag ${g} \u2192 ${b} for ${d}`,"info")):U.length>1&&!g.toLowerCase().startsWith(`${d.toLowerCase()}-`)&&n.onLog?.(`Image tag ${g} has no recognised service prefix; applying verbatim to ${d} \u2014 confirm it points at this service's image`,"warn");else if(_!==void 0)b=_;else{const Y=`No image available to deploy for ${d}: 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.`;n.onLog?.(Y,"warn"),L.push(`${d}: ${Y}`);continue}A.debug(C,"Starting service rollout",{serviceName:d,serviceArn:p,imageTag:b,explicitRollbackTag:g,contentHashTag:_,manifestServiceMatched:F?.name,dockerTarget:F?.docker.target,index:s,total:T.length}),n.onECSUpdate?.(`${ee}: rolling out image tag ${b}`,d);const S=await Z(r,u,p,d,b,n,X,void 0,t.abortSignal);if(A.debug(C,"Service rollout completed",{serviceName:d,success:S.success,...S.success?{taskDefinitionArn:S.data.taskDefinitionArn,imageDigest:S.data.imageDigest,durationMs:S.data.durationMs}:{error:N(S.error.message)}}),!S.success){L.push(`${d}: ${N(S.error.message)}`);continue}Q(d,b,S.data,K,O)}const B=Date.now()-o;if(L.length>0){const s=T.length-L.length,p=T.length,d=L.length===p?`All ${p} ECS service rollouts failed: ${L.join("; ")}`:`Partial rollout: ${s}/${p} services succeeded. Failures: ${L.join("; ")}`;return{kind:"error",error:new Error(d)}}g!==void 0&&await x(r,e.appName,O.map(s=>({serviceName:s.serviceName,imageTag:s.imageTag})),n,t.abortSignal);const W={kind:"completed",data:{target:e.appName,deploymentType:"application",outputs:Object.keys(K).length>0?K:void 0,artefacts:O,durationMs:B}};if(!R)return W;const j=g!==void 0?await pe(r,e,g,U,n,I,t.abortSignal):await he(r,e,a,l?.imageDigestsByService??{},l?.repoUri??le(r.awsProvider.getAccountId(),r.awsProvider.getRegion(),e.appName),n,I,t.abortSignal);return g!==void 0&&j.rolledImageKeys.length>0&&await x(r,e.appName,j.rolledImageKeys,n,t.abortSignal),V(W,j,void 0,e,o)},t.abortSignal)}async function Se(t,r,e,a,l,n,m,o){const f=me(a,l);for(const c of f.unmatchedLiveServiceNames)n.onLog?.(`Service ${c} is not part of this release \u2014 leaving it untouched`,"warn");for(const c of f.staleMapKeys)n.onLog?.(`Service ${c} from the release no longer exists in the stack`,"warn");if(f.matched.length===0)return{kind:"error",error:new Error("None of the services in serviceImageTags matched a live ECS service in this stack \u2014 nothing to roll")};const w=await fe(t,e,f.matched,m);if(!w.success)return{kind:"error",error:w.error};const g={},h=[],v=[];for(let c=0;c<f.matched.length;c++){if(H(m))return{kind:"error",error:new Error(`Rollout cancelled after ${h.length}/${f.matched.length} services`)};const i=f.matched[c];if(!i)continue;const y=`[${c+1}/${f.matched.length}] ${i.serviceName}`,D=w.data.get(i.serviceArn);if(D===void 0){v.push(`${i.serviceName}: pre-flight digest missing after a successful pre-flight`);continue}A.debug(C,"Starting service rollout (serviceImageTags)",{serviceName:i.serviceName,serviceArn:i.serviceArn,imageTag:i.tag,mapKey:i.mapKey,index:c,total:f.matched.length}),n.onECSUpdate?.(`${y}: rolling out image tag ${i.tag}`,i.serviceName);const k=await Z(t,e,i.serviceArn,i.serviceName,i.tag,n,void 0,D,m);if(A.debug(C,"Service rollout completed (serviceImageTags)",{serviceName:i.serviceName,success:k.success,...k.success?{taskDefinitionArn:k.data.taskDefinitionArn,imageDigest:k.data.imageDigest,durationMs:k.data.durationMs}:{error:N(k.error.message)}}),!k.success){v.push(`${i.serviceName}: ${N(k.error.message)}`);continue}Q(i.serviceName,i.tag,k.data,g,h)}const $=Date.now()-o;if(v.length>0){const c=f.matched.length-v.length,i=f.matched.length,y=v.length===i?`All ${i} ECS service rollouts failed: ${v.join("; ")}`:`Partial rollout: ${c}/${i} services succeeded. Failures: ${v.join("; ")}`;return{kind:"error",error:new Error(y)}}return await x(t,r.appName,h.map(c=>({serviceName:c.serviceName,imageTag:c.imageTag})),n,m),{kind:"completed",data:{target:r.appName,deploymentType:"application",outputs:Object.keys(g).length>0?g:void 0,artefacts:h,durationMs:$}}}async function Z(t,r,e,a,l,n,m,o,f){const w=Date.now();let g,h,v,$,c;if(o!==void 0)g=o.taskDefinition.previousTaskDefArn,h=o.taskDefinition.taskDefinition,v=o.taskDefinition.containerDefinitions,$=o.taskDefinition.repoUri,c=o.imageDigest;else{const u=await ue(t,r,e,a);if(!u.success)return z(u.error);g=u.data.previousTaskDefArn,h=u.data.taskDefinition,v=u.data.containerDefinitions,$=u.data.repoUri;const E=await t.ecrImageInspector.getImageDigest($,l,f);if(E.success)c=E.data;else{if(m!==void 0)return z(new Error(`Re-based image tag '${l}' (from supplied '${m}') was not found in ECR for ${a}: ${N(E.error.message)}. The supplied tag's build has no image for ${a} \u2014 supply that service's own tag, or pass --service to roll a single service.`));n.onLog?.(`Could not resolve image digest for ${a} (${N(E.error.message)}); falling back to tag pinning`,"warn")}}const i=u=>c!==void 0?`${u}@${c}`:`${u}:${l}`,y=v.map(u=>u.image?{...u,image:i(ce(u.image))}:u),D=await de(t,{clusterArn:r,serviceArn:e,serviceName:a,taskDefinition:h,containerDefinitions:y,previousTaskDefArn:g,...c!==void 0?{imageDigest:c}:{},startedAtMs:w},n);if(!D.success)return z(D.error);const k=`${$}:${l}`,R=Te($);return te({taskDefinitionArn:D.data.newTaskDefArn,previousTaskDefinitionArn:g,imageUri:k,ecrRepositoryArn:R,imageDigest:c,durationMs:D.data.durationMs})}function Te(t){const r=t.match(/^(\d{12})\.dkr\.ecr\.([a-z0-9-]+)\.amazonaws\.com\/(.+)$/);if(!r)return"";const[,e,a,l]=r;return`arn:aws:ecr:${a}:${e}:repository/${l}`}export{je as deployCodeOnly,Te as deriveEcrRepositoryArn,ke as lookupContentHashTag,ye as rebaseRollbackTagForService,x as reconcileImageTagParameters};
|
|
@@ -4,6 +4,7 @@ import type { DeployCallbacks } from "../../types/callbacks.js";
|
|
|
4
4
|
import type { ApplicationOperation } from "../../types/operations.js";
|
|
5
5
|
import type { DeployServices } from "../serviceFactory.js";
|
|
6
6
|
export declare const DOCKER_BUILD_STEP_NAME = "Building and pushing Docker image";
|
|
7
|
+
export declare function ecrRepoUri(accountId: string, region: string, appName: string): string;
|
|
7
8
|
/**
|
|
8
9
|
* Everything the build phase knows about what it pushed. The tag map drives
|
|
9
10
|
* the CDK `<Service>ImageTag` parameters; the digest map + repo URI feed the
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{isAbsolute as F,join as L,dirname as M,resolve as _}from"node:path";import{existsSync as K}from"node:fs";import{success as N,failure as h}from"@fjall/generator";import{deriveContentHashTag as W,maskSensitiveOutput as b,toKebab as x}from"@fjall/util";import{evaluateBakeGuard as z,evaluateResolvedBuildArgValues as X,acknowledgedBuildArgKeys as Y,buildCacheRepositoryName as H,BAKE_GUARD_EXPOSURE_CLAUSE as q}from"@fjall/util/docker";import{runWithBoundedConcurrency as J,resolveBuildConcurrency as Q}from"@fjall/util/buildConcurrency";import{logger as T}from"@fjall/util/logger";import{parseDockerServicesFromManifest as Z}from"@fjall/util/manifest";import{STEP_IDS as A}from"../../types/stepDefinitions.js";import{isAborted as U}from"../../aws/organisations/types.js";import{resolveBuildSecrets as ee,resolveSecretRefValue as re,resolveBuildSecretSessionProvider as te,sourcedRefsFromBuildArgs as ne}from"./buildSecretResolver.js";import{partitionForRegion as oe}from"./buildSecretSession.js";import{resolveBuildArgs as ie}from"./buildArgResolver.js";import{validateBuildGroup as se}from"./buildGroupValidator.js";import{ensureBuildCacheRepository as ae}from"./ecrCacheRepository.js";const I="dockerBuildHelper",C="Building and pushing Docker image";function de(e,n,t){const r=L(n,e);if(K(r))return{path:r,base:n};if(t!==""&&t!==n){const i=L(t,e);if(K(i))return{path:i,base:t}}return{path:r,base:n}}function ue(e,n,t){const r=e.docker.path,i=e.docker.context;let s,a;if(F(r))s=r,a=n;else{const p=de(r,n,t);s=p.path,a=p.base}return{buildContext:i?F(i)?_(i):_(a,i):_(M(s)),dockerfilePath:s,target:e.docker.target,resolvedViaFallback:a!==n}}function ce(e){return`${e.buildContext}|${e.dockerfilePath}|${e.target??""}`}function le(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 fe(e){return e.startsWith("cn-")?"amazonaws.com.cn":"amazonaws.com"}function G(e,n){return`${e}.dkr.ecr.${n}.${fe(n)}`}function ge(e,n,t){return`${G(e,n)}/${x(t)}`}function me(e,n,t){return`${G(e,n)}/${H(x(t))}`}function pe(e,n,t){const r=new Map;for(const i of e){const s=ue(i,n,t),a=ce(s),m=r.get(a);m?m.members.push(i):r.set(a,{identity:s,members:[i]})}return Array.from(r.values())}async function he(e,n,t,r,i){const s=await e.buildAndPush({appName:n.appName,appPath:n.path,region:t,accountId:r},(a,m,p,S)=>{i.onDockerProgress?.(b(a),m,p,S)});return s.success?N({imageUri:s.data.imageUri,imageTag:s.data.imageTag}):h(s.error)}function j(){return{contentHashTagsByService:{},imageDigestsByService:{}}}async function be(e,n,t,r,i,s,a,m,p,S,w,l,E){const c=e.members[0];if(!K(e.identity.dockerfilePath))return h(new Error(le(c.name,e.identity.dockerfilePath)));e.identity.resolvedViaFallback&&m.onLog?.(`Building service '${c.name}' from ${e.identity.dockerfilePath} (resolved at the project root).`,"info");const y=[...c.docker.buildSecrets??[],...E??[]],v=se({serviceName:c.name,dockerfilePath:e.identity.dockerfilePath,buildContext:e.identity.buildContext,...e.identity.target!==void 0&&{target:e.identity.target},...c.docker.buildArgs!==void 0&&{buildArgs:c.docker.buildArgs},...y.length>0&&{buildSecrets:y},members:e.members.map(o=>({serviceName:o.name,...o.docker.buildArgs!==void 0&&{buildArgs:o.docker.buildArgs},...o.docker.buildSecrets!==void 0&&{buildSecrets:o.docker.buildSecrets}}))});if(!v.success)return h(v.error);const k=z(c.docker.buildArgs,x(t.appName));for(const o of k.warnings)m.onLog?.(b(o.message),"warn");if(k.findings.length>0)return h(new Error(b(k.findings.map(o=>o.message).join(" "))));const d=y,f=ne(c.docker.buildArgs),D={appName:t.appName,region:r,accountId:i,partition:oe(r)},$=await te(p,[...d,...f],D);if(!$.success)return h($.error);const O=$.data,R=await ie({manifestBuildArgs:c.docker.buildArgs,serviceDir:e.identity.buildContext,appDir:t.path,...w!==void 0&&{stage:w},shellEnv:process.env,...l!==void 0&&{flagBuildArgs:l},resolveSourcedValue:(o,P)=>re(O,{id:o,...P},S)});if(!R.success)return h(R.error);const B=R.data.buildArgs;for(const o of R.data.inferredDotenvKeys)m.onLog?.(b(`buildArg "${o}" was auto-declared from a .env file and will be baked into the image \u2014 ${q}.`),"warn");const u=X(B,x(t.appName),Y(c.docker.buildArgs));if(u.length>0)return h(new Error(b(u.map(o=>o.message).join(" "))));let g;if(d.length>0){const o=await ee(O,d,S);if(!o.success)return h(o.error);g=o.data}try{if(U(S))return h(new Error(`Build of service '${c.name}' was aborted before the docker build started`));const o={appName:t.appName,appPath:e.identity.buildContext,region:r,accountId:i,buildContext:e.identity.buildContext,dockerfilePath:e.identity.dockerfilePath,pushByDigest:!0,serviceName:c.name,...a!==void 0&&{cacheRepoUri:a},...e.identity.target!==void 0&&{target:e.identity.target},...Object.keys(B).length>0&&{buildArgs:B},...g!==void 0&&{buildSecrets:g.resolved}};return await ve(e,o,n,s,m)}finally{g!==void 0&&await g.cleanup().catch(()=>m.onLog?.("Build-secret cleanup failed.","warn"))}}async function ve(e,n,t,r,i){const s=await t.buildAndPush(n,(l,E,c,y)=>{i.onDockerProgress?.(b(l),E,c,y)});if(!s.success)return h(s.error);const a=s.data.imageDigest;if(typeof a!="string"||!a.startsWith("sha256:"))return h(new Error(`Buildx push succeeded but returned an invalid digest: ${b(String(a))}`));const m={},p={},S=[];for(const l of e.members){const E=l.docker.target,c=W(a,l.name,E);if(!c.success)return h(c.error);const y=c.data,v=l.name;m[v]=y,p[v]=a,S.push(`${r}:${y}`)}const w=await t.tagByDigest({sourceImage:r,digest:a,tags:S});return w.success?N({contentHashTagsByService:m,imageDigestsByService:p}):h(w.error)}async function Te(e,n,t,r){const i=e.dockerProvider;if(!i)return N(j());const s=n.awsProvider.getAccountId();if(!s)return r.onLog?.("Skipping Docker build \u2014 account ID not available","warn"),N(j());const a=n.awsProvider.getRegion(),m=L(t.path,"cdk.out"),p=Z(m),S=ge(s,a,t.appName);if(T.debug(I,"runDockerBuild starting",{appName:t.appName,appPath:t.path,accountId:s,region:a,cdkOutPath:m,manifestServiceCount:p.length,manifestServices:p.map(d=>({name:d.name,path:d.docker.path,context:d.docker.context,target:d.docker.target})),stepId:A.DOCKER_OPERATIONS,stepName:C}),r.onStepStart?.(A.DOCKER_OPERATIONS,C),p.length===0){r.onLog?.("Initialising ECR repository\u2026","info");const d=await i.initialiseECR({appName:t.appName,region:a,accountId:s});d.success||r.onLog?.(`ECR initialisation failed: ${b(d.error.message)}`,"warn"),r.onLog?.("Building and pushing Docker image\u2026","info");const f=await he(i,t,a,s,r);if(!f.success){T.debug(I,"Docker buildAndPush (legacy) failed",{appName:t.appName,error:b(f.error.message)}),r.onStepComplete?.(A.DOCKER_OPERATIONS,C,"error");const D=new Error(b(f.error.message));return r.onError?.(D),h(D)}return r.onStepComplete?.(A.DOCKER_OPERATIONS,C,"completed"),r.onLog?.(`Docker image pushed: ${f.data.imageUri}`,"info"),N(j())}const w=await i.preflight?.();if(w!==void 0&&!w.success){r.onStepComplete?.(A.DOCKER_OPERATIONS,C,"error");const d=new Error(b(w.error.message));return r.onError?.(d),h(d)}const l=pe(p,t.path,e.workingDirectory);let E=me(s,a,t.appName);const c=await ae(n.awsProvider,H(x(t.appName)),e.abortSignal);c.success||(E=void 0,r.onLog?.(`Building without registry cache \u2014 ${c.error.message}`,"warn")),r.onLog?.(`Building and pushing ${p.length} service image(s) in ${l.length} group(s)\u2026`,"info");let y=!1;if(i.beginEcrSession!==void 0){const d=await i.beginEcrSession({accountId:s,region:a});if(!d.success){r.onStepComplete?.(A.DOCKER_OPERATIONS,C,"error");const f=new Error(b(d.error.message));return r.onError?.(f),h(f)}y=!0}const v=new AbortController,k=()=>v.abort();e.abortSignal!==void 0&&(e.abortSignal.aborted?v.abort():e.abortSignal.addEventListener("abort",k,{once:!0}));try{const d=()=>U(e.abortSignal)?"the deployment was cancelled":"a sibling group failed";let f;const D=Q(l.length),$=l.map((u,g)=>async()=>{const o=u.members.map(V=>V.name).join(", ");if(v.signal.aborted)throw new Error(`Build of group ${g+1}/${l.length} (${o}) was not started \u2014 ${d()}`);r.onLog?.(`Building group ${g+1}/${l.length} (${o})\u2026`,"info");const P=await be(u,i,t,a,s,S,E,r,n.awsProvider,v.signal,e.options?.environment,e.options?.flagBuildArgs,e.options?.flagBuildSecrets);if(!P.success)throw f===void 0&&(f={error:P.error,leader:u.members[0]?.name,members:o}),v.abort(),P.error;return P.data}),O=await J($,D,{abortOnFirstFailure:!0,abortSignal:v.signal});if(f===void 0)for(let u=0;u<l.length;u++){const g=O[u];if(!(g!==void 0&&g.success)){f={error:g!==void 0&&!g.success?g.error:new Error(`Build of group ${u+1}/${l.length} never ran \u2014 ${d()}`),leader:l[u].members[0]?.name,members:l[u].members.map(o=>o.name).join(", ")};break}}if(f!==void 0){T.debug(I,"Docker buildAndPush failed",{appName:t.appName,leader:f.leader,members:f.members,error:b(f.error.message)}),r.onStepComplete?.(A.DOCKER_OPERATIONS,C,"error");const u=new Error(b(f.error.message));return r.onError?.(u),h(u)}const R={},B={};for(const u of O)if(!(u===void 0||!u.success)){for(const[g,o]of Object.entries(u.data.contentHashTagsByService))R[g]=o;for(const[g,o]of Object.entries(u.data.imageDigestsByService))B[g]=o}return T.debug(I,"Docker buildAndPush succeeded",{appName:t.appName,services:p.map(u=>u.name),groupCount:l.length,contentHashTags:R}),r.onStepComplete?.(A.DOCKER_OPERATIONS,C,"completed"),r.onLog?.(`Docker images pushed for ${p.length} service(s)`,"info"),N({contentHashTagsByService:R,imageDigestsByService:B,repoUri:S})}finally{e.abortSignal?.removeEventListener("abort",k),y&&i.endEcrSession!==void 0&&await i.endEcrSession().catch(d=>{T.debug(I,"endEcrSession cleanup failed",{error:b(d instanceof Error?d.message:String(d))})})}}export{C as DOCKER_BUILD_STEP_NAME,j as emptyBuildProducts,Te as runDockerBuild};
|
|
1
|
+
import{isAbsolute as H,join as _,dirname as W,resolve as K}from"node:path";import{existsSync as F}from"node:fs";import{success as P,failure as h}from"@fjall/generator";import{deriveContentHashTag as z,maskSensitiveOutput as b,toKebab as x}from"@fjall/util";import{evaluateBakeGuard as X,evaluateResolvedBuildArgValues as Y,acknowledgedBuildArgKeys as q,createBakeWarningDeduper as J,buildCacheRepositoryName as U,BAKE_GUARD_EXPOSURE_CLAUSE as Q}from"@fjall/util/docker";import{runWithBoundedConcurrency as Z,resolveBuildConcurrency as ee}from"@fjall/util/buildConcurrency";import{logger as T}from"@fjall/util/logger";import{parseDockerServicesFromManifest as re,parseLambdaDockerServicesFromManifest as te}from"@fjall/util/manifest";import{STEP_IDS as A}from"../../types/stepDefinitions.js";import{isAborted as G}from"../../aws/organisations/types.js";import{resolveBuildSecrets as ne,resolveSecretRefValue as oe,resolveBuildSecretSessionProvider as ie,sourcedRefsFromBuildArgs as se}from"./buildSecretResolver.js";import{partitionForRegion as ae}from"./buildSecretSession.js";import{resolveBuildArgs as de}from"./buildArgResolver.js";import{validateBuildGroup as ce}from"./buildGroupValidator.js";import{ensureBuildCacheRepository as ue}from"./ecrCacheRepository.js";const I="dockerBuildHelper",C="Building and pushing Docker image";function le(e,n,t){const r=_(n,e);if(F(r))return{path:r,base:n};if(t!==""&&t!==n){const i=_(t,e);if(F(i))return{path:i,base:t}}return{path:r,base:n}}function fe(e,n,t){const r=e.docker.path,i=e.docker.context;let s,a;if(H(r))s=r,a=n;else{const p=le(r,n,t);s=p.path,a=p.base}return{buildContext:i?H(i)?K(i):K(a,i):K(W(s)),dockerfilePath:s,target:e.docker.target,resolvedViaFallback:a!==n}}function ge(e){return`${e.buildContext}|${e.dockerfilePath}|${e.target??""}`}function me(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 pe(e){return e.startsWith("cn-")?"amazonaws.com.cn":"amazonaws.com"}function M(e,n){return`${e}.dkr.ecr.${n}.${pe(n)}`}function he(e,n,t){return`${M(e,n)}/${x(t)}`}function be(e,n,t){return`${M(e,n)}/${U(x(t))}`}function ve(e,n,t){const r=new Map;for(const i of e){const s=fe(i,n,t),a=ge(s),g=r.get(a);g?g.members.push(i):r.set(a,{identity:s,members:[i]})}return Array.from(r.values())}async function Se(e,n,t,r,i){const s=await e.buildAndPush({appName:n.appName,appPath:n.path,region:t,accountId:r},(a,g,p,y)=>{i.onDockerProgress?.(b(a),g,p,y)});return s.success?P({imageUri:s.data.imageUri,imageTag:s.data.imageTag}):h(s.error)}function j(){return{contentHashTagsByService:{},imageDigestsByService:{}}}async function ye(e,n,t,r,i,s,a,g,p,y,E,f,k,w){const l=e.members[0];if(!F(e.identity.dockerfilePath))return h(new Error(me(l.name,e.identity.dockerfilePath)));e.identity.resolvedViaFallback&&g.onLog?.(`Building service '${l.name}' from ${e.identity.dockerfilePath} (resolved at the project root).`,"info");const v=[...l.docker.buildSecrets??[],...k??[]],N=ce({serviceName:l.name,dockerfilePath:e.identity.dockerfilePath,buildContext:e.identity.buildContext,...e.identity.target!==void 0&&{target:e.identity.target},...l.docker.buildArgs!==void 0&&{buildArgs:l.docker.buildArgs},...v.length>0&&{buildSecrets:v},members:e.members.map(o=>({serviceName:o.name,...o.docker.buildArgs!==void 0&&{buildArgs:o.docker.buildArgs},...o.docker.buildSecrets!==void 0&&{buildSecrets:o.docker.buildSecrets}}))});if(!N.success)return h(N.error);const d=X(l.docker.buildArgs,x(t.appName));for(const o of d.warnings){const S=b(o.message);w(S)&&g.onLog?.(S,"warn")}if(d.findings.length>0)return h(new Error(b(d.findings.map(o=>o.message).join(" "))));const u=v,D=se(l.docker.buildArgs),L={appName:t.appName,region:r,accountId:i,partition:ae(r)},$=await ie(p,[...u,...D],L);if(!$.success)return h($.error);const O=$.data,R=await de({manifestBuildArgs:l.docker.buildArgs,serviceDir:e.identity.buildContext,appDir:t.path,...E!==void 0&&{stage:E},shellEnv:process.env,...f!==void 0&&{flagBuildArgs:f},resolveSourcedValue:(o,S)=>oe(O,{id:o,...S},y)});if(!R.success)return h(R.error);const B=R.data.buildArgs;for(const o of R.data.inferredDotenvKeys){const S=b(`buildArg "${o}" was auto-declared from a .env file and will be baked into the image \u2014 ${Q}.`);w(S)&&g.onLog?.(S,"warn")}const c=Y(B,x(t.appName),q(l.docker.buildArgs));if(c.length>0)return h(new Error(b(c.map(o=>o.message).join(" "))));let m;if(u.length>0){const o=await ne(O,u,y);if(!o.success)return h(o.error);m=o.data}try{if(G(y))return h(new Error(`Build of service '${l.name}' was aborted before the docker build started`));const o={appName:t.appName,appPath:e.identity.buildContext,region:r,accountId:i,buildContext:e.identity.buildContext,dockerfilePath:e.identity.dockerfilePath,pushByDigest:!0,serviceName:l.name,...a!==void 0&&{cacheRepoUri:a},...e.identity.target!==void 0&&{target:e.identity.target},...Object.keys(B).length>0&&{buildArgs:B},...m!==void 0&&{buildSecrets:m.resolved}};return await we(e,o,n,s,g)}finally{m!==void 0&&await m.cleanup().catch(()=>g.onLog?.("Build-secret cleanup failed.","warn"))}}async function we(e,n,t,r,i){const s=await t.buildAndPush(n,(f,k,w,l)=>{i.onDockerProgress?.(b(f),k,w,l)});if(!s.success)return h(s.error);const a=s.data.imageDigest;if(typeof a!="string"||!a.startsWith("sha256:"))return h(new Error(`Buildx push succeeded but returned an invalid digest: ${b(String(a))}`));const g={},p={},y=[];for(const f of e.members){const k=f.docker.target,w=z(a,f.name,k);if(!w.success)return h(w.error);const l=w.data,v=f.name;g[v]=l,p[v]=a,y.push(`${r}:${l}`)}const E=await t.tagByDigest({sourceImage:r,digest:a,tags:y});return E.success?P({contentHashTagsByService:g,imageDigestsByService:p}):h(E.error)}async function _e(e,n,t,r){const i=e.dockerProvider;if(!i)return P(j());const s=n.awsProvider.getAccountId();if(!s)return r.onLog?.("Skipping Docker build \u2014 account ID not available","warn"),P(j());const a=n.awsProvider.getRegion(),g=_(t.path,"cdk.out"),p=[...re(g),...te(g)],y=he(s,a,t.appName);if(T.debug(I,"runDockerBuild starting",{appName:t.appName,appPath:t.path,accountId:s,region:a,cdkOutPath:g,manifestServiceCount:p.length,manifestServices:p.map(d=>({name:d.name,path:d.docker.path,context:d.docker.context,target:d.docker.target})),stepId:A.DOCKER_OPERATIONS,stepName:C}),r.onStepStart?.(A.DOCKER_OPERATIONS,C),p.length===0){r.onLog?.("Initialising ECR repository\u2026","info");const d=await i.initialiseECR({appName:t.appName,region:a,accountId:s});d.success||r.onLog?.(`ECR initialisation failed: ${b(d.error.message)}`,"warn"),r.onLog?.("Building and pushing Docker image\u2026","info");const u=await Se(i,t,a,s,r);if(!u.success){T.debug(I,"Docker buildAndPush (legacy) failed",{appName:t.appName,error:b(u.error.message)}),r.onStepComplete?.(A.DOCKER_OPERATIONS,C,"error");const D=new Error(b(u.error.message));return r.onError?.(D),h(D)}return r.onStepComplete?.(A.DOCKER_OPERATIONS,C,"completed"),r.onLog?.(`Docker image pushed: ${u.data.imageUri}`,"info"),P(j())}const E=await i.preflight?.();if(E!==void 0&&!E.success){r.onStepComplete?.(A.DOCKER_OPERATIONS,C,"error");const d=new Error(b(E.error.message));return r.onError?.(d),h(d)}const f=ve(p,t.path,e.workingDirectory);let k=be(s,a,t.appName);const w=await ue(n.awsProvider,U(x(t.appName)),e.abortSignal);w.success||(k=void 0,r.onLog?.(`Building without registry cache \u2014 ${w.error.message}`,"warn")),r.onLog?.(`Building and pushing ${p.length} service image(s) in ${f.length} group(s)\u2026`,"info");let l=!1;if(i.beginEcrSession!==void 0){const d=await i.beginEcrSession({accountId:s,region:a});if(!d.success){r.onStepComplete?.(A.DOCKER_OPERATIONS,C,"error");const u=new Error(b(d.error.message));return r.onError?.(u),h(u)}l=!0}const v=new AbortController,N=()=>v.abort();e.abortSignal!==void 0&&(e.abortSignal.aborted?v.abort():e.abortSignal.addEventListener("abort",N,{once:!0}));try{const d=()=>G(e.abortSignal)?"the deployment was cancelled":"a sibling group failed";let u;const D=ee(f.length),L=J(),$=f.map((c,m)=>async()=>{const o=c.members.map(V=>V.name).join(", ");if(v.signal.aborted)throw new Error(`Build of group ${m+1}/${f.length} (${o}) was not started \u2014 ${d()}`);r.onLog?.(`Building group ${m+1}/${f.length} (${o})\u2026`,"info");const S=await ye(c,i,t,a,s,y,k,r,n.awsProvider,v.signal,e.options?.environment,e.options?.flagBuildArgs,e.options?.flagBuildSecrets,L);if(!S.success)throw u===void 0&&(u={error:S.error,leader:c.members[0]?.name,members:o}),v.abort(),S.error;return S.data}),O=await Z($,D,{abortOnFirstFailure:!0,abortSignal:v.signal});if(u===void 0)for(let c=0;c<f.length;c++){const m=O[c];if(!(m!==void 0&&m.success)){u={error:m!==void 0&&!m.success?m.error:new Error(`Build of group ${c+1}/${f.length} never ran \u2014 ${d()}`),leader:f[c].members[0]?.name,members:f[c].members.map(o=>o.name).join(", ")};break}}if(u!==void 0){T.debug(I,"Docker buildAndPush failed",{appName:t.appName,leader:u.leader,members:u.members,error:b(u.error.message)}),r.onStepComplete?.(A.DOCKER_OPERATIONS,C,"error");const c=new Error(b(u.error.message));return r.onError?.(c),h(c)}const R={},B={};for(const c of O)if(!(c===void 0||!c.success)){for(const[m,o]of Object.entries(c.data.contentHashTagsByService))R[m]=o;for(const[m,o]of Object.entries(c.data.imageDigestsByService))B[m]=o}return T.debug(I,"Docker buildAndPush succeeded",{appName:t.appName,services:p.map(c=>c.name),groupCount:f.length,contentHashTags:R}),r.onStepComplete?.(A.DOCKER_OPERATIONS,C,"completed"),r.onLog?.(`Docker images pushed for ${p.length} service(s)`,"info"),P({contentHashTagsByService:R,imageDigestsByService:B,repoUri:y})}finally{e.abortSignal?.removeEventListener("abort",N),l&&i.endEcrSession!==void 0&&await i.endEcrSession().catch(d=>{T.debug(I,"endEcrSession cleanup failed",{error:b(d instanceof Error?d.message:String(d))})})}}export{C as DOCKER_BUILD_STEP_NAME,he as ecrRepoUri,j as emptyBuildProducts,_e as runDockerBuild};
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { type ServiceArtefact } from "@fjall/util";
|
|
2
|
+
import type { DeployParams } from "../../types/params.js";
|
|
3
|
+
import type { ApplicationOperation } from "../../types/operations.js";
|
|
4
|
+
import type { DeployServices } from "../serviceFactory.js";
|
|
5
|
+
/**
|
|
6
|
+
* Lambda-parallel rollout logic for `deployCodeOnly`'s three modes, mirroring
|
|
7
|
+
* the ECS rollout helpers in `codeOnlyDeploy.ts`. Unlike ECS, Lambda needs no
|
|
8
|
+
* CloudFormation-export-based discovery step: `computeLambda.ts` sets
|
|
9
|
+
* `functionName: id` verbatim on the underlying `Function` construct, and
|
|
10
|
+
* `compute.ts`'s manifest branch sets `manifestLambda.name = id` — the SAME
|
|
11
|
+
* `id`. So the manifest's `ManifestLambdaDockerEntry.name` IS the live
|
|
12
|
+
* function's exact `FunctionName`; `parseLambdaDockerEntriesFromManifest` is
|
|
13
|
+
* a complete, sufficient "what to update, and by what name" source.
|
|
14
|
+
*/
|
|
15
|
+
export interface LambdaRolloutOutcome {
|
|
16
|
+
outputs: Record<string, string>;
|
|
17
|
+
artefacts: ServiceArtefact[];
|
|
18
|
+
failures: string[];
|
|
19
|
+
/**
|
|
20
|
+
* Distinct `{imageKey, tag}` pairs actually rolled, deduped by imageKey —
|
|
21
|
+
* feed straight into `reconcileImageTagParameters` (rollback modes only;
|
|
22
|
+
* the primary path never reconciles, matching ECS's existing asymmetry).
|
|
23
|
+
*/
|
|
24
|
+
rolledImageKeys: Array<{
|
|
25
|
+
serviceName: string;
|
|
26
|
+
imageTag: string;
|
|
27
|
+
}>;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Primary path: roll every container Lambda whose `imageKey` was rebuilt
|
|
31
|
+
* this cycle. Builds a digest-pinned `ImageUri` directly from
|
|
32
|
+
* `imageDigestsByService` — no ECR `BatchGetImage` call needed, since
|
|
33
|
+
* `runDockerBuild` already resolved that digest for anything it just built.
|
|
34
|
+
*/
|
|
35
|
+
export declare function rolloutLambdaPrimary(services: DeployServices, operation: ApplicationOperation, contentHashTagsByService: Record<string, string>, imageDigestsByService: Record<string, string>, repoUri: string, callbacks: DeployParams["callbacks"], targetService: string | undefined, abortSignal?: AbortSignal): Promise<LambdaRolloutOutcome>;
|
|
36
|
+
/**
|
|
37
|
+
* `--image-tag <tag>` rollback: re-base the supplied tag onto each Lambda's
|
|
38
|
+
* `imageKey` family via `rebaseRollbackTagForService` — reused UNCHANGED,
|
|
39
|
+
* it's pure string manipulation with no ECS-specific logic. `allFamilyNames`
|
|
40
|
+
* MUST be the combined ECS-service-name ∪ Lambda-imageKey set so a supplied
|
|
41
|
+
* tag re-bases correctly across both entity types in one rollback; passing
|
|
42
|
+
* only ECS names here would let an ECS-family tag silently misapply to an
|
|
43
|
+
* unrelated Lambda.
|
|
44
|
+
*/
|
|
45
|
+
export declare function rolloutLambdaImageTag(services: DeployServices, operation: ApplicationOperation, explicitRollbackTag: string, allFamilyNames: readonly string[], callbacks: DeployParams["callbacks"], targetService: string | undefined, abortSignal?: AbortSignal): Promise<LambdaRolloutOutcome>;
|
|
46
|
+
/**
|
|
47
|
+
* `serviceImageTags` map rollback: exact-name match against manifest Lambda
|
|
48
|
+
* entries (simpler than ECS's ARN-suffix matching — Lambda function names
|
|
49
|
+
* are deterministic and exact, no live-ARN fuzziness to resolve). Preflights
|
|
50
|
+
* every matched entry's digest BEFORE touching any function, mirroring
|
|
51
|
+
* `preflightResolveServiceImageTagDigests`'s atomic-fail-closed behaviour: a
|
|
52
|
+
* single bad tag aborts the whole rollback before any `UpdateFunctionCode`
|
|
53
|
+
* call.
|
|
54
|
+
*/
|
|
55
|
+
export declare function rolloutLambdaServiceImageTags(services: DeployServices, operation: ApplicationOperation, serviceImageTags: Record<string, string>, callbacks: DeployParams["callbacks"], abortSignal?: AbortSignal): Promise<{
|
|
56
|
+
outcome: LambdaRolloutOutcome;
|
|
57
|
+
error?: Error;
|
|
58
|
+
}>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{join as I}from"path";import{artefactOutputKey as h,maskSensitiveOutput as $}from"@fjall/util";import{parseLambdaDockerEntriesFromManifest as w}from"@fjall/util/manifest";import{isAborted as b}from"../../aws/organisations/types.js";import{ecrRepoUri as k}from"./dockerBuildHelper.js";import{rebaseRollbackTagForService as L,deriveEcrRepositoryArn as P}from"./codeOnlyDeploy.js";import{matchesServiceName as K}from"./serviceImageTagsRollback.js";function R(){return{outputs:{},artefacts:[],failures:[],rolledImageKeys:[]}}function O(e,t,n,c,i,a,d){const r=P(i),u=`${i}:${c}`;e.outputs[h(t,"ImageTag")]=c,e.outputs[h(t,"ImageUri")]=u,e.outputs[h(t,"ImageDigest")]=a,r!==""&&(e.outputs[h(t,"EcrRepositoryArn")]=r),e.artefacts.push({serviceName:t,imageTag:c,imageUri:u,imageDigest:a,...r!==""?{ecrRepositoryArn:r}:{},functionArn:d.functionArn,publishedVersion:d.version}),e.rolledImageKeys.some(m=>m.serviceName===n)||e.rolledImageKeys.push({serviceName:n,imageTag:c})}async function v(e,t,n,c,i,a,d){d.onLog?.(`Updating Lambda function ${n.name} to ${a}`,"info");const r=await e.lambdaService.updateFunctionCode(n.name,`${c}@${i}`);if(!r.success){t.failures.push(`${n.name}: ${$(r.error.message)}`);return}O(t,n.name,n.imageKey,a,c,i,r.data)}function A(e,t){return t?e.filter(n=>K(n.name,t)):e}async function x(e,t,n,c,i,a,d,r){const u=R(),m=I(t.path,"cdk.out"),p=A(w(m),d);for(const f of p){if(b(r)){u.failures.push(`${f.name}: rollout cancelled before this function was reached`);continue}const l=n[f.imageKey],o=c[f.imageKey];l===void 0||o===void 0||await v(e,u,f,i,o,l,a)}return u}async function M(e,t,n,c,i,a,d){const r=R(),u=I(t.path,"cdk.out"),m=A(w(u),a);if(m.length===0)return r;const p=e.awsProvider.getAccountId(),f=e.awsProvider.getRegion(),l=k(p,f,t.appName);for(const o of m){if(b(d)){r.failures.push(`${o.name}: rollout cancelled before this function was reached`);continue}const g=L(n,o.imageKey,c),s=await e.ecrImageInspector.getImageDigest(l,g,d);if(!s.success||s.data===void 0){const y=s.success?"not an ECR repository":$(s.error.message);r.failures.push(`${o.name}: image tag '${g}' (rebased from '${n}') could not be resolved to a digest: ${y}`);continue}await v(e,r,o,l,s.data,g,i)}return r}async function j(e,t,n,c,i){const a=R(),d=I(t.path,"cdk.out"),u=w(d).map(o=>({entry:o,tag:n[o.name]})).filter(o=>o.tag!==void 0);if(u.length===0)return{outcome:a};const m=e.awsProvider.getAccountId(),p=e.awsProvider.getRegion(),f=k(m,p,t.appName),l=new Map;for(const{entry:o,tag:g}of u){const s=await e.ecrImageInspector.getImageDigest(f,g,i);if(!s.success||s.data===void 0){const y=s.success?"not an ECR repository":$(s.error.message);return{outcome:a,error:new Error(`Lambda function '${o.name}': image tag '${g}' could not be resolved to a digest: ${y}. No function was rolled.`)}}l.set(o.name,s.data)}for(const{entry:o,tag:g}of u){if(b(i)){a.failures.push(`${o.name}: rollout cancelled before this function was reached`);continue}const s=l.get(o.name);s!==void 0&&await v(e,a,o,f,s,g,c)}return{outcome:a}}export{M as rolloutLambdaImageTag,x as rolloutLambdaPrimary,j as rolloutLambdaServiceImageTags};
|
|
@@ -25,4 +25,4 @@ export declare const IMPORT_MAP_TIMEOUT_MS = 30000;
|
|
|
25
25
|
* `deployOnly` early-return check, so built artefacts are available
|
|
26
26
|
* for CDK synth.
|
|
27
27
|
*/
|
|
28
|
-
export declare function runOpenNextBuild(operation: ApplicationOperation, pattern: string | null | undefined, callbacks: DeployCallbacks, options?: DeployOptions): Promise<Result<void>>;
|
|
28
|
+
export declare function runOpenNextBuild(operation: ApplicationOperation, pattern: string | null | undefined, callbacks: DeployCallbacks, options?: DeployOptions, abortSignal?: AbortSignal): Promise<Result<void>>;
|