@fjall/deploy-core 3.11.1 → 3.11.2
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
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
234 files minified at 2026-08-08T02:
|
|
1
|
+
234 files minified at 2026-08-08T02:42:52.365Z
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var De=Object.defineProperty;var D=(e,a)=>De(e,"name",{value:a,configurable:!0});import{join as ue}from"path";import{CloudFormationClient as Pe}from"@aws-sdk/client-cloudformation";import{EC2Client as Re}from"@aws-sdk/client-ec2";import{success as C,failure as c}from"@fjall/generator";import{logger as $}from"@fjall/util/logger";import{ImageTagSchema as Ce,getErrorMessage as ge,imageTagParameterName as Ie,maskSensitiveOutput as y}from"@fjall/util";import{parseDockerDeclarationsFromManifest as Oe}from"@fjall/util/manifest";import{stubCallerIdentity as Ae}from"../../types/deployment/index.js";import{getApplicationDeployOrder as Le,getApplicationStackName as I,getApplicationStepName as pe,getApplicationStepId as fe,APPLICATION_STACKS as P}from"../../types/operations.js";import{CdkContextBuilder as Be}from"../../services/supporting/CdkContextBuilder.js";import{buildParamsContext as Me,bootstrapOrFail as Fe}from"../contextHelpers.js";import{buildBootstrapTagsIfResolved as $e}from"../bootstrapTags.js";import{hasOrganisationTierAccount as xe}from"../../aws/organisations/accountGlobals.js";import{runDetectionPipeline as je}from"./detectionPipeline.js";import{STEP_IDS as _,STEP_NAMES as Ge}from"../../types/stepDefinitions.js";import{StepRegistry as He}from"../../steps/stepRegistry.js";import{checkTargetReadiness as Ue}from"../../aws/targetReadiness.js";import{cascadeHomeRegion as _e}from"../organisation/cascadeHelpers.js";import{withStepLifecycle as qe}from"../stepLifecycle.js";import{DOCKER_BUILD_STEP_NAME as me,runDockerBuild as Ke}from"./dockerBuildHelper.js";import{getParallelPhase2Stacks as We,getStackNamesToDeploy as Ve,deployParallelPhase as ze,deployStackSequential as Je,runDockerPreCompute as Ye,runLambdaVersionPublish as Qe,createBuildCallbacks as Xe,willRunDockerBuild as Ze}from"./applicationDeployHelpers.js";import{verifyAssemblyIntegrity as ke}from"./assemblyIntegrity.js";import{captureDatabaseConnectionOutputs as et,captureServiceTaskDefinitions as tt,reconcileDatabaseEndpointChange as rt}from"./databaseEndpointReconcile.js";import{deployCodeOnly as at}from"./codeOnlyDeploy.js";import{checkDeployPathStackAvailability as he}from"../activeDeploymentGuard.js";import{assertLeaseBeforeStack as q}from"./leaseGate.js";import{collectFullDeployArtefacts as nt}from"./artefactCollection.js";import{CfnRegistryService as ot}from"../../services/infrastructure/CfnRegistryService.js";import{ChangeSetProbe as it}from"../../services/infrastructure/changeSetProbe.js";import{runApprovalGate as st}from"./approvalGate.js";import{buildDestructionTicket as ct,runDestructionGate as lt}from"./destructionGate.js";import{DESTRUCTION_RESUME_TTL_MS as dt,signApprovalToken as ut}from"./plan/approvalToken.js";import{computeAssemblyDigest as gt}from"./plan/assemblyDigest.js";import{createMemoisedPlanLoader as pt}from"./plan/loadDeployPlan.js";import{runDriftPreFlightGate as ft}from"../drift/preFlightGate.js";import{completeForgetAfterDeploy as mt}from"../remediation/forgetResource.js";import{composePostFailureRepairOffer as ye}from"../remediation/postFailureRepairOffer.js";import{renderPinReportLines as kt,runPinRemediation as ht}from"../remediation/pinRemediation.js";import{runRecreatePreFlight as yt}from"../remediation/recreatePreFlight.js";import{runRecreateSurgery as St}from"../remediation/recreateResource.js";const x="applicationDeploy";function bt(e){const a=Object.entries(e);if(a.length===0)return;const n={};for(const[t,i]of a){const S=Ie(t);n[S]=i}return n}D(bt,"buildCdkImageTagParameters");function wt(e){if(e?.mode!==void 0&&e.mode!=="rollback"){if(e.imageTag)return c(new Error(`--image-tag requires rollback mode (explicit mode '${e.mode}' was supplied alongside an image tag)`));if(e.serviceImageTags)return c(new Error(`serviceImageTags requires rollback mode (explicit mode '${e.mode}' was supplied alongside serviceImageTags)`))}if(e?.mode==="rollback"){if(e.imageTag&&e.serviceImageTags)return c(new Error("rollback mode accepts exactly one of --image-tag or serviceImageTags \u2014 they are mutually exclusive"));if(!e.imageTag&&!e.serviceImageTags)return c(new Error("rollback mode requires --image-tag <tag> or serviceImageTags naming the image(s) to roll to"))}if(e?.containerName!==void 0&&!e.imageTag)return c(new Error("--container-name requires --image-tag \u2014 it targets which container within the service's task definition receives the rolled-back image"));if(e?.serviceImageTags!==void 0){if(e.serviceName)return c(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 c(new Error("serviceImageTags must name at least one service"));for(const[n,t]of a){const i=Ce.safeParse(t);if(!i.success)return c(new Error(`serviceImageTags.${n} is not a valid image tag: ${i.error.message}`))}}return C(void 0)}D(wt,"validateRollbackOptions");function Se(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(S=>({stackName:S,...t!==""?{accountId:t}:{},...i!==""?{region:i}:{}})))}D(Se,"emitStackTargets");async function lr(e,a,n){const{callbacks:t,options:i}=e,S=Date.now(),j=i?.mode??(i?.imageTag||i?.serviceImageTags?"rollback":i?.deployOnly?"code-only":"full");if(j==="restart"){const r=new Error('mode "restart" is not a deploy \u2014 call the restart() entry point (fjall rollout) instead');return t.onError?.(r),c(r)}const Y=wt(i);if(!Y.success){const r=y(Y.error.message),o=new Error(r);return t.onError?.(o),c(o)}const G=Be.buildDeploymentContext({deployType:"application",target:n.appName,path:n.path,region:a.awsProvider.getRegion(),callerIdentity:Ae(a.awsProvider.getAccountId()),...Me({orgConfig:e.orgConfig,identity:e.identity,skipOidc:e.options?.skipOidc}),...e.managedDomainBindings!==void 0?{managedDomainBindings:e.managedDomainBindings}:{}},{verbose:i?.verbose,infraOnly:i?.infraOnly},e.orgConfig),T=a.frameworkRegistry.resolve({appPath:n.path});let Q;const L=j==="rollback";if(T&&!L){Q=T.builder.plan({appPath:n.path},T.detection);const r=Xe(t),o=await T.builder.build(n.path,Q,r,{skipBuild:i?.skipBuild,infraOnly:i?.infraOnly,...e.abortSignal!==void 0&&{abortSignal:e.abortSignal}});if(!o.success){const s=new Error(y(o.error.message));return t.onError?.(s),c(s)}}if(j==="code-only"||L){t.onLog?.(L?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=ue(n.path,"cdk.out"),o=Oe(r),s=o.declaresBuild,p=T?.detection.hasDockerfile===!0,l=s||p;$.debug(x,"Deploy-only branch entered",{mode:j,imageTag:i?.imageTag,serviceImageTags:i?.serviceImageTags,appName:n.appName,appPath:n.path,cdkOutPath:r,dockerProviderAvailable:e.dockerProvider!==void 0,builderName:T?.builder.name,hasDockerfileFromManifest:s,hasDockerfileFromDisk:p,hasDockerfile:l,manifestDockerServiceCount:o.ecsServices.length,manifestDockerPaths:o.ecsServices.map(f=>f.docker.path),manifestLambdaDockerEntryCount:o.lambdaEntries.length}),!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 he([I(n.appName,P.COMPUTE)],a.cfnService,e.abortSignal);if(!u.success)return t.onError?.(u.error),c(u.error);Se(t,a,[I(n.appName,P.COMPUTE)]);let k={},d;if(!L&&e.dockerProvider!==void 0&&l){$.debug(x,"Running Docker build before code-only deploy",{source:s?"manifest":"disk"});const f=await Ke(e,a,n,t);if(!f.success)return c(f.error);k=f.data.contentHashTagsByService,d=f.data}else $.debug(x,"Skipping Docker build",{reason:L?"rollback":e.dockerProvider===void 0?"no dockerProvider":"no Dockerfile detected"});const m=await q(e.assertLeaseForStack,I(n.appName,P.COMPUTE),e.abortSignal);return m!==void 0?(t.onError?.(m),c(m)):at(e,a,n,k,d)}t.onLog?.("Analysing infrastructure\u2026","info");const H=await je(n,a,G,t,e.abortSignal);if(!H.success){const r=new Error(y(H.error.message),{cause:H.error});return t.onError?.(r),c(r)}const g=H.data;try{await t.onDetectionComplete?.({...g,builderName:T?.builder.name??"unknown"})}catch(r){const o=new Error(y(ge(r)),{cause:r});return t.onError?.(o),c(o)}const be={deploymentType:"application",operation:"deploy",deployOnly:!1,infraOnly:i?.infraOnly??!1,hasDockerfile:g.hasDockerfile,pattern:g.pattern,resources:g.resources,...T&&{builderName:T.builder.name}},X=He.getSteps(be),Z=X.findIndex(r=>r.id===_.TARGET_READINESS),ee=await qe(t,{stepId:_.TARGET_READINESS,stepName:Ge.TARGET_READINESS,...Z>=0&&{stepIndex:Z,totalSteps:X.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=_e(e.orgConfig),l=await Ue({cloudFormation:a.awsProvider.getClient(Pe),ec2:new Re({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),{cause:l.error})}},e.abortSignal);if(!ee.success)return ee;const N=Le({pattern:g.pattern,resources:g.resources}),O=N.length,te=g.hasDockerfile&&e.dockerProvider!==void 0&&N.includes(P.COMPUTE)&&i?.infraOnly!==!0;if(!g.hasDifferences&&!i?.force&&!te){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=g.hasDockerfile&&e.dockerProvider!==void 0&&N.includes(P.COMPUTE);for(let s=0;s<N.length;s++){const p=N[s];r&&p===P.COMPUTE&&(t.onStepStart?.(_.DOCKER_OPERATIONS,me),t.onStepComplete?.(_.DOCKER_OPERATIONS,me,"skipped"));const l=fe(p,"deploy"),u=pe(p,"deploy");t.onStepStart?.(l,u,s,O),t.onStepComplete?.(l,u,"skipped",s,O)}const o=await a.stackService.resolveWebsiteUrl(n.appName);return C({target:n.appName,deploymentType:"application",outputs:o?{websiteUrl:o}:void 0,noChanges:!0,artefacts:[],durationMs:Date.now()-S})}const A=ue(n.path,"cdk.out"),b=Ve({deployOrder:N,stackChanges:g.stackChanges,appName:n.appName,force:i?.force===!0,computeBuildPossible:te});Se(t,a,[...b]);const K=await he([...b],a.cfnService,e.abortSignal);if(!K.success)return t.onError?.(K.error),c(K.error);const re={stacksToDeploy:[...b],imageBuild:Ze({stackNamesToDeploy:b,appName:n.appName,hasDockerProvider:e.dockerProvider!==void 0,hasDockerfile:g.hasDockerfile})},ae=gt(g.currentHashes,re),W=pt({cfnService:a.cfnService,cdkOutPath:A,changedStacks:[...b],assemblyDigest:ae,registry:new ot(a.awsProvider),changeSetProbe:new it(a.awsProvider),...e.abortSignal!==void 0?{abortSignal:e.abortSignal}:{}}),V=await ft(a.awsProvider,{stackNames:[...b],cdkOutPath:A,cfnService:a.cfnService,callbacks:t,...e.abortSignal!==void 0?{abortSignal:e.abortSignal}:{}});if(V.kind==="blocked"){const r=new Error(V.message);return t.onError?.(r),c(r)}const ne=V.verifiedAbsentByNecessity??[],U=e.approvalGate!==void 0||i?.approvalToken!==void 0&&i.approvalToken!==""||i?.planOnly===!0;if(U){const r=await st({approvalGate:e.approvalGate,options:i??{},target:n.appName,cdkOutPath:A,detection:{stackChanges:g.stackChanges,currentHashes:g.currentHashes},scope:re,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"),C({target:n.appName,deploymentType:"application",rejected:!0,artefacts:[],...o.planDetail!==void 0?{planDetail:o.planDetail}:{},durationMs:Date.now()-S});if(o.kind==="awaiting"){const s=await W(),p=s.success?ct(s.data,ne):void 0;return C({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()-S})}}const E=await lt({loadPlan:W,options:i??{},target:n.appName,callbacks:t,verifiedAbsentByNecessity:ne,...e.destructionConsentGate!==void 0?{consentGate:e.destructionConsentGate}:{}});if(!E.success)return t.onError?.(E.error),c(E.error);if(E.data.kind==="withheld"){const r=U?ut({assemblyDigest:ae,ttlMs:dt}):void 0;return C({target:n.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()-S})}if(E.data.kind==="proceed"){const{ticket:r,verbsByPhysicalName:o}=E.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 ht(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 kt(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"),C({target:n.appName,deploymentType:"application",pinPending:!0,pinReport:l,pinDeferredConsents:r.resources.length-s.length,destructionTicket:r,artefacts:[],durationMs:Date.now()-S})}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 d of p){const m=l.get(d.stack)??[];m.push(d),l.set(d.stack,m)}const u=await yt(a.awsProvider,{stacks:[...l].map(([d,m])=>({stackName:d,targets:m.map(f=>({logicalId:f.logicalId,resourceType:f.resourceType,physicalId:f.physicalName}))})),...e.abortSignal!==void 0?{abortSignal:e.abortSignal}:{}});if(!u.success){const d=new Error(y(u.error.message),{cause:u.error});return t.onError?.(d),c(d)}const k=[];for(const[d,m]of l){const f=await q(e.assertLeaseForStack,d,e.abortSignal,"destroy");if(f!==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?.(f),c(f);const R=m.map(h=>h.physicalName);t.onLog?.(`Recreate remediation for ${n.appName}: orchestrating the pinned-name replacement of ${R.join(", ")} on ${d}`,"warn");const F=await St(a.awsProvider,{stackName:d,targets:m.map(h=>({logicalId:h.logicalId,resourceType:h.resourceType,physicalId:h.physicalName})),appName:n.appName,consentedPhysicalNames:R,doneJournalPolicy:"archive-and-restart",plannedReplacementLogicalIds:m.map(h=>h.logicalId),onProgress:D(h=>t.onLog?.(h,"info"),"onProgress"),...e.abortSignal!==void 0?{abortSignal:e.abortSignal}:{}});if(!F.success){const h=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.`:"",w=new Error(y(`${F.error.message}${h}`));return t.onError?.(w),c(w)}k.push(`${d} (${R.join(", ")})`)}}}const we=e.orgConfig!==void 0&&!xe(e.orgConfig.providerAccounts),Te=$e({account:e.orgConfig?.providerAccounts.find(r=>r.id===a.awsProvider.getAccountId()),estate:we?"solo":"org"}),oe=await Fe(a,G,t,Te);if(!oe.success)return oe;const ie=I(n.appName,P.DATABASE),z=b.has(ie),Ne=z?await et(a.cfnService,n.appName,e.abortSignal):void 0,Ee=z?await tt(a.ecsResolver,a.ecsService,n.appName,e.abortSignal):void 0,B={},M=new Map;let se,ce={};const v=D(async()=>{!z||!M.has(ie)||await rt({appName:n.appName,connectionOutputsBefore:Ne,serviceTaskDefinitionsBefore:Ee,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}:{}})},"reconcileIfDatabaseReplaced");for(let r=0;r<N.length;r++){const o=N[r],s=I(n.appName,o),p=fe(o,"deploy"),l=pe(o,"deploy");if(!b.has(s)){t.onStepStart?.(p,l,r,O),t.onLog?.(`Skipping ${o} \u2014 no changes detected`,"info"),t.onStepComplete?.(p,l,"skipped",r,O);continue}const u=await q(e.assertLeaseForStack,s,e.abortSignal);if(u!==void 0)return await v(),t.onError?.(u),c(u);const k=We(N,r,n.appName,b);if(k.length>=2){const w=await ke({hashService:a.hashService,cdkOutPath:A,stackNames:k.map(J=>I(n.appName,J)),detectionHashes:g.currentHashes,approvalGated:U});if(!w.success)return await v(),t.onError?.(w.error),c(w.error);const de=await ze(k,n,a,G,t,r,O,g,B,M);if(!de.success)return await v(),c(await ye(a.awsProvider,{stackNames:k.map(J=>I(n.appName,J)),appName:n.appName,originalError:de.error,cfnService:a.cfnService,...e.abortSignal!==void 0?{abortSignal:e.abortSignal}:{}}));r+=k.length-1;continue}const d=await Ye(o,e,a,n,t,g.hasDockerfile);if(d!==null&&!d.success)return await v(),c(d.error);d!==null&&d.success&&(se=d.data);const m=d!==null&&d.success?d.data.contentHashTagsByService:{},f=bt(m);if(d!==null){const w=await q(e.assertLeaseForStack,s,e.abortSignal);if(w!==void 0)return await v(),t.onError?.(w),c(w)}const R=await ke({hashService:a.hashService,cdkOutPath:A,stackNames:[s],detectionHashes:g.currentHashes,approvalGated:U});if(!R.success)return await v(),t.onError?.(R.error),c(R.error);const F=await Je(o,a,G,t,r,O,B,f!==void 0||e.abortSignal!==void 0?{...f!==void 0?{parameters:f}:{},...e.abortSignal!==void 0?{abortSignal:e.abortSignal}:{}}:void 0);if(!F.success)return await v(),c(await ye(a.awsProvider,{stackNames:[s],appName:n.appName,originalError:F.error,cfnService:a.cfnService,...e.abortSignal!==void 0?{abortSignal:e.abortSignal}:{}}));o===P.COMPUTE&&(ce=await Qe(n,a,t,m,e.abortSignal));const h=g.currentHashes.get(s);h&&M.set(s,h)}if(await v(),M.size>0){const r=await a.hashService.updateStateAfterDeploy(n.path,M);if(!r.success){const o=y(r.error.message);$.debug(x,"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 le=await a.stackService.resolveWebsiteUrl(n.appName);le&&(B.websiteUrl=le);const ve=await nt(a,n.appName,se,t,A,ce,e.abortSignal);return await mt(a.awsProvider,{stackNames:[...b],appName:n.appName,onLog:D((r,o)=>t.onLog?.(r,o),"onLog"),...e.abortSignal!==void 0?{abortSignal:e.abortSignal}:{}}).catch(r=>{$.debug(x,"Forget completion sweep failed",{error:y(ge(r))})}),C({target:n.appName,deploymentType:"application",outputs:Object.keys(B).length>0?B:void 0,artefacts:ve,durationMs:Date.now()-S})}D(lr,"deployApplication");export{lr as deployApplication,Se as emitStackTargets};
|
|
1
|
+
var De=Object.defineProperty;var D=(e,a)=>De(e,"name",{value:a,configurable:!0});import{join as ue}from"path";import{CloudFormationClient as Pe}from"@aws-sdk/client-cloudformation";import{EC2Client as Re}from"@aws-sdk/client-ec2";import{success as C,failure as c}from"@fjall/generator";import{logger as $}from"@fjall/util/logger";import{ImageTagSchema as Ce,getErrorMessage as ge,imageTagParameterName as Ie,maskSensitiveOutput as y}from"@fjall/util";import{parseDockerDeclarationsFromManifest as Oe}from"@fjall/util/manifest";import{stubCallerIdentity as Ae}from"../../types/deployment/index.js";import{getApplicationDeployOrder as Le,getApplicationStackName as I,getApplicationStepName as fe,getApplicationStepId as pe,APPLICATION_STACKS as P}from"../../types/operations.js";import{CdkContextBuilder as Be}from"../../services/supporting/CdkContextBuilder.js";import{buildParamsContext as Me,bootstrapOrFail as Fe}from"../contextHelpers.js";import{buildBootstrapTagsIfResolved as $e}from"../bootstrapTags.js";import{hasOrganisationTierAccount as xe}from"../../aws/organisations/accountGlobals.js";import{runDetectionPipeline as je}from"./detectionPipeline.js";import{STEP_IDS as _,STEP_NAMES as Ge}from"../../types/stepDefinitions.js";import{StepRegistry as He}from"../../steps/stepRegistry.js";import{checkTargetReadiness as Ue}from"../../aws/targetReadiness.js";import{cascadeHomeRegion as _e}from"../organisation/cascadeHelpers.js";import{withStepLifecycle as qe}from"../stepLifecycle.js";import{DOCKER_BUILD_STEP_NAME as me,runDockerBuild as Ke}from"./dockerBuildHelper.js";import{getParallelPhase2Stacks as We,getStackNamesToDeploy as Ve,deployParallelPhase as ze,deployStackSequential as Je,runDockerPreCompute as Ye,runLambdaVersionPublish as Qe,createBuildCallbacks as Xe,willRunDockerBuild as Ze}from"./applicationDeployHelpers.js";import{verifyAssemblyIntegrity as ke}from"./assemblyIntegrity.js";import{captureDatabaseConnectionOutputs as et,captureServiceTaskDefinitions as tt,reconcileDatabaseEndpointChange as rt}from"./databaseEndpointReconcile.js";import{deployCodeOnly as at}from"./codeOnlyDeploy.js";import{checkDeployPathStackAvailability as he}from"../activeDeploymentGuard.js";import{assertLeaseBeforeStack as q}from"./leaseGate.js";import{collectFullDeployArtefacts as nt}from"./artefactCollection.js";import{CfnRegistryService as ot}from"../../services/infrastructure/CfnRegistryService.js";import{ChangeSetProbe as it}from"../../services/infrastructure/changeSetProbe.js";import{runApprovalGate as st}from"./approvalGate.js";import{buildDestructionTicket as ct,runDestructionGate as lt}from"./destructionGate.js";import{DESTRUCTION_RESUME_TTL_MS as dt,signApprovalToken as ut}from"./plan/approvalToken.js";import{computeAssemblyDigest as gt}from"./plan/assemblyDigest.js";import{createMemoisedPlanLoader as ft}from"./plan/loadDeployPlan.js";import{runDriftPreFlightGate as pt}from"../drift/preFlightGate.js";import{completeForgetAfterDeploy as mt}from"../remediation/forgetResource.js";import{composePostFailureRepairOffer as ye}from"../remediation/postFailureRepairOffer.js";import{renderPinReportLines as kt,runPinRemediation as ht}from"../remediation/pinRemediation.js";import{runRecreatePreFlight as yt}from"../remediation/recreatePreFlight.js";import{runRecreateSurgery as St}from"../remediation/recreateResource.js";const x="applicationDeploy";function bt(e){const a=Object.entries(e);if(a.length===0)return;const n={};for(const[t,i]of a){const S=Ie(t);n[S]=i}return n}D(bt,"buildCdkImageTagParameters");function wt(e){if(e?.mode!==void 0&&e.mode!=="rollback"){if(e.imageTag)return c(new Error(`--image-tag requires rollback mode (explicit mode '${e.mode}' was supplied alongside an image tag)`));if(e.serviceImageTags)return c(new Error(`serviceImageTags requires rollback mode (explicit mode '${e.mode}' was supplied alongside serviceImageTags)`))}if(e?.mode==="rollback"){if(e.imageTag&&e.serviceImageTags)return c(new Error("rollback mode accepts exactly one of --image-tag or serviceImageTags \u2014 they are mutually exclusive"));if(!e.imageTag&&!e.serviceImageTags)return c(new Error("rollback mode requires --image-tag <tag> or serviceImageTags naming the image(s) to roll to"))}if(e?.containerName!==void 0&&!e.imageTag)return c(new Error("--container-name requires --image-tag \u2014 it targets which container within the service's task definition receives the rolled-back image"));if(e?.serviceImageTags!==void 0){if(e.serviceName)return c(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 c(new Error("serviceImageTags must name at least one service"));for(const[n,t]of a){const i=Ce.safeParse(t);if(!i.success)return c(new Error(`serviceImageTags.${n} is not a valid image tag: ${i.error.message}`))}}return C(void 0)}D(wt,"validateRollbackOptions");function Se(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(S=>({stackName:S,...t!==""?{accountId:t}:{},...i!==""?{region:i}:{}})))}D(Se,"emitStackTargets");async function lr(e,a,n){const{callbacks:t,options:i}=e,S=Date.now(),j=i?.mode??(i?.imageTag||i?.serviceImageTags?"rollback":i?.deployOnly?"code-only":"full");if(j==="restart"){const r=new Error('mode "restart" is not a deploy \u2014 call the restart() entry point (fjall rollout) instead');return t.onError?.(r),c(r)}const Y=wt(i);if(!Y.success){const r=y(Y.error.message),o=new Error(r);return t.onError?.(o),c(o)}const G=Be.buildDeploymentContext({deployType:"application",target:n.appName,path:n.path,region:a.awsProvider.getRegion(),callerIdentity:Ae(a.awsProvider.getAccountId()),...Me({orgConfig:e.orgConfig,identity:e.identity,skipOidc:e.options?.skipOidc}),...e.managedDomainBindings!==void 0?{managedDomainBindings:e.managedDomainBindings}:{}},{verbose:i?.verbose,infraOnly:i?.infraOnly},e.orgConfig),T=a.frameworkRegistry.resolve({appPath:n.path});let Q;const L=j==="rollback";if(T&&!L){Q=T.builder.plan({appPath:n.path},T.detection);const r=Xe(t),o=await T.builder.build(n.path,Q,r,{skipBuild:i?.skipBuild,infraOnly:i?.infraOnly,...e.abortSignal!==void 0&&{abortSignal:e.abortSignal}});if(!o.success){const s=new Error(y(o.error.message));return t.onError?.(s),c(s)}}if(j==="code-only"||L){t.onLog?.(L?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=ue(n.path,"cdk.out"),o=Oe(r),s=o.declaresBuild,f=T?.detection.hasDockerfile===!0,l=s||f;$.debug(x,"Deploy-only branch entered",{mode:j,imageTag:i?.imageTag,serviceImageTags:i?.serviceImageTags,appName:n.appName,appPath:n.path,cdkOutPath:r,dockerProviderAvailable:e.dockerProvider!==void 0,builderName:T?.builder.name,hasDockerfileFromManifest:s,hasDockerfileFromDisk:f,hasDockerfile:l,manifestDockerServiceCount:o.ecsServices.length,manifestDockerPaths:o.ecsServices.map(p=>p.docker.path),manifestLambdaDockerEntryCount:o.lambdaEntries.length}),!s&&!f&&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 he([I(n.appName,P.COMPUTE)],a.cfnService,e.abortSignal);if(!u.success)return t.onError?.(u.error),c(u.error);Se(t,a,[I(n.appName,P.COMPUTE)]);let k={},d;if(!L&&e.dockerProvider!==void 0&&l){$.debug(x,"Running Docker build before code-only deploy",{source:s?"manifest":"disk"});const p=await Ke(e,a,n,t);if(!p.success)return c(p.error);k=p.data.contentHashTagsByService,d=p.data}else $.debug(x,"Skipping Docker build",{reason:L?"rollback":e.dockerProvider===void 0?"no dockerProvider":"no Dockerfile detected"});const m=await q(e.assertLeaseForStack,I(n.appName,P.COMPUTE),e.abortSignal);return m!==void 0?(t.onError?.(m),c(m)):at(e,a,n,k,d)}t.onLog?.("Analysing infrastructure\u2026","info");const H=await je(n,a,G,t,e.abortSignal);if(!H.success){const r=new Error(y(H.error.message),{cause:H.error});return t.onError?.(r),c(r)}const g=H.data;try{await t.onDetectionComplete?.({...g,builderName:T?.builder.name??"unknown"})}catch(r){const o=new Error(y(ge(r)),{cause:r});return t.onError?.(o),c(o)}const be={deploymentType:"application",operation:"deploy",deployOnly:!1,infraOnly:i?.infraOnly??!1,hasDockerfile:g.hasDockerfile,pattern:g.pattern,resources:g.resources,...T&&{builderName:T.builder.name}},X=He.getSteps(be),Z=X.findIndex(r=>r.id===_.TARGET_READINESS),ee=await qe(t,{stepId:_.TARGET_READINESS,stepName:Ge.TARGET_READINESS,...Z>=0&&{stepIndex:Z,totalSteps:X.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(),f=_e(e.orgConfig),l=await Ue({cloudFormation:a.awsProvider.getClient(Pe),ec2:new Re({region:f,...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),{cause:l.error})}},e.abortSignal);if(!ee.success)return ee;const N=Le({pattern:g.pattern,resources:g.resources}),O=N.length,te=g.hasDockerfile&&e.dockerProvider!==void 0&&N.includes(P.COMPUTE)&&i?.infraOnly!==!0;if(!g.hasDifferences&&!i?.force&&!te){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=g.hasDockerfile&&e.dockerProvider!==void 0&&N.includes(P.COMPUTE);for(let s=0;s<N.length;s++){const f=N[s];r&&f===P.COMPUTE&&(t.onStepStart?.(_.DOCKER_OPERATIONS,me),t.onStepComplete?.(_.DOCKER_OPERATIONS,me,"skipped"));const l=pe(f,"deploy"),u=fe(f,"deploy");t.onStepStart?.(l,u,s,O),t.onStepComplete?.(l,u,"skipped",s,O)}const o=await a.stackService.resolveWebsiteUrl(n.appName);return C({target:n.appName,deploymentType:"application",outputs:o?{websiteUrl:o}:void 0,noChanges:!0,artefacts:[],durationMs:Date.now()-S})}const A=ue(n.path,"cdk.out"),b=Ve({deployOrder:N,stackChanges:g.stackChanges,appName:n.appName,force:i?.force===!0,computeBuildPossible:te});Se(t,a,[...b]);const K=await he([...b],a.cfnService,e.abortSignal);if(!K.success)return t.onError?.(K.error),c(K.error);const re={stacksToDeploy:[...b],imageBuild:Ze({stackNamesToDeploy:b,appName:n.appName,hasDockerProvider:e.dockerProvider!==void 0,hasDockerfile:g.hasDockerfile,infraOnly:i?.infraOnly===!0})},ae=gt(g.currentHashes,re),W=ft({cfnService:a.cfnService,cdkOutPath:A,changedStacks:[...b],assemblyDigest:ae,registry:new ot(a.awsProvider),changeSetProbe:new it(a.awsProvider),...e.abortSignal!==void 0?{abortSignal:e.abortSignal}:{}}),V=await pt(a.awsProvider,{stackNames:[...b],cdkOutPath:A,cfnService:a.cfnService,callbacks:t,...e.abortSignal!==void 0?{abortSignal:e.abortSignal}:{}});if(V.kind==="blocked"){const r=new Error(V.message);return t.onError?.(r),c(r)}const ne=V.verifiedAbsentByNecessity??[],U=e.approvalGate!==void 0||i?.approvalToken!==void 0&&i.approvalToken!==""||i?.planOnly===!0;if(U){const r=await st({approvalGate:e.approvalGate,options:i??{},target:n.appName,cdkOutPath:A,detection:{stackChanges:g.stackChanges,currentHashes:g.currentHashes},scope:re,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"),C({target:n.appName,deploymentType:"application",rejected:!0,artefacts:[],...o.planDetail!==void 0?{planDetail:o.planDetail}:{},durationMs:Date.now()-S});if(o.kind==="awaiting"){const s=await W(),f=s.success?ct(s.data,ne):void 0;return C({target:n.appName,deploymentType:"application",awaitingApproval:!0,approvalToken:o.approvalToken,assemblyDigest:o.assemblyDigest,planDetail:o.planDetail,...f!==void 0?{destructionTicket:f}:{},artefacts:[],durationMs:Date.now()-S})}}const E=await lt({loadPlan:W,options:i??{},target:n.appName,callbacks:t,verifiedAbsentByNecessity:ne,...e.destructionConsentGate!==void 0?{consentGate:e.destructionConsentGate}:{}});if(!E.success)return t.onError?.(E.error),c(E.error);if(E.data.kind==="withheld"){const r=U?ut({assemblyDigest:ae,ttlMs:dt}):void 0;return C({target:n.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()-S})}if(E.data.kind==="proceed"){const{ticket:r,verbsByPhysicalName:o}=E.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 ht(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 kt(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"),C({target:n.appName,deploymentType:"application",pinPending:!0,pinReport:l,pinDeferredConsents:r.resources.length-s.length,destructionTicket:r,artefacts:[],durationMs:Date.now()-S})}const f=r.resources.filter(l=>l.finding==="pinned-name-replacement"&&o.get(l.physicalName)==="recreate");if(f.length>0){const l=new Map;for(const d of f){const m=l.get(d.stack)??[];m.push(d),l.set(d.stack,m)}const u=await yt(a.awsProvider,{stacks:[...l].map(([d,m])=>({stackName:d,targets:m.map(p=>({logicalId:p.logicalId,resourceType:p.resourceType,physicalId:p.physicalName}))})),...e.abortSignal!==void 0?{abortSignal:e.abortSignal}:{}});if(!u.success){const d=new Error(y(u.error.message),{cause:u.error});return t.onError?.(d),c(d)}const k=[];for(const[d,m]of l){const p=await q(e.assertLeaseForStack,d,e.abortSignal,"destroy");if(p!==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?.(p),c(p);const R=m.map(h=>h.physicalName);t.onLog?.(`Recreate remediation for ${n.appName}: orchestrating the pinned-name replacement of ${R.join(", ")} on ${d}`,"warn");const F=await St(a.awsProvider,{stackName:d,targets:m.map(h=>({logicalId:h.logicalId,resourceType:h.resourceType,physicalId:h.physicalName})),appName:n.appName,consentedPhysicalNames:R,doneJournalPolicy:"archive-and-restart",plannedReplacementLogicalIds:m.map(h=>h.logicalId),onProgress:D(h=>t.onLog?.(h,"info"),"onProgress"),...e.abortSignal!==void 0?{abortSignal:e.abortSignal}:{}});if(!F.success){const h=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.`:"",w=new Error(y(`${F.error.message}${h}`));return t.onError?.(w),c(w)}k.push(`${d} (${R.join(", ")})`)}}}const we=e.orgConfig!==void 0&&!xe(e.orgConfig.providerAccounts),Te=$e({account:e.orgConfig?.providerAccounts.find(r=>r.id===a.awsProvider.getAccountId()),estate:we?"solo":"org"}),oe=await Fe(a,G,t,Te);if(!oe.success)return oe;const ie=I(n.appName,P.DATABASE),z=b.has(ie),Ne=z?await et(a.cfnService,n.appName,e.abortSignal):void 0,Ee=z?await tt(a.ecsResolver,a.ecsService,n.appName,e.abortSignal):void 0,B={},M=new Map;let se,ce={};const v=D(async()=>{!z||!M.has(ie)||await rt({appName:n.appName,connectionOutputsBefore:Ne,serviceTaskDefinitionsBefore:Ee,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}:{}})},"reconcileIfDatabaseReplaced");for(let r=0;r<N.length;r++){const o=N[r],s=I(n.appName,o),f=pe(o,"deploy"),l=fe(o,"deploy");if(!b.has(s)){t.onStepStart?.(f,l,r,O),t.onLog?.(`Skipping ${o} \u2014 no changes detected`,"info"),t.onStepComplete?.(f,l,"skipped",r,O);continue}const u=await q(e.assertLeaseForStack,s,e.abortSignal);if(u!==void 0)return await v(),t.onError?.(u),c(u);const k=We(N,r,n.appName,b);if(k.length>=2){const w=await ke({hashService:a.hashService,cdkOutPath:A,stackNames:k.map(J=>I(n.appName,J)),detectionHashes:g.currentHashes,approvalGated:U});if(!w.success)return await v(),t.onError?.(w.error),c(w.error);const de=await ze(k,n,a,G,t,r,O,g,B,M);if(!de.success)return await v(),c(await ye(a.awsProvider,{stackNames:k.map(J=>I(n.appName,J)),appName:n.appName,originalError:de.error,cfnService:a.cfnService,...e.abortSignal!==void 0?{abortSignal:e.abortSignal}:{}}));r+=k.length-1;continue}const d=await Ye(o,e,a,n,t,g.hasDockerfile);if(d!==null&&!d.success)return await v(),c(d.error);d!==null&&d.success&&(se=d.data);const m=d!==null&&d.success?d.data.contentHashTagsByService:{},p=bt(m);if(d!==null){const w=await q(e.assertLeaseForStack,s,e.abortSignal);if(w!==void 0)return await v(),t.onError?.(w),c(w)}const R=await ke({hashService:a.hashService,cdkOutPath:A,stackNames:[s],detectionHashes:g.currentHashes,approvalGated:U});if(!R.success)return await v(),t.onError?.(R.error),c(R.error);const F=await Je(o,a,G,t,r,O,B,p!==void 0||e.abortSignal!==void 0?{...p!==void 0?{parameters:p}:{},...e.abortSignal!==void 0?{abortSignal:e.abortSignal}:{}}:void 0);if(!F.success)return await v(),c(await ye(a.awsProvider,{stackNames:[s],appName:n.appName,originalError:F.error,cfnService:a.cfnService,...e.abortSignal!==void 0?{abortSignal:e.abortSignal}:{}}));o===P.COMPUTE&&(ce=await Qe(n,a,t,m,e.abortSignal));const h=g.currentHashes.get(s);h&&M.set(s,h)}if(await v(),M.size>0){const r=await a.hashService.updateStateAfterDeploy(n.path,M);if(!r.success){const o=y(r.error.message);$.debug(x,"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 le=await a.stackService.resolveWebsiteUrl(n.appName);le&&(B.websiteUrl=le);const ve=await nt(a,n.appName,se,t,A,ce,e.abortSignal);return await mt(a.awsProvider,{stackNames:[...b],appName:n.appName,onLog:D((r,o)=>t.onLog?.(r,o),"onLog"),...e.abortSignal!==void 0?{abortSignal:e.abortSignal}:{}}).catch(r=>{$.debug(x,"Forget completion sweep failed",{error:y(ge(r))})}),C({target:n.appName,deploymentType:"application",outputs:Object.keys(B).length>0?B:void 0,artefacts:ve,durationMs:Date.now()-S})}D(lr,"deployApplication");export{lr as deployApplication,Se as emitStackTargets};
|
|
@@ -28,20 +28,19 @@ export declare function getStackNamesToDeploy(params: {
|
|
|
28
28
|
/**
|
|
29
29
|
* Single source of the deploy loop's "will an image build/push run" outcome,
|
|
30
30
|
* bound into the approval digest (DeployScope.imageBuild) so a token approves
|
|
31
|
-
* the exact build behaviour, not the flag that requested it. Mirrors
|
|
32
|
-
*
|
|
33
|
-
* to-deploy set),
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
* the loop consults only the three gates above, so `--infra-only` reaches
|
|
38
|
-
* this predicate solely through the to-deploy set.
|
|
31
|
+
* the exact build behaviour, not the flag that requested it. Mirrors
|
|
32
|
+
* `runDockerPreCompute`'s ACTUAL gates: the skip predicate (Compute must be
|
|
33
|
+
* in the to-deploy set), its provider/hasDockerfile guard, and — because a
|
|
34
|
+
* hash-changed Compute stack can still land in the to-deploy set for a
|
|
35
|
+
* genuine infra reason — an explicit `infraOnly` check, so `--infra-only`
|
|
36
|
+
* never authorises an image build no matter why Compute is in that set.
|
|
39
37
|
*/
|
|
40
38
|
export declare function willRunDockerBuild(params: {
|
|
41
39
|
stackNamesToDeploy: ReadonlySet<string>;
|
|
42
40
|
appName: string;
|
|
43
41
|
hasDockerProvider: boolean;
|
|
44
42
|
hasDockerfile: boolean;
|
|
43
|
+
infraOnly: boolean;
|
|
45
44
|
}): boolean;
|
|
46
45
|
/**
|
|
47
46
|
* Identify Phase 2 stacks (Storage, Messaging, Database) that are consecutive
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
var B=Object.defineProperty;var a=(e,r)=>B(e,"name",{value:r,configurable:!0});import{join as T}from"node:path";import{success as N,failure as S}from"@fjall/generator";import{maskSensitiveOutput as w}from"@fjall/util";import{parseLambdaDockerEntriesFromManifest as
|
|
1
|
+
var B=Object.defineProperty;var a=(e,r)=>B(e,"name",{value:r,configurable:!0});import{join as T}from"node:path";import{success as N,failure as S}from"@fjall/generator";import{maskSensitiveOutput as w}from"@fjall/util";import{parseLambdaDockerEntriesFromManifest as R}from"@fjall/util/manifest";import{isAborted as k}from"../../aws/organisations/types.js";import{APPLICATION_STACKS as E,getApplicationStackName as P,getApplicationStepName as b,getApplicationStepId as C,PARALLEL_DEPLOY_GROUPS as $}from"../../types/operations.js";import{emitProgress as D}from"../../services/supporting/helpers.js";import{forwardOutput as O,forwardResourceProgress as j}from"../contextHelpers.js";import{emptyBuildProducts as v,runDockerBuild as I}from"./dockerBuildHelper.js";import{runWelcomeImageSetup as _}from"./welcomeImageHelper.js";import{withStepLifecycle as U}from"../stepLifecycle.js";function X(e){const r=new Set;for(const i of e.deployOrder){const s=P(e.appName,i),o=e.stackChanges.get(s)??!0,u=i===E.COMPUTE&&e.computeBuildPossible;(o||e.force||u)&&r.add(s)}return r}a(X,"getStackNamesToDeploy");function Z(e){return!e.infraOnly&&e.stackNamesToDeploy.has(P(e.appName,E.COMPUTE))&&e.hasDockerProvider&&e.hasDockerfile}a(Z,"willRunDockerBuild");function ee(e,r,i,s){const o=new Set($.PHASE_2.stacks),u=[];for(let n=r;n<e.length&&o.has(e[n]);n++)u.push(e[n]);if(u.length<2)return[];const c=u.filter(n=>s.has(P(i,n)));return c.length>=2?c:[]}a(ee,"getParallelPhase2Stacks");async function te(e,r,i,s,o,u,c,n,m,f){for(let t=0;t<e.length;t++){const l=e[t],p=C(l,"deploy"),g=b(l,"deploy");o.onStepStart?.(p,g,u+t,c)}D(o,"Deploying infrastructure in parallel\u2026"),o.onParallelPhaseStart?.(e,"Storage and database resources (parallel)");const d=await i.stackService.deployStacksInParallel(e,s,{onOutput:O(o),onResourceProgress:a((t,l)=>{o.onResourceProgress?.(t),l&&o.onParallelStackResourceProgress?.(l,t)},"onResourceProgress"),onStackComplete:a((t,l,p,g)=>{const y=C(t,"deploy"),x=b(t,"deploy"),A=u+e.indexOf(t),L=k(i.abortSignal);o.onStepComplete?.(y,x,l?"completed":L?"cancelled":"error",A,c),!l&&g&&!L&&o.onError?.(new Error(w(g.message)))},"onStackComplete")});if(!d.success)return o.onParallelPhaseComplete?.([]),S(d.error);const h=d.data.filter(t=>!t.success);if(o.onParallelPhaseComplete?.(d.data.map(t=>({stack:t.stack,success:t.success,error:t.error}))),h.length>0){const t=h.map(p=>p.stack).join(", "),l=h.map(p=>`${p.stack}: ${w(p.error?.message??"Unknown error")}`).join(`
|
|
2
2
|
`);return S(new Error(`Failed to deploy stacks: ${t}
|
|
3
3
|
|
|
4
|
-
${
|
|
4
|
+
${l}`))}for(const t of d.data){if(t.success&&t.outputs)for(const[g,y]of Object.entries(t.outputs))m[g]=String(y);const l=P(r.appName,t.stack),p=n.currentHashes.get(l);p&&f.set(l,p)}return N(void 0)}a(te,"deployParallelPhase");async function re(e,r,i,s,o,u,c,n){const m=C(e,"deploy"),f=b(e,"deploy");return U(s,{stepId:m,stepName:f,stepIndex:o,totalSteps:u},async()=>{const d=await r.stackService.deployStack(e,i,{onOutput:O(s),onResourceProgress:j(s)},n?.parameters!==void 0||n?.abortSignal!==void 0?{...n?.parameters!==void 0?{parameters:n.parameters}:{},...n?.abortSignal!==void 0?{abortSignal:n.abortSignal}:{}}:void 0);if(!d.success)return{kind:"error",error:d.error};if(d.data.outputs)for(const[h,t]of Object.entries(d.data.outputs))c[h]=String(t);return{kind:"completed",data:void 0}},r.abortSignal)}a(re,"deployStackSequential");async function oe(e,r,i,s,o,u){if(e!==E.COMPUTE||!r.dockerProvider)return null;if(u&&r.options?.infraOnly!==!0){const n=await I(r,i,s,o);return n.success?N(n.data):S(n.error)}if(u)return null;const c=await _(r,i,s,o);return c.success?N(v()):S(c.error)}a(oe,"runDockerPreCompute");async function ne(e,r,i,s,o){if(Object.keys(s).length===0)return{};const u=T(e.path,"cdk.out"),n=R(u).filter(f=>s[f.imageKey]!==void 0);if(n.length===0)return{};const m={};for(const f of n){if(k(o)){i.onLog?.(`Skipping the Lambda version publish for '${f.name}' \u2014 the deployment was cancelled. The function's $LATEST code is up to date; only the published-version record is affected.`,"warn");continue}const d=await r.lambdaService.publishVersion(f.name,o);if(!d.success){i.onLog?.(`Published image for Lambda '${f.name}', but publishing a new version failed: ${w(d.error.message)}. The function's $LATEST code is up to date; only the published-version record is affected.`,"warn");continue}m[f.name]=d.data}return m}a(ne,"runLambdaVersionPublish");function ie(e){return{onBuildStart:a(r=>{e.onOpenNextBuildStart?.(),e.onLog?.(`${r} build started`,"info")},"onBuildStart"),onBuildProgress:a((r,i)=>{e.onOpenNextProgress?.(i)},"onBuildProgress"),onBuildComplete:a(r=>{e.onOpenNextBuildComplete?.(),e.onLog?.(`${r} build complete`,"info")},"onBuildComplete"),onBuildError:a((r,i)=>{e.onOpenNextBuildError?.(i)},"onBuildError")}}a(ie,"createBuildCallbacks");export{ie as createBuildCallbacks,te as deployParallelPhase,re as deployStackSequential,ee as getParallelPhase2Stacks,X as getStackNamesToDeploy,oe as runDockerPreCompute,ne as runLambdaVersionPublish,Z as willRunDockerBuild};
|