@fjall/deploy-core 2.30.3 → 2.31.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/.minified +1 -1
- package/dist/src/aws/organisations/oidcProvider.js +1 -1
- package/dist/src/aws/utils/cloudformationEventHelpers.d.ts +2 -2
- package/dist/src/aws/utils/cloudformationEventHelpers.js +1 -1
- package/dist/src/index.d.ts +2 -2
- package/dist/src/index.js +1 -1
- package/dist/src/orchestration/activeDeploymentGuard.d.ts +1 -1
- package/dist/src/orchestration/activeDeploymentGuard.js +3 -3
- package/dist/src/orchestration/application/applicationDeploy.js +1 -1
- package/dist/src/orchestration/application/applicationDeployHelpers.d.ts +2 -1
- package/dist/src/orchestration/application/applicationDeployHelpers.js +3 -3
- package/dist/src/orchestration/application/applicationDestroy.js +1 -1
- package/dist/src/orchestration/application/artefactCollection.d.ts +3 -2
- package/dist/src/orchestration/application/artefactCollection.js +1 -1
- package/dist/src/orchestration/application/codeOnlyDeploy.d.ts +0 -22
- package/dist/src/orchestration/application/codeOnlyDeploy.js +1 -1
- package/dist/src/orchestration/application/databaseEndpointReconcile.d.ts +2 -2
- package/dist/src/orchestration/application/databaseEndpointReconcile.js +1 -1
- package/dist/src/orchestration/application/destroyPreFlight.d.ts +33 -0
- package/dist/src/orchestration/application/destroyPreFlight.js +1 -0
- package/dist/src/orchestration/application/detectionPipeline.d.ts +1 -1
- package/dist/src/orchestration/application/detectionPipeline.js +1 -1
- package/dist/src/orchestration/application/dockerBuildHelper.js +1 -1
- package/dist/src/orchestration/application/lambdaCodeOnlyRollout.d.ts +42 -10
- package/dist/src/orchestration/application/lambdaCodeOnlyRollout.js +1 -1
- package/dist/src/orchestration/application/plan/computeDeployPlan.d.ts +1 -1
- package/dist/src/orchestration/application/plan/computeDeployPlan.js +1 -1
- package/dist/src/orchestration/application/serviceImageTagsRollback.d.ts +1 -1
- package/dist/src/orchestration/application/serviceImageTagsRollback.js +1 -1
- package/dist/src/orchestration/application/taskDefinitionRoll.d.ts +1 -1
- package/dist/src/orchestration/application/taskDefinitionRoll.js +1 -1
- package/dist/src/orchestration/destroy.js +1 -1
- package/dist/src/orchestration/drift/driftPreFlight.js +3 -3
- package/dist/src/orchestration/index.d.ts +1 -1
- package/dist/src/orchestration/index.js +1 -1
- package/dist/src/orchestration/organisation/cascadeHelpers.js +1 -1
- package/dist/src/orchestration/organisationDeploy/infraSteps.d.ts +1 -1
- package/dist/src/orchestration/organisationDeploy/infraSteps.js +1 -1
- package/dist/src/orchestration/organisationDeploy/orgCascadeDeploy.js +4 -4
- package/dist/src/orchestration/organisationDeploy/singleComponentDeploy.js +1 -1
- package/dist/src/orchestration/probes/deleteBlockProbes.d.ts +38 -0
- package/dist/src/orchestration/probes/deleteBlockProbes.js +1 -0
- package/dist/src/orchestration/remediation/recreatePreFlight.js +1 -1
- package/dist/src/orchestration/remediation/recreateResource.js +1 -1
- package/dist/src/orchestration/remediation/removalUpdate.d.ts +74 -13
- package/dist/src/orchestration/remediation/removalUpdate.js +1 -1
- package/dist/src/orchestration/restart/restartApplication.js +1 -1
- package/dist/src/orchestration/restart/secretsDrift.js +1 -1
- package/dist/src/orchestration/stackCleanup/stackResources.d.ts +5 -3
- package/dist/src/services/application/ApplicationStackService.d.ts +2 -1
- package/dist/src/services/application/ApplicationStackService.js +1 -1
- package/dist/src/services/infrastructure/CloudFormationService.d.ts +11 -10
- package/dist/src/services/infrastructure/CloudFormationService.js +1 -1
- package/dist/src/services/infrastructure/EcsService.d.ts +11 -9
- package/dist/src/services/infrastructure/EcsService.js +1 -1
- package/dist/src/services/infrastructure/EcsServiceResolver.d.ts +4 -4
- package/dist/src/services/infrastructure/EcsServiceResolver.js +1 -1
- package/dist/src/services/infrastructure/LambdaService.d.ts +19 -2
- package/dist/src/services/infrastructure/LambdaService.js +1 -1
- package/dist/src/types/application/ApplicationServiceTypes.d.ts +1 -0
- package/dist/src/types/application/ApplicationServiceTypes.js +1 -1
- package/dist/src/types/remediation.d.ts +1 -0
- package/dist/src/types/remediation.js +1 -1
- package/package.json +5 -4
|
@@ -1 +1 @@
|
|
|
1
|
-
import{maskSensitiveOutput as
|
|
1
|
+
import{maskSensitiveOutput as h}from"@fjall/util";import{logger as w}from"@fjall/util/logger";import{getApplicationStackName as R,APPLICATION_STACKS as T}from"../../types/operations.js";import{extractEcsServiceName as N}from"../../aws/utils/arnParser.js";import{isAborted as C}from"../../aws/organisations/types.js";import{resolveServiceTaskDefinition as O}from"./serviceImageTagsRollback.js";import{registerAndRollTaskDefinition as E}from"./taskDefinitionRoll.js";const b="databaseEndpointReconcile",j=["Endpoint","Port"];async function I(s,t,a){const c=R(t,T.DATABASE),e=await s.getStackOutputs(c,void 0,a);if(!e.success){w.debug(b,"Could not read database stack outputs",{stackName:c,error:h(e.error.message)});return}const r={};for(const i of e.data){const l=i.OutputKey,f=i.OutputValue;l===void 0||f===void 0||j.some(d=>l.endsWith(d))&&(r[l]=f)}return r}async function W(s,t,a,c){const e=await s.getDeployableClusterAndServices(a,c);if(!e.success){w.debug(b,"Could not discover services pre-deploy",{appName:a,error:h(e.error.message)});return}const{clusterArn:r,serviceArns:i}=e.data;if(r===void 0||i.length===0)return{};const l=await t.describeServices(r,i,c);if(!l.success){w.debug(b,"Could not describe services pre-deploy",{appName:a,error:h(l.error.message)});return}const f={};for(const d of l.data)d.serviceArn!==void 0&&d.taskDefinition!==void 0&&(f[d.serviceArn]=d.taskDefinition);return f}function x(s,t){return Object.keys(s).filter(a=>t[a]!==void 0&&t[a]!==s[a]).sort()}async function q(s){const{appName:t,connectionOutputsBefore:a,services:c,callbacks:e,abortSignal:r}=s,i=`Run \`fjall rollout ${t}\` to restart them manually.`,l=`Could not verify the database endpoint was unchanged by this deploy. If the database was replaced (e.g. restored from a snapshot), run \`fjall rollout ${t}\` so running services reconnect to it.`;if(a===void 0)return e.onLog?.(l,"warn"),{kind:"unverified"};const f=await I(c.cfnService,t);if(f===void 0)return e.onLog?.(l,"warn"),{kind:"unverified"};const d=x(a,f);if(d.length===0)return{kind:"not-needed"};if(e.onLog?.(`Database connection outputs changed this deploy (${d.join(", ")}) \u2014 the database was replaced.`,"info"),C(r))return e.onLog?.(`Deploy cancelled before services were restarted onto the new database endpoint. ${i}`,"warn"),{kind:"cancelled"};const k=await c.ecsResolver.getDeployableClusterAndServices(t,r);if(!k.success)return e.onLog?.(`Could not discover this application's ECS services to restart them onto the new database endpoint: ${h(k.error.message)}. ${i}`,"warn"),{kind:"failed"};const{clusterArn:m,serviceArns:$}=k.data;if(m===void 0||$.length===0)return e.onLog?.(`Database connection outputs changed but no running ECS services were found to restart. If this application has running services, run \`fjall rollout ${t}\` so they reconnect to the new database endpoint.`,"warn"),{kind:"not-needed"};const A=s.serviceTaskDefinitionsBefore,L=A!==void 0&&Object.keys(A).length>0,p=[];let D=0,S=0;const y=(n,o)=>{e.onLog?.(`Could not restart ${n} onto the new database endpoint: ${h(o)}. ${i}`,"warn"),S+=1};for(const n of $){if(C(r))return e.onLog?.(`Deploy cancelled before services were restarted onto the new database endpoint. ${i}`,"warn"),{kind:"cancelled"};const o=N(n)??n,u=await O({ecsService:c.ecsService},m,n,o,r);if(!u.success){y(o,u.error.message);continue}const v=A?.[n];if(L&&v===void 0){D+=1;continue}if(v!==void 0&&v!==u.data.previousTaskDefArn){D+=1;continue}p.push({serviceArn:n,serviceName:o,resolved:u.data})}if(p.length===0)return S>0?{kind:"failed"}:(e.onLog?.(`Services were already relaunched during this deploy \u2014 no restart needed. If something other than this deploy relaunched them, run \`fjall rollout ${t}\` to be safe.`,"info"),{kind:"already-current"});if(s.confirmRestart!==void 0){const n={appName:t,changedOutputs:d,serviceCount:p.length};let o;try{o=await s.confirmRestart(n)}catch(u){return e.onLog?.(`Restart confirmation failed: ${h(u instanceof Error?u.message:String(u))}. ${i}`,"warn"),{kind:"failed"}}if(!o)return e.onLog?.(`Restart declined \u2014 running containers are still connected to the old database endpoint. ${i}`,"warn"),{kind:"declined"}}e.onLog?.(`Restarting ${p.length} service${p.length===1?"":"s"} so running containers reconnect to the new database\u2026`,"info");const g=[];for(const n of p){if(C(r))return e.onLog?.(`Deploy cancelled before all services were restarted onto the new database endpoint. ${i}`,"warn"),{kind:"cancelled"};let o=n.resolved;if(s.confirmRestart!==void 0){const v=await O({ecsService:c.ecsService},m,n.serviceArn,n.serviceName,r);if(!v.success){y(n.serviceName,v.error.message);continue}if(v.data.previousTaskDefArn!==n.resolved.previousTaskDefArn){e.onLog?.(`${n.serviceName} was relaunched while awaiting confirmation \u2014 skipping its restart.`,"info");continue}o=v.data}const u=await E({ecsService:c.ecsService},{clusterArn:m,serviceArn:n.serviceArn,serviceName:n.serviceName,taskDefinition:o.taskDefinition,containerDefinitions:o.containerDefinitions,previousTaskDefArn:o.previousTaskDefArn,startedAtMs:Date.now()},e,r);if(!u.success){y(n.serviceName,u.error.message);continue}g.push(n.serviceName)}return g.length>0&&(w.debug(b,"Restarted services after database replacement",{appName:t,changedOutputs:d,services:g,alreadyRolled:D}),e.onLog?.(`Restarted ${g.length} service${g.length===1?"":"s"} onto the new database endpoint.`,"info")),S>0?{kind:"failed"}:{kind:"reconciled",services:g}}export{I as captureDatabaseConnectionOutputs,W as captureServiceTaskDefinitions,q as reconcileDatabaseEndpointChange};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Destroy pre-flight — ONE pure-read pass over EVERY stack about to be
|
|
3
|
+
* destroyed, BEFORE the first mutation. The destroy engine empties
|
|
4
|
+
* pre-empty-tagged S3 buckets before each CDK delete, so a delete-refusing
|
|
5
|
+
* setting (RDS DeletionProtection — fjall's own database constructs default
|
|
6
|
+
* it ON — or DynamoDB DeletionProtectionEnabled) discovered mid-run would
|
|
7
|
+
* leave the application half-destroyed: buckets emptied, stack DELETE_FAILED,
|
|
8
|
+
* raw CFN error surfaced. Probing first fails closed with the exact cure
|
|
9
|
+
* command while nothing has been touched.
|
|
10
|
+
*
|
|
11
|
+
* Fail-closed polarity (matching the recreate pre-flight, opposite of the
|
|
12
|
+
* drift preFlightGate): an unverifiable resource blocks the destroy, because
|
|
13
|
+
* proceeding risks partial data loss rather than a merely stale answer.
|
|
14
|
+
* Already-deleted stacks are the sanctioned exception — the destroy engine
|
|
15
|
+
* converts them to skipped, so a missing stack has nothing to block.
|
|
16
|
+
*
|
|
17
|
+
* Failure messages are pre-masked at construction (identifier-only content
|
|
18
|
+
* plus masked SDK error text).
|
|
19
|
+
*/
|
|
20
|
+
import { type Result } from "@fjall/generator";
|
|
21
|
+
import type { AwsProvider } from "../../aws/AwsProvider.js";
|
|
22
|
+
export interface RunDestroyPreFlightParams {
|
|
23
|
+
/** EVERY stack the destroy will touch — the pass must clear all of them
|
|
24
|
+
* before the engine mutates any. */
|
|
25
|
+
stackNames: readonly string[];
|
|
26
|
+
abortSignal?: AbortSignal;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Run the delete-block probe over every live resource (in every stack about
|
|
30
|
+
* to be destroyed) whose type carries a registry entry. Pure reads only —
|
|
31
|
+
* the first failure aborts the whole destroy before anything is mutated.
|
|
32
|
+
*/
|
|
33
|
+
export declare function runDestroyPreFlight(aws: AwsProvider, params: RunDestroyPreFlightParams): Promise<Result<void, Error>>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{CloudFormationClient as l}from"@aws-sdk/client-cloudformation";import{success as u,failure as n}from"@fjall/generator";import{getErrorMessage as f,maskSensitiveOutput as y}from"@fjall/util";import{logger as h}from"@fjall/util/logger";import{isAborted as g}from"../../aws/organisations/types.js";import{STACK_NOT_FOUND_PATTERN as p}from"../../types/constants.js";import{DELETE_BLOCK_PROBES as a}from"../probes/deleteBlockProbes.js";import{collectStackResources as m}from"../stackCleanup/stackResources.js";const b="DestroyPreFlight";async function S(s,r){const c=s.getClient(l);for(const t of r.stackNames){if(g(r.abortSignal))return n(new Error(`Destroy pre-flight aborted before ${t} was checked \u2014 nothing was destroyed in this run.`));let d;try{d=await m(c,t,e=>e.ResourceType!==void 0&&a[e.ResourceType]!==void 0&&e.ResourceStatus!=="DELETE_COMPLETE",e=>e.LogicalResourceId!==void 0&&e.PhysicalResourceId!==void 0&&e.PhysicalResourceId!==""&&e.ResourceType!==void 0?{logicalId:e.LogicalResourceId,physicalId:e.PhysicalResourceId,resourceType:e.ResourceType}:void 0,r.abortSignal)}catch(e){if(e instanceof Error&&e.message.includes(p)){h.debug(b,`Stack ${t} does not exist \u2014 nothing to probe`);continue}return n(new Error(`Destroy pre-flight for ${t}: could not list stack resources to verify deletability: ${y(f(e))}. Retry the destroy. Nothing was destroyed in this run.`))}for(const e of d){const o=a[e.resourceType];if(o===void 0)continue;const i=await o.probe(s,e.physicalId,r.abortSignal);if(!i.success)return n(new Error(`Destroy pre-flight for ${t}: could not verify that ${e.physicalId} (${e.logicalId}) is deletable \u2014 ${o.method} failed: ${i.error.message}. Retry the destroy. Nothing was destroyed in this run.`));if(i.data.blocked)return n(new Error(`Destroy pre-flight for ${t}: ${e.physicalId} (${e.logicalId}) has ${i.data.setting} enabled \u2014 CloudFormation cannot delete it, so the destroy would fail partway after other resources were already removed. Disable it on the live resource (e.g. '${o.disableCommand(e.physicalId)}'), then re-run the destroy. Nothing was destroyed in this run.`))}}return u(void 0)}export{S as runDestroyPreFlight};
|
|
@@ -11,4 +11,4 @@ export type { DetectionResult };
|
|
|
11
11
|
* Detects the application pattern, synthesises infrastructure, computes
|
|
12
12
|
* template hashes, and determines which stacks have changed.
|
|
13
13
|
*/
|
|
14
|
-
export declare function runDetectionPipeline(operation: ApplicationOperation, services: DeployServices, context: DeploymentContext, callbacks: DeployCallbacks): Promise<Result<DetectionResult>>;
|
|
14
|
+
export declare function runDetectionPipeline(operation: ApplicationOperation, services: DeployServices, context: DeploymentContext, callbacks: DeployCallbacks, abortSignal?: AbortSignal): Promise<Result<DetectionResult>>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{join as
|
|
1
|
+
import{join as $}from"path";import{success as N,failure as n}from"@fjall/generator";import{logger as F}from"@fjall/util/logger";import{maskSensitiveOutput as o}from"@fjall/util";import{deriveResourcesFromManifestStacks as H}from"../../types/detection/patternDetection.js";import{emitProgress as w,PROGRESS_MESSAGES as l}from"../../services/supporting/helpers.js";import{parseRequiredSecretsFromManifest as M,parseImportedSecretNamesFromManifest as O}from"../manifestSecretParser.js";import{resolveSecretArns as L}from"../secretArnResolver.js";import{isAborted as q}from"../../aws/organisations/types.js";import{parseDockerDeclarationsFromManifest as T}from"@fjall/util/manifest";async function z(c,r,a,e,S){e.onDetectionPhaseChange?.("detect","started");const C=r.frameworkRegistry.resolve({appPath:c.path}),y=C?.detection.pattern??null,R=C?.detection.hasDatabase??!1;e.onLog?.(`Pattern detected: ${y??"standard"}`,"info"),e.onDetectionPhaseChange?.("detect","completed");const i=$(c.path,"cdk.out");e.onDetectionPhaseChange?.("synth","started"),w(e,l.SYNTH);const D=await r.cdkService.runCdkSynth(a,t=>e.onCdkOutput?.(t,"synth"));if(!D.success)return n(new Error(o(`CDK synthesis failed: ${D.error}`)));if(e.onDetectionPhaseChange?.("synth","completed"),a.resolvedSecretArns===void 0){const t=O(i);if(t.length>0){e.onLog?.(`Resolving ${t.length} imported secret ARN(s)\u2026`,"info");const s=await L(r.awsProvider,t);if(!s.success)return n(new Error(o(s.error.message)));a.resolvedSecretArns=JSON.stringify(s.data);const g=await r.cdkService.runCdkSynth(a,E=>e.onCdkOutput?.(E,"synth"));if(!g.success)return n(new Error(o(`CDK synthesis failed: ${g.error}`)))}}const d=M(i);d.length>0&&e.onLog?.(`Found ${d.length} required secret path(s) in manifest`,"info"),e.onDetectionPhaseChange?.("hash","started"),w(e,l.HASH);const m=await r.hashService.getTemplateHashes(i);if(!m.success)return n(new Error(o(`Template hash computation failed: ${m.error.message}`)));const f=m.data,u=await r.hashService.compareWithState(f,c.path);if(!u.success)return n(new Error(o(`Hash comparison failed: ${u.error.message}`)));const h=u.data;e.onDetectionPhaseChange?.("hash","completed");const p=new Map(h.stackChanges);for(const[t,s]of h.stackChanges){if(q(S))break;s||await r.cfnService.stackExists(t,void 0,S)||(F.debug("detectionPipeline","Stale hash detected \u2014 stack missing in CFN",{stackName:t}),p.set(t,!0))}const k=Array.from(f.keys()),P=H(k),v=P.hasCompute&&T(i).declaresBuild,A=Array.from(p.values()).some(Boolean);return e.onLog?.(`Detection complete: ${h.changedCount} changed, ${h.unchangedCount} unchanged`,"info"),N({pattern:y,hasDatabase:R,hasDifferences:A,stackChanges:p,currentHashes:f,resources:P,hasDockerfile:v,requiredSecrets:d})}export{z as runDetectionPipeline};
|
|
@@ -1 +1 @@
|
|
|
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};
|
|
1
|
+
import{isAbsolute as H,join as K,dirname as z,resolve as _}from"node:path";import{existsSync as F}from"node:fs";import{success as N,failure as h}from"@fjall/generator";import{deriveContentHashTag as J,getErrorMessage as M,maskSensitiveOutput as b,toKebab as x}from"@fjall/util";import{evaluateBakeGuard as X,evaluateResolvedBuildArgValues as Y,acknowledgedBuildArgKeys as q,createBakeWarningDeduper as Q,buildCacheRepositoryName as U,BAKE_GUARD_EXPOSURE_CLAUSE as Z}from"@fjall/util/docker";import{runWithBoundedConcurrency as ee,resolveBuildConcurrency as re}from"@fjall/util/buildConcurrency";import{logger as T}from"@fjall/util/logger";import{parseDockerServicesFromManifest as te,parseLambdaDockerServicesFromManifest as ne}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 oe,resolveSecretRefValue as ie,resolveBuildSecretSessionProvider as se,sourcedRefsFromBuildArgs as ae}from"./buildSecretResolver.js";import{partitionForRegion as de}from"./buildSecretSession.js";import{resolveBuildArgs as ce}from"./buildArgResolver.js";import{validateBuildGroup as ue}from"./buildGroupValidator.js";import{ensureBuildCacheRepository as le}from"./ecrCacheRepository.js";const L="dockerBuildHelper",C="Building and pushing Docker image";function fe(e,n,t){const r=K(n,e);if(F(r))return{path:r,base:n};if(t!==""&&t!==n){const o=K(t,e);if(F(o))return{path:o,base:t}}return{path:r,base:n}}function ge(e,n,t){const r=e.docker.path,o=e.docker.context;let s,a;if(H(r))s=r,a=n;else{const m=fe(r,n,t);s=m.path,a=m.base}return{buildContext:o?H(o)?_(o):_(a,o):_(z(s)),dockerfilePath:s,target:e.docker.target,resolvedViaFallback:a!==n}}function me(e){return`${e.buildContext}|${e.dockerfilePath}|${e.target??""}`}function pe(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 he(e){return e.startsWith("cn-")?"amazonaws.com.cn":"amazonaws.com"}function V(e,n){return`${e}.dkr.ecr.${n}.${he(n)}`}function be(e,n,t){return`${V(e,n)}/${x(t)}`}function ye(e,n,t){return`${V(e,n)}/${U(x(t))}`}function ve(e,n,t){const r=new Map;for(const o of e){const s=ge(o,n,t),a=me(s),g=r.get(a);g?g.members.push(o):r.set(a,{identity:s,members:[o]})}return Array.from(r.values())}async function Se(e,n,t,r,o){const s=await e.buildAndPush({appName:n.appName,appPath:n.path,region:t,accountId:r},(a,g,m,S)=>{o.onDockerProgress?.(b(a),g,m,S)});return s.success?N({imageUri:s.data.imageUri,imageTag:s.data.imageTag}):h(s.error)}function j(){return{contentHashTagsByService:{},imageDigestsByService:{}}}async function we(e,n,t,r,o,s,a,g,m,S,E,f,k,w){const l=e.members[0];if(!F(e.identity.dockerfilePath))return h(new Error(pe(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 y=[...l.docker.buildSecrets??[],...k??[]],P=ue({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},...y.length>0&&{buildSecrets:y},members:e.members.map(i=>({serviceName:i.name,...i.docker.buildArgs!==void 0&&{buildArgs:i.docker.buildArgs},...i.docker.buildSecrets!==void 0&&{buildSecrets:i.docker.buildSecrets}}))});if(!P.success)return h(P.error);const d=X(l.docker.buildArgs,x(t.appName));for(const i of d.warnings){const v=b(i.message);w(v)&&g.onLog?.(v,"warn")}if(d.findings.length>0)return h(new Error(b(d.findings.map(i=>i.message).join(" "))));const c=y,D=ae(l.docker.buildArgs),I={appName:t.appName,region:r,accountId:o,partition:de(r)},$=await se(m,[...c,...D],I);if(!$.success)return h($.error);const O=$.data,R=await ce({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:(i,v)=>ie(O,{id:i,...v},S)});if(!R.success)return h(R.error);const B=R.data.buildArgs;for(const i of R.data.inferredDotenvKeys){const v=b(`buildArg "${i}" was auto-declared from a .env file and will be baked into the image \u2014 ${Z}.`);w(v)&&g.onLog?.(v,"warn")}const u=Y(B,x(t.appName),q(l.docker.buildArgs));if(u.length>0)return h(new Error(b(u.map(i=>i.message).join(" "))));let p;if(c.length>0){const i=await oe(O,c,S);if(!i.success)return h(i.error);p=i.data}try{if(G(S))return h(new Error(`Build of service '${l.name}' was aborted before the docker build started`));const i={appName:t.appName,appPath:e.identity.buildContext,region:r,accountId:o,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},...p!==void 0&&{buildSecrets:p.resolved}};return await Ee(e,i,n,s,g)}finally{p!==void 0&&await p.cleanup().catch(()=>g.onLog?.("Build-secret cleanup failed.","warn"))}}async function Ee(e,n,t,r,o){const s=await t.buildAndPush(n,(f,k,w,l)=>{o.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={},m={},S=[];for(const f of e.members){const k=f.docker.target,w=J(a,f.name,k);if(!w.success)return h(w.error);const l=w.data,y=f.name;g[y]=l,m[y]=a,S.push(`${r}:${l}`)}const E=await t.tagByDigest({sourceImage:r,digest:a,tags:S});return E.success?N({contentHashTagsByService:g,imageDigestsByService:m}):h(E.error)}function ke(e,n){const t=new Map(e.map(r=>[r.name,r]));for(const r of n){const o=t.get(r.name);if(o!==void 0&&JSON.stringify(o.docker)!==JSON.stringify(r.docker))throw new Error(`ECS service "${o.name}" and Lambda image key "${r.name}" share one build key but declare different docker configs \u2014 the second build would silently overwrite the first and both would deploy the same image. Rename one of them, or make the docker configs identical to deliberately share the build.`)}}async function Fe(e,n,t,r){const o=e.dockerProvider;if(!o)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(),g=K(t.path,"cdk.out");let m;try{const d=te(g),c=ne(g);ke(d,c),m=[...d,...c]}catch(d){const c=new Error(b(M(d)));return r.onError?.(c),h(c)}const S=be(s,a,t.appName);if(T.debug(L,"runDockerBuild starting",{appName:t.appName,appPath:t.path,accountId:s,region:a,cdkOutPath:g,manifestServiceCount:m.length,manifestServices:m.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),m.length===0){r.onLog?.("Initialising ECR repository\u2026","info");const d=await o.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 c=await Se(o,t,a,s,r);if(!c.success){T.debug(L,"Docker buildAndPush (legacy) failed",{appName:t.appName,error:b(c.error.message)}),r.onStepComplete?.(A.DOCKER_OPERATIONS,C,"error");const D=new Error(b(c.error.message));return r.onError?.(D),h(D)}return r.onStepComplete?.(A.DOCKER_OPERATIONS,C,"completed"),r.onLog?.(`Docker image pushed: ${c.data.imageUri}`,"info"),N(j())}const E=await o.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(m,t.path,e.workingDirectory);let k=ye(s,a,t.appName);const w=await le(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 ${m.length} service image(s) in ${f.length} group(s)\u2026`,"info");let l=!1;if(o.beginEcrSession!==void 0){const d=await o.beginEcrSession({accountId:s,region:a});if(!d.success){r.onStepComplete?.(A.DOCKER_OPERATIONS,C,"error");const c=new Error(b(d.error.message));return r.onError?.(c),h(c)}l=!0}const y=new AbortController,P=()=>y.abort();e.abortSignal!==void 0&&(e.abortSignal.aborted?y.abort():e.abortSignal.addEventListener("abort",P,{once:!0}));try{const d=()=>G(e.abortSignal)?"the deployment was cancelled":"a sibling group failed";let c;const D=re(f.length),I=Q(),$=f.map((u,p)=>async()=>{const i=u.members.map(W=>W.name).join(", ");if(y.signal.aborted)throw new Error(`Build of group ${p+1}/${f.length} (${i}) was not started \u2014 ${d()}`);r.onLog?.(`Building group ${p+1}/${f.length} (${i})\u2026`,"info");const v=await we(u,o,t,a,s,S,k,r,n.awsProvider,y.signal,e.options?.environment,e.options?.flagBuildArgs,e.options?.flagBuildSecrets,I);if(!v.success)throw c===void 0&&(c={error:v.error,leader:u.members[0]?.name,members:i}),y.abort(),v.error;return v.data}),O=await ee($,D,{abortOnFirstFailure:!0,abortSignal:y.signal});if(c===void 0)for(let u=0;u<f.length;u++){const p=O[u];if(!(p!==void 0&&p.success)){c={error:p!==void 0&&!p.success?p.error:new Error(`Build of group ${u+1}/${f.length} never ran \u2014 ${d()}`),leader:f[u].members[0]?.name,members:f[u].members.map(i=>i.name).join(", ")};break}}if(c!==void 0){T.debug(L,"Docker buildAndPush failed",{appName:t.appName,leader:c.leader,members:c.members,error:b(c.error.message)}),r.onStepComplete?.(A.DOCKER_OPERATIONS,C,"error");const u=new Error(b(c.error.message));return r.onError?.(u),h(u)}const R={},B={};for(const u of O)if(!(u===void 0||!u.success)){for(const[p,i]of Object.entries(u.data.contentHashTagsByService))R[p]=i;for(const[p,i]of Object.entries(u.data.imageDigestsByService))B[p]=i}return T.debug(L,"Docker buildAndPush succeeded",{appName:t.appName,services:m.map(u=>u.name),groupCount:f.length,contentHashTags:R}),r.onStepComplete?.(A.DOCKER_OPERATIONS,C,"completed"),r.onLog?.(`Docker images pushed for ${m.length} service(s)`,"info"),N({contentHashTagsByService:R,imageDigestsByService:B,repoUri:S})}finally{e.abortSignal?.removeEventListener("abort",P),l&&o.endEcrSession!==void 0&&await o.endEcrSession().catch(d=>{T.debug(L,"endEcrSession cleanup failed",{error:b(M(d))})})}}export{C as DOCKER_BUILD_STEP_NAME,be as ecrRepoUri,j as emptyBuildProducts,Fe as runDockerBuild};
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import { type Result } from "@fjall/generator";
|
|
1
2
|
import { type ServiceArtefact } from "@fjall/util";
|
|
3
|
+
import { type ManifestLambdaDockerEntry } from "@fjall/util/manifest";
|
|
2
4
|
import type { DeployParams } from "../../types/params.js";
|
|
3
5
|
import type { ApplicationOperation } from "../../types/operations.js";
|
|
4
6
|
import type { DeployServices } from "../serviceFactory.js";
|
|
@@ -26,6 +28,17 @@ export interface LambdaRolloutOutcome {
|
|
|
26
28
|
imageTag: string;
|
|
27
29
|
}>;
|
|
28
30
|
}
|
|
31
|
+
/**
|
|
32
|
+
* Exact case-insensitive match on a manifest Lambda function name. Deliberately
|
|
33
|
+
* NOT `matchesServiceName` (serviceImageTagsRollback.ts): its `endsWith` suffix
|
|
34
|
+
* fallback exists for ECS live-ARN names that carry cluster prefixes, but a
|
|
35
|
+
* manifest Lambda `name` IS the exact `FunctionName` (see the module JSDoc
|
|
36
|
+
* above), so suffix matching would let `--service order` silently roll
|
|
37
|
+
* `processOrder`. Callers in `codeOnlyDeploy.ts` (the not-found gate) must use
|
|
38
|
+
* this same matcher — an exact filter behind a fuzzy gate turns a typo into a
|
|
39
|
+
* silent zero-artefact no-op.
|
|
40
|
+
*/
|
|
41
|
+
export declare function matchesLambdaFunctionName(manifestName: string, candidate: string): boolean;
|
|
29
42
|
/**
|
|
30
43
|
* Primary path: roll every container Lambda whose `imageKey` was rebuilt
|
|
31
44
|
* this cycle. Builds a digest-pinned `ImageUri` directly from
|
|
@@ -44,15 +57,34 @@ export declare function rolloutLambdaPrimary(services: DeployServices, operation
|
|
|
44
57
|
*/
|
|
45
58
|
export declare function rolloutLambdaImageTag(services: DeployServices, operation: ApplicationOperation, explicitRollbackTag: string, allFamilyNames: readonly string[], callbacks: DeployParams["callbacks"], targetService: string | undefined, abortSignal?: AbortSignal): Promise<LambdaRolloutOutcome>;
|
|
46
59
|
/**
|
|
47
|
-
* `serviceImageTags`
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
|
|
60
|
+
* Everything the `serviceImageTags` execute phase needs, resolved BEFORE any
|
|
61
|
+
* mutation on either entity type. `repoUri` is "" when nothing matched (no
|
|
62
|
+
* account/region lookup was needed).
|
|
63
|
+
*/
|
|
64
|
+
export interface LambdaServiceImageTagsPreflight {
|
|
65
|
+
matched: Array<{
|
|
66
|
+
entry: ManifestLambdaDockerEntry;
|
|
67
|
+
tag: string;
|
|
68
|
+
}>;
|
|
69
|
+
digestsByFunctionName: Map<string, string>;
|
|
70
|
+
repoUri: string;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* `serviceImageTags` map rollback, preflight half: exact-name match against
|
|
74
|
+
* manifest Lambda entries (simpler than ECS's ARN-suffix matching — Lambda
|
|
75
|
+
* function names are deterministic and exact, no live-ARN fuzziness to
|
|
76
|
+
* resolve), then resolve every matched entry's digest, mirroring
|
|
51
77
|
* `preflightResolveServiceImageTagDigests`'s atomic-fail-closed behaviour: a
|
|
52
|
-
* single bad tag
|
|
53
|
-
*
|
|
78
|
+
* single bad tag fails the whole rollback. Split from the execute half so
|
|
79
|
+
* `deployCodeOnly` can run BOTH namespaces' preflights before mutating
|
|
80
|
+
* EITHER — a bad Lambda tag must abort while ECS is still untouched, and
|
|
81
|
+
* vice versa.
|
|
82
|
+
*/
|
|
83
|
+
export declare function preflightLambdaServiceImageTags(services: DeployServices, operation: ApplicationOperation, serviceImageTags: Record<string, string>, abortSignal?: AbortSignal): Promise<Result<LambdaServiceImageTagsPreflight, Error>>;
|
|
84
|
+
/**
|
|
85
|
+
* `serviceImageTags` map rollback, execute half: roll every preflighted
|
|
86
|
+
* function to its digest-pinned image. Never fails fatally — per-function
|
|
87
|
+
* failures land in `outcome.failures`, matching the ECS roll loop's
|
|
88
|
+
* continue-on-failure semantics.
|
|
54
89
|
*/
|
|
55
|
-
export declare function
|
|
56
|
-
outcome: LambdaRolloutOutcome;
|
|
57
|
-
error?: Error;
|
|
58
|
-
}>;
|
|
90
|
+
export declare function executeLambdaServiceImageTagsRollout(services: DeployServices, preflight: LambdaServiceImageTagsPreflight, callbacks: DeployParams["callbacks"], abortSignal?: AbortSignal): Promise<LambdaRolloutOutcome>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{join as
|
|
1
|
+
import{join as h}from"path";import{success as R,failure as L}from"@fjall/generator";import{artefactOutputKey as y,maskSensitiveOutput as w}from"@fjall/util";import{parseLambdaDockerEntriesFromManifest as I}from"@fjall/util/manifest";import{isAborted as A}from"../../aws/organisations/types.js";import{ecrRepoUri as P}from"./dockerBuildHelper.js";import{rebaseRollbackTagForService as F,deriveEcrRepositoryArn as K}from"./codeOnlyDeploy.js";function $(){return{outputs:{},artefacts:[],failures:[],rolledImageKeys:[]}}function b(e,t,n){return A(n)?(e.failures.push(`${t}: rollout cancelled before this function was reached`),!0):!1}function N(e,t,n,a,i,c,r){const o=K(i),s=`${i}:${a}`;e.outputs[y(t,"ImageTag")]=a,e.outputs[y(t,"ImageUri")]=s,e.outputs[y(t,"ImageDigest")]=c,o!==""&&(e.outputs[y(t,"EcrRepositoryArn")]=o),e.artefacts.push({serviceName:t,imageTag:a,imageUri:s,imageDigest:c,...o!==""?{ecrRepositoryArn:o}:{},functionArn:r.functionArn,publishedVersion:r.version}),e.rolledImageKeys.some(f=>f.serviceName===n)||e.rolledImageKeys.push({serviceName:n,imageTag:a})}async function v(e,t,n,a,i,c,r,o){r.onLog?.(`Updating Lambda function ${n.name} to ${c}`,"info");const s=await e.lambdaService.updateFunctionCode(n.name,`${a}@${i}`,o);if(!s.success){t.failures.push(`${n.name}: ${w(s.error.message)}`);return}s.data.unverifiedReason!==void 0&&r.onLog?.(`Updated ${n.name}, but completion of the update could not be verified: ${s.data.unverifiedReason}`,"warn"),N(t,n.name,n.imageKey,c,a,i,s.data)}function O(e,t){return e.toLowerCase()===t.toLowerCase()}function k(e,t){return t?e.filter(n=>O(n.name,t)):e}async function j(e,t,n,a,i,c,r,o){const s=$(),f=h(t.path,"cdk.out"),p=k(I(f),r);for(const u of p){if(b(s,u.name,o))continue;const m=n[u.imageKey],d=a[u.imageKey];m===void 0||d===void 0||await v(e,s,u,i,d,m,c,o)}return s}async function V(e,t,n,a,i,c,r){const o=$(),s=h(t.path,"cdk.out"),f=k(I(s),c);if(f.length===0)return o;const p=e.awsProvider.getAccountId(),u=e.awsProvider.getRegion(),m=P(p,u,t.appName);for(const d of f){if(b(o,d.name,r))continue;const g=F(n,d.imageKey,a),l=await e.ecrImageInspector.getImageDigest(m,g,r);if(!l.success||l.data===void 0){const E=l.success?"not an ECR repository":w(l.error.message);o.failures.push(`${d.name}: image tag '${g}' (rebased from '${n}') could not be resolved to a digest: ${E}`);continue}await v(e,o,d,m,l.data,g,i,r)}return o}async function q(e,t,n,a){const i=h(t.path,"cdk.out"),r=I(i).map(u=>({entry:u,tag:n[u.name]})).filter(u=>u.tag!==void 0);if(r.length===0)return R({matched:r,digestsByFunctionName:new Map,repoUri:""});const o=e.awsProvider.getAccountId(),s=e.awsProvider.getRegion(),f=P(o,s,t.appName),p=new Map;for(const{entry:u,tag:m}of r){if(A(a))return L(new Error("Pre-flight cancelled. No function was rolled."));const d=await e.ecrImageInspector.getImageDigest(f,m,a);if(!d.success||d.data===void 0){const g=d.success?"not an ECR repository":w(d.error.message);return L(new Error(`Lambda function '${u.name}': image tag '${m}' could not be resolved to a digest: ${g}. No function was rolled.`))}p.set(u.name,d.data)}return R({matched:r,digestsByFunctionName:p,repoUri:f})}async function z(e,t,n,a){const i=$();for(const{entry:c,tag:r}of t.matched){if(b(i,c.name,a))continue;const o=t.digestsByFunctionName.get(c.name);o!==void 0&&await v(e,i,c,t.repoUri,o,r,n,a)}return i}export{z as executeLambdaServiceImageTagsRollout,O as matchesLambdaFunctionName,q as preflightLambdaServiceImageTags,V as rolloutLambdaImageTag,j as rolloutLambdaPrimary};
|
|
@@ -11,7 +11,7 @@ import type { ChangeSetProbeRunner } from "../../../services/infrastructure/chan
|
|
|
11
11
|
import type { DeployPlan } from "./types.js";
|
|
12
12
|
/** The narrow CFN read surface the gate needs — CloudFormationService satisfies it. */
|
|
13
13
|
export interface CfnTemplateReader {
|
|
14
|
-
getTemplate(stackName: string): Promise<Result<string, CloudFormationError>>;
|
|
14
|
+
getTemplate(stackName: string, abortSignal?: AbortSignal): Promise<Result<string, CloudFormationError>>;
|
|
15
15
|
}
|
|
16
16
|
export interface ComputeDeployPlanStack {
|
|
17
17
|
stackName: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{success as
|
|
1
|
+
import{success as l,failure as n}from"@fjall/generator";import{maskSensitiveOutput as c}from"@fjall/util";import{isAborted as f}from"../../../aws/organisations/types.js";import{buildDeployPlan as u}from"./buildDeployPlan.js";import{applyChangeSetEscalation as p}from"./changeSetEscalation.js";import{applyRegistryOverlay as d}from"./registryOverlay.js";function g(e){const r=e.trim();if(r==="")return l({});try{return l(JSON.parse(r))}catch(t){return n(new Error(`Failed to parse deployed template: ${c(t instanceof Error?t.message:String(t))}`))}}async function T(e){const r=[];for(const i of e.stacks){if(f(e.abortSignal))return n(new Error("Deploy-plan computation aborted"));const o=await e.cfnService.getTemplate(i.stackName,e.abortSignal);let s={};if(o.success){const a=g(o.data);if(!a.success)return n(a.error);s=a.data}else if(o.error.errorType!=="stack_not_found")return n(o.error);r.push({stackName:i.stackName,oldTemplate:s,newTemplate:i.newTemplate})}let t=u(r,e.assemblyDigest);return e.registry!==void 0&&(t=await d({plan:t,registry:e.registry,stacks:r,...e.abortSignal!==void 0?{abortSignal:e.abortSignal}:{}})),e.changeSetProbe!==void 0&&(t=await p({plan:t,probe:e.changeSetProbe,stacks:r,...e.abortSignal!==void 0?{abortSignal:e.abortSignal}:{}})),l(t)}export{T as computeDeployPlan};
|
|
@@ -41,7 +41,7 @@ export interface ResolvedServiceTaskDefinition {
|
|
|
41
41
|
* resolution (map mode) and the rollout itself (legacy `--image-tag` mode) so
|
|
42
42
|
* the describe → task-definition → repo-URI derivation lives in one place.
|
|
43
43
|
*/
|
|
44
|
-
export declare function resolveServiceTaskDefinition(services: Pick<DeployServices, "ecsService">, clusterArn: string, serviceArn: string, serviceName: string): Promise<Result<ResolvedServiceTaskDefinition, Error>>;
|
|
44
|
+
export declare function resolveServiceTaskDefinition(services: Pick<DeployServices, "ecsService">, clusterArn: string, serviceArn: string, serviceName: string, abortSignal?: AbortSignal): Promise<Result<ResolvedServiceTaskDefinition, Error>>;
|
|
45
45
|
export interface PreResolvedRollout {
|
|
46
46
|
taskDefinition: ResolvedServiceTaskDefinition;
|
|
47
47
|
imageDigest: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{success as h,failure as
|
|
1
|
+
import{success as h,failure as m}from"@fjall/generator";import{maskSensitiveOutput as w}from"@fjall/util";import{extractEcsServiceName as y}from"../../aws/utils/arnParser.js";import{isAborted as E}from"../../aws/organisations/types.js";import{stripTagOrDigest as $}from"../../services/infrastructure/EcrImageInspectorService.js";function k(a,g){const o=a.toLowerCase(),r=g.toLowerCase();return o===r||o.endsWith(r)}function L(a,g){const o=Object.entries(g),r=[],f=[],n=new Set,s=a.map(e=>{const t=y(e)??e;return{serviceArn:e,serviceName:t,lowerName:t.toLowerCase()}}),i=new Map,d=new Set;for(const e of s){const t=o.find(([c])=>c.toLowerCase()===e.lowerName);t!==void 0&&(i.set(e.serviceArn,t),d.add(t[0]))}const u=new Map,v=new Map;for(const e of s){if(i.has(e.serviceArn))continue;const t=o.filter(([c])=>!d.has(c)&&k(e.serviceName,c)).sort(([c],[p])=>p.length-c.length)[0];t!==void 0&&(u.set(e.serviceArn,t),v.set(t[0],(v.get(t[0])??0)+1))}for(const e of s){const t=u.get(e.serviceArn),c=i.get(e.serviceArn)??(t!==void 0&&v.get(t[0])===1?t:void 0);if(c===void 0){f.push(e.serviceName);continue}const[p,D]=c;n.add(p),r.push({serviceArn:e.serviceArn,serviceName:e.serviceName,tag:D,mapKey:p})}const l=o.map(([e])=>e).filter(e=>!n.has(e));return{matched:r,unmatchedLiveServiceNames:f,staleMapKeys:l}}async function N(a,g,o,r,f){const n=await a.ecsService.describeServices(g,[o],f);if(!n.success)return m(new Error(`Failed to describe service ${r}: ${w(n.error.message)}`));const s=n.data[0];if(!s?.taskDefinition)return m(new Error(`Service ${r} has no task definition`));const i=s.taskDefinition,d=await a.ecsService.getLatestTaskDefinition(i,f);if(!d.success)return m(new Error(`Failed to fetch task definition for ${r}: ${w(d.error.message)}`));const u=d.data;if(!u||!u.containerDefinitions||u.containerDefinitions.length===0)return m(new Error(`Task definition for ${r} has no container definitions`));const v=u.containerDefinitions,l=v.find(t=>t.image)?.image;if(!l)return m(new Error(`Task definition for ${r} has no container image \u2014 cannot derive repository URI`));const e=$(l);return h({previousTaskDefArn:i,taskDefinition:u,containerDefinitions:v,repoUri:e})}async function I(a,g,o,r){const f=new Map;for(const n of o){if(E(r))return m(new Error("Pre-flight cancelled. No service was rolled."));const s=await N(a,g,n.serviceArn,n.serviceName,r);if(!s.success)return m(new Error(`Pre-flight failed resolving the current task definition for ${n.serviceName} (tag ${n.tag}): ${w(s.error.message)}. No service was rolled.`));const i=await a.ecrImageInspector.getImageDigest(s.data.repoUri,n.tag,r);if(!i.success||i.data===void 0){const d=i.success?"ECR returned no digest for this tag (non-ECR repository, or the image is missing)":w(i.error.message);return m(new Error(`Pre-flight digest resolution failed for ${n.serviceName} (tag ${n.tag}): ${d}. No service was rolled.`))}f.set(n.serviceArn,{taskDefinition:s.data,imageDigest:i.data})}return h(f)}export{L as matchServiceImageTagsToLiveServices,k as matchesServiceName,I as preflightResolveServiceImageTagDigests,N as resolveServiceTaskDefinition};
|
|
@@ -29,5 +29,5 @@ export interface RollTaskDefinitionOk {
|
|
|
29
29
|
* task-definition field omitted here is silently dropped from the new
|
|
30
30
|
* revision on BOTH paths, so it must have exactly one home.
|
|
31
31
|
*/
|
|
32
|
-
export declare function registerAndRollTaskDefinition(services: Pick<DeployServices, "ecsService">, input: RollTaskDefinitionInput, callbacks: DeployCallbacks): Promise<Result<RollTaskDefinitionOk, Error>>;
|
|
32
|
+
export declare function registerAndRollTaskDefinition(services: Pick<DeployServices, "ecsService">, input: RollTaskDefinitionInput, callbacks: DeployCallbacks, abortSignal?: AbortSignal): Promise<Result<RollTaskDefinitionOk, Error>>;
|
|
33
33
|
export declare function extractRevision(taskDefArn: string): number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{success as
|
|
1
|
+
import{success as S,failure as l}from"@fjall/generator";import{logger as $}from"@fjall/util/logger";import{maskSensitiveOutput as p}from"@fjall/util";const y="taskDefinitionRoll";async function O(n,a,r,g){const{clusterArn:f,serviceArn:s,serviceName:t,taskDefinition:e,containerDefinitions:R,previousTaskDefArn:C,imageDigest:D,startedAtMs:v}=a,m=e.family;if(!m)return l(new Error(`Task definition for ${t} has no family`));const d=await n.ecsService.registerTaskDefinition({family:m,taskRoleArn:e.taskRoleArn,executionRoleArn:e.executionRoleArn,networkMode:e.networkMode,containerDefinitions:R,volumes:e.volumes,placementConstraints:e.placementConstraints,requiresCompatibilities:e.requiresCompatibilities,cpu:e.cpu,memory:e.memory,runtimePlatform:e.runtimePlatform,proxyConfiguration:e.proxyConfiguration,inferenceAccelerators:e.inferenceAccelerators,pidMode:e.pidMode,ipcMode:e.ipcMode,ephemeralStorage:e.ephemeralStorage},g);if(!d.success)return l(new Error(`Failed to register task definition for ${t}: ${d.error.message}`));const i=d.data;r.onTaskDefRegistered?.({serviceName:t,taskDefinitionArn:i,previousTaskDefinitionArn:C,revision:E(i),family:m,...D!==void 0?{imageDigest:D}:{}});const k=await n.ecsService.updateService(f,s,{taskDefinition:i,forceNewDeployment:!0},g);if(!k.success)return l(new Error(`Failed to update service ${t}: ${k.error.message}`));const A=await n.ecsService.pollDeployment({clusterArn:f,serviceArn:s,waitForCompletion:!0,progressCallback:o=>{const w=o.latestEvent?` \u2014 ${p(o.latestEvent)}`:"";r.onECSProgress?.(`${t}: ${p(o.message)}${w}`,o.percentComplete)}}),c=Date.now()-v;if(!A.success){const o=p(A.error.message);return r.onECSComplete?.({serviceName:t,serviceArn:s,success:!1,taskDefinitionArn:i,durationMs:c,reason:o}),l(new Error(`ECS rollout failed for ${t}: ${o}`))}const u=await n.ecsService.getDeploymentStatus(f,s);return r.onECSComplete?.({serviceName:t,serviceArn:s,success:!0,taskDefinitionArn:i,durationMs:c,finalRunningCount:u.success?u.data.runningCount:void 0,finalDesiredCount:u.success?u.data.desiredCount:void 0}),$.debug(y,`Rolled ${t} successfully`,{serviceArn:s,newTaskDefArn:i,previousTaskDefArn:C,durationMs:c}),S({newTaskDefArn:i,durationMs:c})}function E(n){const a=n.lastIndexOf(":");if(a<0)return 0;const r=n.slice(a+1);return/^\d+$/.test(r)?parseInt(r,10):0}export{E as extractRevision,O as registerAndRollTaskDefinition};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{success as
|
|
1
|
+
import{success as a,failure as l}from"@fjall/generator";import{isApplicationOperation as g,isOrganisationOperation as f,getApplicationStackName as p,getOrganisationStackName as y,APPLICATION_STACKS as b}from"../types/operations.js";import{isAborted as u}from"../aws/organisations/types.js";import{createDeployServices as k}from"./serviceFactory.js";import{resolveOperation as w}from"./resolveOperation.js";import{destroyApplication as S}from"./application/applicationDestroy.js";import{destroyOrganisation as D}from"./organisation/organisationDestroy.js";import{checkActiveDeployments as v}from"./activeDeploymentGuard.js";async function I(t){const i=Date.now(),n=k({target:t.target,workingDirectory:t.workingDirectory,awsCredentials:t.awsCredentials,callbacks:t.callbacks,options:{verbose:t.options?.verbose,force:t.options?.force,cascade:t.options?.cascade,skipConfirmation:t.options?.skipConfirmation},orgConfig:t.orgConfig,identity:t.identity,...t.abortSignal!==void 0?{abortSignal:t.abortSignal}:{}}),r=await w(t.target,t.workingDirectory);if(!r.success)return t.callbacks.onError?.(r.error),r;const e=r.data;if(!t.options?.force){const o=g(e)?Object.values(b).map(d=>p(e.appName,d)):[y(e.type)],c=await v(o,n.cfnService,t.abortSignal);if(!c.success)return t.callbacks.onError?.(c.error),l(c.error)}if(g(e)){const o=await S(t,n,e);return u(t.abortSignal)?a({target:t.target,deploymentType:"application",cancelled:!0,durationMs:Date.now()-i}):o.success&&o.data.durationMs===void 0?a({...o.data,durationMs:Date.now()-i}):o}if(f(e)){const o=await D(t,n,e);return u(t.abortSignal)?a({target:t.target,deploymentType:"organisation",cancelled:!0,durationMs:Date.now()-i}):o}const s=new Error(`Unsupported destroy target: ${t.target}`);return t.callbacks.onError?.(s),l(s)}export{I as destroy};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import{success as g}from"@fjall/generator";import{detectStackDrift as $}from"./detectStackDrift.js";import{readDriftSuspects as v}from"./driftSuspectJournal.js";const y=new Set(["UPDATE_ROLLBACK_COMPLETE","UPDATE_ROLLBACK_FAILED","UPDATE_FAILED"]),b=9e4;async function D(
|
|
2
|
-
`:`Deploy blocked: CloudFormation still tracks ${
|
|
3
|
-
`,p=t?"Retry the deploy \u2014 verification is transient when the live template read fails; if it keeps failing, check CloudFormation access to the stack template.":"Run 'fjall drift detect' for the full report, then 'fjall drift repair --remediate <physicalName>=<verb>' to remediate confirmed deletions.";return m+`${
|
|
1
|
+
import{success as g}from"@fjall/generator";import{detectStackDrift as $}from"./detectStackDrift.js";import{readDriftSuspects as v}from"./driftSuspectJournal.js";const y=new Set(["UPDATE_ROLLBACK_COMPLETE","UPDATE_ROLLBACK_FAILED","UPDATE_FAILED"]),b=9e4;async function D(i,l){const n=l.budgetMs??b,c=l.abortSignal!==void 0?AbortSignal.any([l.abortSignal,AbortSignal.timeout(n)]):AbortSignal.any([AbortSignal.timeout(n)]),r={confirmed:[],unconfirmed:[],probedStacks:[]},u=await Promise.all(l.stackNames.map(async o=>{const a=await l.cfnService.getStackStatus(o,void 0,c);return{stackName:o,status:a.success?a.data?.status??null:null}}));for(const{stackName:o,status:a}of u){if(!(a!==null&&y.has(a))){const t=await v(i.aws.getAccountId(),i.aws.getRegion(),o,i.journal);if(t===null||t.suspects.length===0)continue}r.probedStacks.push(o);const d=await $(i,{stackName:o,abortSignal:c});if(d.success)for(const t of d.data.findings)t.verdict==="confirmed-deleted"?r.confirmed.push(t):(t.verdict==="unconfirmed"||t.verdict==="stack-missing")&&r.unconfirmed.push(t)}return g(r)}function I(i,l=[],n={}){const c=i.map(e=>` ${e.stackName} \u203A ${e.logicalId} (${e.resourceType})${e.physicalId!==void 0?` [${e.physicalId}]`:""} \u2014 ${e.verdictDetail}`),r=l.map(e=>`Remediation ${e} is in progress \u2014 run 'fjall drift resume ${e}' to continue it.`),u=(n.flipNotLive??[]).map(({finding:e,opId:s})=>`Remediation ${s} recorded its Retain flip on ${e.stackName} \u203A ${e.logicalId}, but the flip is no longer live on the stack \u2014 re-run 'fjall drift repair' to re-verify before deploying.`),o=(n.flipUnverified??[]).map(({finding:e,opId:s,message:h})=>`Remediation ${s} should complete with this deploy, but ${e.stackName} \u203A ${e.logicalId} could not be verified: the live template could not be read to confirm the Retain flip is still live \u2014 ${h}. Retry the deploy.`),a=(n.stillDeclared??[]).map(({finding:e,opId:s})=>`Remediation ${s} is ready to converge, but ${e.stackName} \u203A ${e.logicalId} is still declared in your fjall config \u2014 remove the resource from the config, then deploy to complete the forget.`),f=[...r,...u,...o,...a],d=new Set((n.flipUnverified??[]).map(({finding:e})=>`${e.stackName}/${e.logicalId}`)),t=i.length>0&&i.every(e=>d.has(`${e.stackName}/${e.logicalId}`)),m=t?`Deploy blocked: the Retain-flip state of ${i.length} mid-remediation resource(s) could not be verified (the live template could not be read):
|
|
2
|
+
`:`Deploy blocked: CloudFormation still tracks ${i.length} resource(s) deleted outside CloudFormation \u2014 deploying would fail and re-wedge the stack:
|
|
3
|
+
`,p=t?"Retry the deploy \u2014 verification is transient when the live template read fails; if it keeps failing, check CloudFormation access to the stack template.":"Run 'fjall drift detect' for the full report, then 'fjall drift repair --remediate <physicalName>=<verb>' to remediate confirmed deletions.";return m+`${c.join(`
|
|
4
4
|
`)}
|
|
5
5
|
${p}${f.length>0?`
|
|
6
6
|
${f.join(`
|
|
@@ -62,7 +62,7 @@ export { applyRetainFlip, composeFlipCapabilities, computeTemplateDigest, derive
|
|
|
62
62
|
export { assessForgetResumability, completeForgetAfterConverge, completeForgetAfterDeploy, formatRecreateInFlightCures, partitionPreFlightByRemediation, runForgetSurgery, synthTemplateDeclaresResource, type CompleteForgetAfterDeployParams, type CompleteForgetOutcome, type CompleteForgetParams, type ForeignMarkerOperation, type ForgetOrchestrationOptions, type ForgetResumeAssessment, type ForgetResumeSource, type ForgetSurgeryParams, type ForgetSurgeryReport, type PreFlightRemediationDeps, type PreFlightRemediationPartition } from "./remediation/forgetResource.js";
|
|
63
63
|
export { runRecreateSurgery, snapshotPolicyForType, SNAPSHOT_CAPABLE_RESOURCE_TYPES, type RecreateOrchestrationOptions, type RecreateSurgeryParams, type RecreateSurgeryReport } from "./remediation/recreateResource.js";
|
|
64
64
|
export { runRecreatePreFlight, type RecreatePreFlightStack, type RunRecreatePreFlightParams } from "./remediation/recreatePreFlight.js";
|
|
65
|
-
export { deriveRemovalTemplate, findRemovalDeletionFailures, proveRemovalTargetsOnly, type ProveRemovalTargetsOnlyParams, type RemovalDeletionFailure, type RemovalDerivation, type RemovalProofResult, type StrippedExport } from "./remediation/removalUpdate.js";
|
|
65
|
+
export { deriveRemovalTemplate, findRemovalDeletionFailures, findResourcesPresent, proveRemovalTargetsOnly, type CoRemovedResource, type ProveRemovalTargetsOnlyParams, type RemovalDeletionFailure, type RemovalDerivation, type RemovalProofResult, type StrippedExport } from "./remediation/removalUpdate.js";
|
|
66
66
|
export { advertisableDriftVerbs, buildDriftRepairTicket, runDriftRepairGate, type DriftRepairGateOutcome, type RunDriftRepairGateParams } from "./remediation/driftRepairGate.js";
|
|
67
67
|
export { formatPinOutcomeDetail, pinProbeCoverage, renderPinReportLines, runPinRemediation, type PinnedProperty, type PinProbeCoverage, type PinRemediationReport, type PinReportOnlyReason, type PinResourceOutcome, type PinTarget, type RunPinRemediationParams } from "./remediation/pinRemediation.js";
|
|
68
68
|
export { composePostFailureRepairOffer, formatDriftRepairOffer, DriftRepairAvailableError, type ComposePostFailureRepairOfferParams } from "./remediation/postFailureRepairOffer.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{deploy as t}from"./deploy.js";import{destroy as i}from"./destroy.js";import{restart as n,probeSecretsDrift as s}from"./restart/restartApplication.js";import{computeSecretsDrift as p,deriveStaleParameters as c,extractConsumedSsmParameters as m,ssmParameterNameFromValueFrom as u}from"./restart/secretsDrift.js";import{deployOrganisation as d}from"./organisation/organisationDeploy.js";import{destroyOrganisation as T}from"./organisation/organisationDestroy.js";import{cleanupFailedStack as x,emptyS3Bucket as P,preEmptyStackBuckets as A,formatQuarantineSuspectedMessage as E,formatRetainedBucketsMessage as D,isQuarantineDetail as y,isRetainedBucketsDetail as _,PRE_EMPTY_TAG_KEYS as g,isCleanableState as O,SAFE_CLEANUP_STATES as F}from"./stackCleanup.js";import{partitionAccounts as k,buildRegionList as B,buildAccountRegionPairs as b,cascadeHomeRegion as C,cascadeOperationKey as L}from"./organisation/cascadeHelpers.js";import{projectScalarSummary as I,projectAccountRows as h}from"./organisation/cascadeSummary.js";import{reconcileProviderAccounts as G,mergeReconciledProviderAccounts as K}from"./organisation/reconcileProviderAccounts.js";import{decideNextTransition as Y,reconcileTrailMigration as V,ORG_TRAIL_BUCKET_OUTPUT_KEY as J,TRAIL_BUCKET_OUTPUT_KEY as H,TRAIL_KEY_ARN_OUTPUT_KEY as Q}from"./trailMigration/trailMigration.js";import{decommissionMemberTrailStorage as j}from"./trailMigration/memberTrailCleanup.js";import{unlockBucket as q}from"./unlock/unlockBucket.js";import{unlockQueue as X}from"./unlock/unlockQueue.js";import{triageBucketPolicy as $,isEnforceSslStatement as ee}from"./unlock/bucketPolicyTriage.js";import{toResourcePolicyStatements as te}from"./unlock/toResourcePolicyStatements.js";import{restoreBucketPolicy as ie,synthesiseEnforceSslDocument as ae,ensureEnforceSsl as ne}from"./unlock/restoreBucketPolicy.js";import{restoreAndReconcileQuarantinedBucket as le}from"./unlock/restoreAndReconcileQuarantinedBucket.js";import{parseAccountsConfiguration as ce,flattenAccountsToEnvironments as me,extractAllAccountNames as ue,accountsConfigToOUTree as fe,isStringArray as de,isAccountsConfig as Re,isOuOnlyAccountBucket as Te,OU_ONLY_ACCOUNT_BUCKETS as Se}from"./organisation/accountsConfig.js";import{resolveBuildSecrets as Pe,resolveSecretRefValue as Ae,resolveBuildSecretSessionProvider as Ee,sourcedRefsFromBuildArgs as De}from"./application/buildSecretResolver.js";import{buildBuildSecretSessionPolicy as _e,partitionForRegion as ge}from"./application/buildSecretSession.js";import{buildDeploySessionPolicy as Fe}from"./application/deploySessionPolicy.js";import{resolveBuildArgs as ke}from"./application/buildArgResolver.js";import{validateBuildGroup as be}from"./application/buildGroupValidator.js";import{runOpenNextBuild as Le}from"./application/openNextBuild.js";import{runOrganisationSetup as Ie,ORG_SETUP_PHASES as he}from"./organisation/organisationSetup.js";export*from"./builders/index.js";import{buildDeployPlan as Ke,computeDeployPlan as Me,computeAssemblyDigest as Ye,digestHead as Ve,classifyImpact as Je,derivePropertyChanges as He,isDataLoss as Qe,isStatefulResourceType as we,renderPlanSummary as je,renderPlanLines as We,toWirePlanChanges as qe,signApprovalToken as ze,verifyApprovalToken as Xe,DEFAULT_APPROVAL_TTL_MS as Ze,APPROVAL_TOKEN_PATTERN as $e}from"./application/plan/index.js";import{runApprovalGate as rr}from"./application/approvalGate.js";import{runDestructionGate as or,buildDestructionTicket as ir,evaluateDestructionConsents as ar,computeTicketDigest as nr,legalRemediationVerbs as sr,executableRemediationVerbs as lr,advertisableRemediationVerbs as pr,renderDestructionTicketLines as cr,renderConsentVerdictLines as mr}from"./application/destructionGate.js";import{loadDeployPlan as fr,createMemoisedPlanLoader as dr}from"./application/plan/loadDeployPlan.js";import{classifyDriftFailure as Tr,fetchLastOperationEvents as Sr}from"./drift/classifyDriftFailure.js";import{DriftProbe as Pr}from"./drift/driftProbe.js";import{clearDriftSuspects as Er,defaultDriftJournalDir as Dr,readDriftSuspects as yr,recordDriftSuspects as _r}from"./drift/driftSuspectJournal.js";import{detectStackDrift as Or}from"./drift/detectStackDrift.js";import{runDriftPreFlight as vr,formatDriftPreFlightBlock as kr,DRIFT_PREFLIGHT_TRIGGER_STATUSES as Br,DRIFT_PREFLIGHT_BUDGET_MS as br}from"./drift/driftPreFlight.js";import{archiveCompletedRemediationJournal as Lr,computeRemediationOpId as Ur,defaultRemediationJournalDir as Ir,findRemediationJournalByOpId as hr,listActiveRemediationOps as Nr,listActiveRemediationOpsForContext as Gr,readRemediationJournal as Kr,sweepExpiredRemediationJournals as Mr,writeRemediationJournal as Yr,REMEDIATION_JOURNAL_RETENTION_DAYS as Vr}from"./remediation/remediationJournal.js";import{captureRemediationForensics as Hr,defaultRemediationForensicsDir as Qr}from"./remediation/forensicsCapture.js";import{applyRetainFlip as jr,composeFlipCapabilities as Wr,computeTemplateDigest as qr,deriveRetainFlipTemplate as zr,findMarkedResourcesByOpId as Xr,proveFlipMetadataOnly as Zr,readRetainFlipState as $r,verifyRetainFlipDiff as et,FORGET_MARKER_KEY as rt,FORGET_FLIP_SPEC as tt,RECREATE_MARKER_KEY as ot}from"./remediation/retainFlip.js";import{assessForgetResumability as at,completeForgetAfterConverge as nt,completeForgetAfterDeploy as st,formatRecreateInFlightCures as lt,partitionPreFlightByRemediation as pt,runForgetSurgery as ct,synthTemplateDeclaresResource as mt}from"./remediation/forgetResource.js";import{runRecreateSurgery as ft,snapshotPolicyForType as dt,SNAPSHOT_CAPABLE_RESOURCE_TYPES as Rt}from"./remediation/recreateResource.js";import{runRecreatePreFlight as St}from"./remediation/recreatePreFlight.js";import{deriveRemovalTemplate as Pt,findRemovalDeletionFailures as At,proveRemovalTargetsOnly as Et}from"./remediation/removalUpdate.js";import{advertisableDriftVerbs as yt,buildDriftRepairTicket as _t,runDriftRepairGate as gt}from"./remediation/driftRepairGate.js";import{formatPinOutcomeDetail as Ft,pinProbeCoverage as vt,renderPinReportLines as kt,runPinRemediation as Bt}from"./remediation/pinRemediation.js";import{composePostFailureRepairOffer as Ct,formatDriftRepairOffer as Lt,DriftRepairAvailableError as Ut}from"./remediation/postFailureRepairOffer.js";import{checkDeployPathStackAvailability as ht,isDeployPathBlockingStatus as Nt,StackUnavailableError as Gt}from"./activeDeploymentGuard.js";import{assertLeaseBeforeStack as Mt,LeaseDeniedError as Yt}from"./application/leaseGate.js";export{$e as APPROVAL_TOKEN_PATTERN,Ze as DEFAULT_APPROVAL_TTL_MS,br as DRIFT_PREFLIGHT_BUDGET_MS,Br as DRIFT_PREFLIGHT_TRIGGER_STATUSES,Pr as DriftProbe,Ut as DriftRepairAvailableError,tt as FORGET_FLIP_SPEC,rt as FORGET_MARKER_KEY,Yt as LeaseDeniedError,he as ORG_SETUP_PHASES,J as ORG_TRAIL_BUCKET_OUTPUT_KEY,Se as OU_ONLY_ACCOUNT_BUCKETS,g as PRE_EMPTY_TAG_KEYS,ot as RECREATE_MARKER_KEY,Vr as REMEDIATION_JOURNAL_RETENTION_DAYS,F as SAFE_CLEANUP_STATES,Rt as SNAPSHOT_CAPABLE_RESOURCE_TYPES,Gt as StackUnavailableError,H as TRAIL_BUCKET_OUTPUT_KEY,Q as TRAIL_KEY_ARN_OUTPUT_KEY,fe as accountsConfigToOUTree,yt as advertisableDriftVerbs,pr as advertisableRemediationVerbs,jr as applyRetainFlip,Lr as archiveCompletedRemediationJournal,Mt as assertLeaseBeforeStack,at as assessForgetResumability,b as buildAccountRegionPairs,_e as buildBuildSecretSessionPolicy,Ke as buildDeployPlan,Fe as buildDeploySessionPolicy,ir as buildDestructionTicket,_t as buildDriftRepairTicket,B as buildRegionList,Hr as captureRemediationForensics,C as cascadeHomeRegion,L as cascadeOperationKey,ht as checkDeployPathStackAvailability,Tr as classifyDriftFailure,Je as classifyImpact,x as cleanupFailedStack,Er as clearDriftSuspects,nt as completeForgetAfterConverge,st as completeForgetAfterDeploy,Wr as composeFlipCapabilities,Ct as composePostFailureRepairOffer,Ye as computeAssemblyDigest,Me as computeDeployPlan,Ur as computeRemediationOpId,p as computeSecretsDrift,qr as computeTemplateDigest,nr as computeTicketDigest,dr as createMemoisedPlanLoader,Y as decideNextTransition,j as decommissionMemberTrailStorage,Dr as defaultDriftJournalDir,Qr as defaultRemediationForensicsDir,Ir as defaultRemediationJournalDir,t as deploy,d as deployOrganisation,He as derivePropertyChanges,Pt as deriveRemovalTemplate,zr as deriveRetainFlipTemplate,c as deriveStaleParameters,i as destroy,T as destroyOrganisation,Or as detectStackDrift,Ve as digestHead,P as emptyS3Bucket,ne as ensureEnforceSsl,ar as evaluateDestructionConsents,lr as executableRemediationVerbs,ue as extractAllAccountNames,m as extractConsumedSsmParameters,Sr as fetchLastOperationEvents,Xr as findMarkedResourcesByOpId,hr as findRemediationJournalByOpId,At as findRemovalDeletionFailures,me as flattenAccountsToEnvironments,kr as formatDriftPreFlightBlock,Lt as formatDriftRepairOffer,Ft as formatPinOutcomeDetail,E as formatQuarantineSuspectedMessage,lt as formatRecreateInFlightCures,D as formatRetainedBucketsMessage,Re as isAccountsConfig,O as isCleanableState,Qe as isDataLoss,Nt as isDeployPathBlockingStatus,ee as isEnforceSslStatement,Te as isOuOnlyAccountBucket,y as isQuarantineDetail,_ as isRetainedBucketsDetail,we as isStatefulResourceType,de as isStringArray,sr as legalRemediationVerbs,Nr as listActiveRemediationOps,Gr as listActiveRemediationOpsForContext,fr as loadDeployPlan,K as mergeReconciledProviderAccounts,ce as parseAccountsConfiguration,k as partitionAccounts,ge as partitionForRegion,pt as partitionPreFlightByRemediation,vt as pinProbeCoverage,A as preEmptyStackBuckets,s as probeSecretsDrift,h as projectAccountRows,I as projectScalarSummary,Zr as proveFlipMetadataOnly,Et as proveRemovalTargetsOnly,yr as readDriftSuspects,Kr as readRemediationJournal,$r as readRetainFlipState,G as reconcileProviderAccounts,V as reconcileTrailMigration,_r as recordDriftSuspects,mr as renderConsentVerdictLines,cr as renderDestructionTicketLines,kt as renderPinReportLines,We as renderPlanLines,je as renderPlanSummary,ke as resolveBuildArgs,Ee as resolveBuildSecretSessionProvider,Pe as resolveBuildSecrets,Ae as resolveSecretRefValue,n as restart,le as restoreAndReconcileQuarantinedBucket,ie as restoreBucketPolicy,rr as runApprovalGate,or as runDestructionGate,vr as runDriftPreFlight,gt as runDriftRepairGate,ct as runForgetSurgery,Le as runOpenNextBuild,Ie as runOrganisationSetup,Bt as runPinRemediation,St as runRecreatePreFlight,ft as runRecreateSurgery,ze as signApprovalToken,dt as snapshotPolicyForType,De as sourcedRefsFromBuildArgs,u as ssmParameterNameFromValueFrom,Mr as sweepExpiredRemediationJournals,mt as synthTemplateDeclaresResource,ae as synthesiseEnforceSslDocument,te as toResourcePolicyStatements,qe as toWirePlanChanges,$ as triageBucketPolicy,q as unlockBucket,X as unlockQueue,be as validateBuildGroup,Xe as verifyApprovalToken,et as verifyRetainFlipDiff,Yr as writeRemediationJournal};
|
|
1
|
+
import{deploy as t}from"./deploy.js";import{destroy as i}from"./destroy.js";import{restart as n,probeSecretsDrift as s}from"./restart/restartApplication.js";import{computeSecretsDrift as p,deriveStaleParameters as c,extractConsumedSsmParameters as m,ssmParameterNameFromValueFrom as u}from"./restart/secretsDrift.js";import{deployOrganisation as d}from"./organisation/organisationDeploy.js";import{destroyOrganisation as T}from"./organisation/organisationDestroy.js";import{cleanupFailedStack as x,emptyS3Bucket as P,preEmptyStackBuckets as A,formatQuarantineSuspectedMessage as E,formatRetainedBucketsMessage as D,isQuarantineDetail as y,isRetainedBucketsDetail as _,PRE_EMPTY_TAG_KEYS as g,isCleanableState as O,SAFE_CLEANUP_STATES as F}from"./stackCleanup.js";import{partitionAccounts as k,buildRegionList as B,buildAccountRegionPairs as b,cascadeHomeRegion as C,cascadeOperationKey as L}from"./organisation/cascadeHelpers.js";import{projectScalarSummary as I,projectAccountRows as h}from"./organisation/cascadeSummary.js";import{reconcileProviderAccounts as G,mergeReconciledProviderAccounts as K}from"./organisation/reconcileProviderAccounts.js";import{decideNextTransition as Y,reconcileTrailMigration as V,ORG_TRAIL_BUCKET_OUTPUT_KEY as J,TRAIL_BUCKET_OUTPUT_KEY as H,TRAIL_KEY_ARN_OUTPUT_KEY as Q}from"./trailMigration/trailMigration.js";import{decommissionMemberTrailStorage as j}from"./trailMigration/memberTrailCleanup.js";import{unlockBucket as q}from"./unlock/unlockBucket.js";import{unlockQueue as X}from"./unlock/unlockQueue.js";import{triageBucketPolicy as $,isEnforceSslStatement as ee}from"./unlock/bucketPolicyTriage.js";import{toResourcePolicyStatements as te}from"./unlock/toResourcePolicyStatements.js";import{restoreBucketPolicy as ie,synthesiseEnforceSslDocument as ae,ensureEnforceSsl as ne}from"./unlock/restoreBucketPolicy.js";import{restoreAndReconcileQuarantinedBucket as le}from"./unlock/restoreAndReconcileQuarantinedBucket.js";import{parseAccountsConfiguration as ce,flattenAccountsToEnvironments as me,extractAllAccountNames as ue,accountsConfigToOUTree as fe,isStringArray as de,isAccountsConfig as Re,isOuOnlyAccountBucket as Te,OU_ONLY_ACCOUNT_BUCKETS as Se}from"./organisation/accountsConfig.js";import{resolveBuildSecrets as Pe,resolveSecretRefValue as Ae,resolveBuildSecretSessionProvider as Ee,sourcedRefsFromBuildArgs as De}from"./application/buildSecretResolver.js";import{buildBuildSecretSessionPolicy as _e,partitionForRegion as ge}from"./application/buildSecretSession.js";import{buildDeploySessionPolicy as Fe}from"./application/deploySessionPolicy.js";import{resolveBuildArgs as ke}from"./application/buildArgResolver.js";import{validateBuildGroup as be}from"./application/buildGroupValidator.js";import{runOpenNextBuild as Le}from"./application/openNextBuild.js";import{runOrganisationSetup as Ie,ORG_SETUP_PHASES as he}from"./organisation/organisationSetup.js";export*from"./builders/index.js";import{buildDeployPlan as Ke,computeDeployPlan as Me,computeAssemblyDigest as Ye,digestHead as Ve,classifyImpact as Je,derivePropertyChanges as He,isDataLoss as Qe,isStatefulResourceType as we,renderPlanSummary as je,renderPlanLines as We,toWirePlanChanges as qe,signApprovalToken as ze,verifyApprovalToken as Xe,DEFAULT_APPROVAL_TTL_MS as Ze,APPROVAL_TOKEN_PATTERN as $e}from"./application/plan/index.js";import{runApprovalGate as rr}from"./application/approvalGate.js";import{runDestructionGate as or,buildDestructionTicket as ir,evaluateDestructionConsents as ar,computeTicketDigest as nr,legalRemediationVerbs as sr,executableRemediationVerbs as lr,advertisableRemediationVerbs as pr,renderDestructionTicketLines as cr,renderConsentVerdictLines as mr}from"./application/destructionGate.js";import{loadDeployPlan as fr,createMemoisedPlanLoader as dr}from"./application/plan/loadDeployPlan.js";import{classifyDriftFailure as Tr,fetchLastOperationEvents as Sr}from"./drift/classifyDriftFailure.js";import{DriftProbe as Pr}from"./drift/driftProbe.js";import{clearDriftSuspects as Er,defaultDriftJournalDir as Dr,readDriftSuspects as yr,recordDriftSuspects as _r}from"./drift/driftSuspectJournal.js";import{detectStackDrift as Or}from"./drift/detectStackDrift.js";import{runDriftPreFlight as vr,formatDriftPreFlightBlock as kr,DRIFT_PREFLIGHT_TRIGGER_STATUSES as Br,DRIFT_PREFLIGHT_BUDGET_MS as br}from"./drift/driftPreFlight.js";import{archiveCompletedRemediationJournal as Lr,computeRemediationOpId as Ur,defaultRemediationJournalDir as Ir,findRemediationJournalByOpId as hr,listActiveRemediationOps as Nr,listActiveRemediationOpsForContext as Gr,readRemediationJournal as Kr,sweepExpiredRemediationJournals as Mr,writeRemediationJournal as Yr,REMEDIATION_JOURNAL_RETENTION_DAYS as Vr}from"./remediation/remediationJournal.js";import{captureRemediationForensics as Hr,defaultRemediationForensicsDir as Qr}from"./remediation/forensicsCapture.js";import{applyRetainFlip as jr,composeFlipCapabilities as Wr,computeTemplateDigest as qr,deriveRetainFlipTemplate as zr,findMarkedResourcesByOpId as Xr,proveFlipMetadataOnly as Zr,readRetainFlipState as $r,verifyRetainFlipDiff as et,FORGET_MARKER_KEY as rt,FORGET_FLIP_SPEC as tt,RECREATE_MARKER_KEY as ot}from"./remediation/retainFlip.js";import{assessForgetResumability as at,completeForgetAfterConverge as nt,completeForgetAfterDeploy as st,formatRecreateInFlightCures as lt,partitionPreFlightByRemediation as pt,runForgetSurgery as ct,synthTemplateDeclaresResource as mt}from"./remediation/forgetResource.js";import{runRecreateSurgery as ft,snapshotPolicyForType as dt,SNAPSHOT_CAPABLE_RESOURCE_TYPES as Rt}from"./remediation/recreateResource.js";import{runRecreatePreFlight as St}from"./remediation/recreatePreFlight.js";import{deriveRemovalTemplate as Pt,findRemovalDeletionFailures as At,findResourcesPresent as Et,proveRemovalTargetsOnly as Dt}from"./remediation/removalUpdate.js";import{advertisableDriftVerbs as _t,buildDriftRepairTicket as gt,runDriftRepairGate as Ot}from"./remediation/driftRepairGate.js";import{formatPinOutcomeDetail as vt,pinProbeCoverage as kt,renderPinReportLines as Bt,runPinRemediation as bt}from"./remediation/pinRemediation.js";import{composePostFailureRepairOffer as Lt,formatDriftRepairOffer as Ut,DriftRepairAvailableError as It}from"./remediation/postFailureRepairOffer.js";import{checkDeployPathStackAvailability as Nt,isDeployPathBlockingStatus as Gt,StackUnavailableError as Kt}from"./activeDeploymentGuard.js";import{assertLeaseBeforeStack as Yt,LeaseDeniedError as Vt}from"./application/leaseGate.js";export{$e as APPROVAL_TOKEN_PATTERN,Ze as DEFAULT_APPROVAL_TTL_MS,br as DRIFT_PREFLIGHT_BUDGET_MS,Br as DRIFT_PREFLIGHT_TRIGGER_STATUSES,Pr as DriftProbe,It as DriftRepairAvailableError,tt as FORGET_FLIP_SPEC,rt as FORGET_MARKER_KEY,Vt as LeaseDeniedError,he as ORG_SETUP_PHASES,J as ORG_TRAIL_BUCKET_OUTPUT_KEY,Se as OU_ONLY_ACCOUNT_BUCKETS,g as PRE_EMPTY_TAG_KEYS,ot as RECREATE_MARKER_KEY,Vr as REMEDIATION_JOURNAL_RETENTION_DAYS,F as SAFE_CLEANUP_STATES,Rt as SNAPSHOT_CAPABLE_RESOURCE_TYPES,Kt as StackUnavailableError,H as TRAIL_BUCKET_OUTPUT_KEY,Q as TRAIL_KEY_ARN_OUTPUT_KEY,fe as accountsConfigToOUTree,_t as advertisableDriftVerbs,pr as advertisableRemediationVerbs,jr as applyRetainFlip,Lr as archiveCompletedRemediationJournal,Yt as assertLeaseBeforeStack,at as assessForgetResumability,b as buildAccountRegionPairs,_e as buildBuildSecretSessionPolicy,Ke as buildDeployPlan,Fe as buildDeploySessionPolicy,ir as buildDestructionTicket,gt as buildDriftRepairTicket,B as buildRegionList,Hr as captureRemediationForensics,C as cascadeHomeRegion,L as cascadeOperationKey,Nt as checkDeployPathStackAvailability,Tr as classifyDriftFailure,Je as classifyImpact,x as cleanupFailedStack,Er as clearDriftSuspects,nt as completeForgetAfterConverge,st as completeForgetAfterDeploy,Wr as composeFlipCapabilities,Lt as composePostFailureRepairOffer,Ye as computeAssemblyDigest,Me as computeDeployPlan,Ur as computeRemediationOpId,p as computeSecretsDrift,qr as computeTemplateDigest,nr as computeTicketDigest,dr as createMemoisedPlanLoader,Y as decideNextTransition,j as decommissionMemberTrailStorage,Dr as defaultDriftJournalDir,Qr as defaultRemediationForensicsDir,Ir as defaultRemediationJournalDir,t as deploy,d as deployOrganisation,He as derivePropertyChanges,Pt as deriveRemovalTemplate,zr as deriveRetainFlipTemplate,c as deriveStaleParameters,i as destroy,T as destroyOrganisation,Or as detectStackDrift,Ve as digestHead,P as emptyS3Bucket,ne as ensureEnforceSsl,ar as evaluateDestructionConsents,lr as executableRemediationVerbs,ue as extractAllAccountNames,m as extractConsumedSsmParameters,Sr as fetchLastOperationEvents,Xr as findMarkedResourcesByOpId,hr as findRemediationJournalByOpId,At as findRemovalDeletionFailures,Et as findResourcesPresent,me as flattenAccountsToEnvironments,kr as formatDriftPreFlightBlock,Ut as formatDriftRepairOffer,vt as formatPinOutcomeDetail,E as formatQuarantineSuspectedMessage,lt as formatRecreateInFlightCures,D as formatRetainedBucketsMessage,Re as isAccountsConfig,O as isCleanableState,Qe as isDataLoss,Gt as isDeployPathBlockingStatus,ee as isEnforceSslStatement,Te as isOuOnlyAccountBucket,y as isQuarantineDetail,_ as isRetainedBucketsDetail,we as isStatefulResourceType,de as isStringArray,sr as legalRemediationVerbs,Nr as listActiveRemediationOps,Gr as listActiveRemediationOpsForContext,fr as loadDeployPlan,K as mergeReconciledProviderAccounts,ce as parseAccountsConfiguration,k as partitionAccounts,ge as partitionForRegion,pt as partitionPreFlightByRemediation,kt as pinProbeCoverage,A as preEmptyStackBuckets,s as probeSecretsDrift,h as projectAccountRows,I as projectScalarSummary,Zr as proveFlipMetadataOnly,Dt as proveRemovalTargetsOnly,yr as readDriftSuspects,Kr as readRemediationJournal,$r as readRetainFlipState,G as reconcileProviderAccounts,V as reconcileTrailMigration,_r as recordDriftSuspects,mr as renderConsentVerdictLines,cr as renderDestructionTicketLines,Bt as renderPinReportLines,We as renderPlanLines,je as renderPlanSummary,ke as resolveBuildArgs,Ee as resolveBuildSecretSessionProvider,Pe as resolveBuildSecrets,Ae as resolveSecretRefValue,n as restart,le as restoreAndReconcileQuarantinedBucket,ie as restoreBucketPolicy,rr as runApprovalGate,or as runDestructionGate,vr as runDriftPreFlight,Ot as runDriftRepairGate,ct as runForgetSurgery,Le as runOpenNextBuild,Ie as runOrganisationSetup,bt as runPinRemediation,St as runRecreatePreFlight,ft as runRecreateSurgery,ze as signApprovalToken,dt as snapshotPolicyForType,De as sourcedRefsFromBuildArgs,u as ssmParameterNameFromValueFrom,Mr as sweepExpiredRemediationJournals,mt as synthTemplateDeclaresResource,ae as synthesiseEnforceSslDocument,te as toResourcePolicyStatements,qe as toWirePlanChanges,$ as triageBucketPolicy,q as unlockBucket,X as unlockQueue,be as validateBuildGroup,Xe as verifyApprovalToken,et as verifyRetainFlipDiff,Yr as writeRemediationJournal};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{join as
|
|
1
|
+
import{join as B}from"path";import{success as j,failure as C}from"@fjall/generator";import{logger as y}from"@fjall/util/logger";import{maskSensitiveOutput as p,mapSettledWithConcurrency as K}from"@fjall/util";import{ORGANISATION_TYPES as R,getOrganisationStackName as H}from"../../types/operations.js";import{CdkContextBuilder as z}from"../../services/supporting/CdkContextBuilder.js";import{stubCallerIdentity as Y}from"../../types/deployment/index.js";import{CloudFormationService as M}from"../../services/infrastructure/CloudFormationService.js";import{getCascadeStateFilePath as W,regionSuffix as X}from"../../types/config/FjallState.js";import{BackupClient as q}from"@aws-sdk/client-backup";import{accountHasDisasterRecovery as J,describeBackupVaultExists as Q}from"../../aws/organisations/backup.js";import{buildParamsContext as Z,collectStackOutputs as T,assumeCascadeRole as k,forwardOutput as E,reconcileOidcProviderIfCarried as V}from"../contextHelpers.js";import{assertLeaseBeforeStack as ee}from"../application/leaseGate.js";import{runDriftPreFlightGate as te}from"../drift/preFlightGate.js";import{accountTier as G}from"@fjall/util";import{DEFAULT_REGION as U}from"../../aws/utils/regions.js";const oe=4;function we(e){const a=e.find(t=>G(t)==="platform"),c=e.filter(t=>G(t)==="account");return{platformAccount:a,memberAccounts:c}}function re(e){const a=e?.primaryRegion??U,c=e?.secondaryRegions??[],t=new Set([a,...c]),i=e?.disasterRecoveryRegion;return i&&t.add(i),[...t]}function Pe(e){return re(e)[0]??U}function ne(e,a){return`${e} (${a})`}function $e(e,a){const c=[];for(const t of a)for(const i of e)c.push({account:i,region:t});return c}import{buildCascadeRoleArn as Ee}from"../contextHelpers.js";async function Ae(e,a,c,t){c.onLog?.(`Verifying cascade role access for ${a.length} account(s)\u2026`,"info");const i=await K(a,oe,async s=>k(e.awsProvider,s.id,s.region??e.awsProvider.getRegion(),`fjall-preflight-${s.name}`,t)),r=[];return i.forEach((s,o)=>{const n=a[o];if(n){if(s.status==="rejected"){r.push({accountId:n.id,accountName:n.name,error:s.reason instanceof Error?s.reason.message:String(s.reason)});return}s.value.success||r.push({accountId:n.id,accountName:n.name,error:s.value.error.message})}}),r.length===0&&c.onLog?.(`Cascade role access verified for ${a.length} account(s)`,"info"),r}async function Oe(e,a,c,t,i,r,s){const o=s?.region??t.region??a.awsProvider.getRegion(),n=ne(t.name,o),u=s?.orgConfig??e.orgConfig,g=s?.ipamPoolId;r.onCascadeAccountStart?.(n,t.id,o,i);const f=await k(a.awsProvider,t.id,o,`fjall-cascade-${t.name}`,e.abortSignal);if(!f.success)return r.onCascadeAccountComplete?.(n,!1,p(f.error.message),o),C(new Error(`Failed to assume role for ${t.name}: ${p(f.error.message)}`));const{provider:l,credentials:S}=f.data,P=B(e.workingDirectory,"fjall",i==="platform"?R.PLATFORM:R.ACCOUNT),$=B(P,`cdk.out.${t.id}.${X(o)}`),h=z.buildDeploymentContext({deployType:i,target:c.target,path:P,assemblyDir:$,environment:t.environment??void 0,region:o,accountName:t.name,callerIdentity:Y(t.id),ipamPoolId:g,...Z({orgConfig:u,identity:e.identity,skipOidc:e.options?.skipOidc,region:o,primaryRegion:s?.primaryRegion,trailLifecycle:t.trailLifecycle})},{verbose:e.options?.verbose},u);if(J(t.environment,u?.disasterRecoveryRegion)){const m=await Q(l.getClient(q),e.abortSignal);if(!m.success)return r.onCascadeAccountComplete?.(n,!1,p(m.error.message),o),C(new Error(`Backup vault probe failed for ${t.name}: ${p(m.error.message)}`));h.fjallAdoptBackupVault=m.data}r.onCascadeAccountPhaseChange?.(n,"synth",o);const A=await a.cdkService.runCdkSynth(h,E(r),S);if(!A.success)return r.onCascadeAccountComplete?.(n,!1,p(`Synth failed: ${A.error}`),o),C(new Error(`Synth failed for ${t.name}: ${p(A.error)}`));const d=H(i==="platform"?R.PLATFORM:R.ACCOUNT),I=W(P,t.id,o),w=new M(l),{changed:_,currentHash:F}=await a.hashService.compareCascadeStack($,d,I);if(!_&&e.options?.force!==!0&&await w.stackExists(d,void 0,e.abortSignal)){const m=await w.getStackOutputs(d,void 0,e.abortSignal);m.success||y.debug("cascadeHelpers","Failed to read outputs for skipped cascade account (non-critical)",{stackName:d,account:t.name});const b=T(m);return await V(h,l,r,e.abortSignal),r.onLog?.(`${t.name}: no infrastructure changes \u2014 skipping deploy`,"info"),r.onCascadeAccountComplete?.(n,!0,void 0,o,b,!0),j({outputs:b,skipped:!0})}const O=await te(l,{stackNames:[d],cdkOutPath:$,cfnService:w,callbacks:r,...e.abortSignal!==void 0?{abortSignal:e.abortSignal}:{}});if(O.kind==="blocked")return r.onCascadeAccountComplete?.(n,!1,O.message,o),C(new Error(O.message));const v=await ee(e.assertLeaseForStack,d,e.abortSignal);if(v!==void 0)return r.onCascadeAccountComplete?.(n,!1,v.message,o),C(v);r.onCascadeAccountPhaseChange?.(n,"bootstrap",o);const x=await a.cdkService.runCdkBootstrap(h,E(r),S);if(!x.success)return r.onCascadeAccountComplete?.(n,!1,p(`Bootstrap failed: ${x.error}`),o),C(new Error(`Bootstrap failed for ${t.name}: ${p(x.error)}`));r.onCascadeAccountPhaseChange?.(n,"deploy",o);const D=await a.cdkService.runCdkDeploy(h,d,E(r),m=>r.onCascadeAccountResourceProgress?.(n,m,o),l,S);if(!D.success)return r.onCascadeAccountComplete?.(n,!1,p(D.error),o),C(new Error(p(D.error)));const N=await w.getStackOutputs(d,void 0,e.abortSignal);N.success||y.debug("cascadeHelpers","Failed to read cascade account stack outputs (non-critical)",{stackName:d,account:t.name});const L=T(N);return await V(h,l,r,e.abortSignal),F!==void 0&&((await a.hashService.persistCascadeStack(I,d,F)).success||y.debug("cascadeHelpers","Failed to persist cascade hash state (non-critical)",{stackName:d,account:t.name})),r.onCascadeAccountComplete?.(n,!0,void 0,o,L,!1),j({outputs:L,skipped:!1})}async function ve(e,a,c,t){const i=new Map,r=e.awsProvider.getRegion(),s=await k(e.awsProvider,a.id,r,`fjall-ipam-read-${a.name}`,t);if(!s.success)return y.debug("organisationDeploy",`Cannot read Platform outputs: ${s.error.message}`),i;const o=new M(s.data.provider),n=H(R.PLATFORM),u=await o.getStackOutputs(n);if(!u.success)return y.debug("organisationDeploy",`Failed to read Platform stack outputs: ${u.error.message}`),i;const g=/^IpamPoolId(\d{12})(\w+)$/;for(const f of u.data){const l=f.OutputKey?.match(g);if(l&&f.OutputValue){const S=`${l[1]}-${l[2]}`;i.set(S,f.OutputValue)}}return i.size>0&&c.onLog?.(`Read ${i.size} IPAM pool ID(s) from Platform stack`,"info"),i}async function xe(e,a){const c=e.getDomains();if(c.length===0)return{domainsDeployed:0,errors:[]};a.onCascadePhaseStart?.("domains");const t=c.filter(o=>o.type==="apex"),i=c.filter(o=>o.type==="delegated");let r=0;const s=[];for(const o of t){const n=await e.deployDomain(o.name,a);n.success?r++:s.push(`${o.name}: ${n.error.message}`)}if(i.length>0){const o=await Promise.allSettled(i.map(n=>e.deployDomain(n.name,a)));for(let n=0;n<o.length;n++){const u=o[n],g=i[n];if(!(!u||!g))if(u.status==="fulfilled")u.value.success?r++:s.push(`${g.name}: ${u.value.error.message}`);else{const f=u.reason instanceof Error?u.reason.message:String(u.reason);s.push(`${g.name}: ${f}`)}}}return a.onCascadePhaseComplete?.("domains"),{domainsDeployed:r,errors:s}}export{oe as CASCADE_MAX_CONCURRENCY,$e as buildAccountRegionPairs,Ee as buildCascadeRoleArn,re as buildRegionList,Pe as cascadeHomeRegion,ne as cascadeOperationKey,Oe as deployCascadeAccount,xe as deployDomains,we as partitionAccounts,Ae as probeCascadeRoles,ve as readPlatformIpamPoolIds};
|
|
@@ -44,4 +44,4 @@ export declare function failPrepareStep(callbacks: DeployCallbacks): void;
|
|
|
44
44
|
/** Mask a failure message, surface it via onError, and return the failure. */
|
|
45
45
|
export declare function maskAndFail(callbacks: DeployCallbacks, message: string): Result<never>;
|
|
46
46
|
/** Best-effort stack-output read: log at debug on failure, never fail. */
|
|
47
|
-
export declare function readStackOutputsBestEffort(services: DeployServices, callbacks: DeployCallbacks, stackName: string, failureLogMessage: string): Promise<Record<string, string> | undefined>;
|
|
47
|
+
export declare function readStackOutputsBestEffort(services: DeployServices, callbacks: DeployCallbacks, stackName: string, failureLogMessage: string, abortSignal?: AbortSignal): Promise<Record<string, string> | undefined>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{failure as
|
|
1
|
+
import{failure as C}from"@fjall/generator";import{collectStackOutputs as P}from"../contextHelpers.js";import{maskSensitiveOutput as a}from"@fjall/util";import{INFRA_STEP_NAME as E,STEP_IDS as e,STEP_NAMES as o}from"../../types/stepDefinitions.js";const A={CONNECT:{id:e.CONNECT,name:E.CONNECT},PREPARE:{id:e.PREPARE_ENVIRONMENT,name:E.PREPARE},DEPLOY:{id:e.DEPLOY,name:E.DEPLOY},MONITORING:{id:e.MONITORING,name:E.MONITORING},ORG_DEPLOY:{id:e.ORG_DEPLOY,name:o.ORG_DEPLOY},CASCADE_PLATFORM:{id:e.CASCADE_PLATFORM,name:o.CASCADE_PLATFORM},CASCADE_ACCOUNTS:{id:e.CASCADE_ACCOUNTS,name:o.CASCADE_ACCOUNTS}},R=4;function c(t,r){t.onStackTargets===void 0||r.length===0||t.onStackTargets(r)}function p(t){t.onStepComplete?.(A.PREPARE.id,A.PREPARE.name,"error",1,R)}function s(t,r){const n=new Error(a(r));return t.onError?.(n),C(n)}async function f(t,r,n,i,S){const O=await t.cfnService.getStackOutputs(n,void 0,S);return O.success||r.onLog?.(i,"debug"),P(O)}export{A as INFRA_STEPS,R as INFRA_STEP_TOTAL,c as emitOrgStackTargets,p as failPrepareStep,s as maskAndFail,f as readStackOutputsBestEffort};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import{join as _}from"node:path";import{OrganizationsClient as Q}from"@aws-sdk/client-organizations";import{success as V,failure as v}from"@fjall/generator";import{ORGANISATION_TYPES as A,getOrganisationStackName as R}from"../../types/operations.js";import{synthOrFail as X,bootstrapOrFail as Z,forwardOutput as tt,forwardResourceProgress as et}from"../contextHelpers.js";import{partitionAccounts as ot,probeCascadeRoles as at,buildRegionList as rt,buildAccountRegionPairs as nt}from"../organisation/cascadeHelpers.js";import{assertLeaseBeforeStack as
|
|
1
|
+
import{join as _}from"node:path";import{OrganizationsClient as Q}from"@aws-sdk/client-organizations";import{success as V,failure as v}from"@fjall/generator";import{ORGANISATION_TYPES as A,getOrganisationStackName as R}from"../../types/operations.js";import{synthOrFail as X,bootstrapOrFail as Z,forwardOutput as tt,forwardResourceProgress as et}from"../contextHelpers.js";import{partitionAccounts as ot,probeCascadeRoles as at,buildRegionList as rt,buildAccountRegionPairs as nt}from"../organisation/cascadeHelpers.js";import{assertLeaseBeforeStack as it}from"../application/leaseGate.js";import{accountTier as ct,maskSensitiveOutput as g}from"@fjall/util";import{findDevelopmentOuId as st}from"../../aws/organisations/organisationalUnits.js";import{buildOrgContext as dt,resolveOrgDetails as ut}from"./orgContext.js";import{INFRA_STEPS as B,emitOrgStackTargets as lt,maskAndFail as O,readStackOutputsBestEffort as Y}from"./infraSteps.js";import{resolveCascadeAccounts as gt}from"./resolveCascadeAccounts.js";import{executeCascade as ft}from"./cascadeExecution.js";import{reconcileOrgTrailOutputs as pt}from"./trailReconciliation.js";import{runDriftPreFlightGate as mt}from"../drift/preFlightGate.js";async function Et(a,e,I,z){const{callbacks:t,options:D}=a,{providerAccounts:f,effectiveOrgConfig:K,defaultRegion:U}=await gt(a,e),p=await ut(e,a.abortSignal);if(!p.success)return O(t,p.error.message);const w=await st(e.awsProvider.getClient(Q),p.data.rootId,a.abortSignal);if(!w.success)return O(t,w.error.message);const q={...p.data,...w.data!==void 0?{devOuId:w.data}:{}},J=f.find(o=>ct(o)==="organisation"),d=dt(a,e,I,"organisation",q,void 0,J?.trailLifecycle),l=D?.cascade!==!1,{platformAccount:s,memberAccounts:P}=ot(f),E=l&&s!==void 0?1:0,L=l&&P.length>0?1:0,i=2+E+L,T=l?[...s!==void 0?[s]:[],...P]:[];if(T.length>0){const o=await at(e,T,t,a.abortSignal),r=s!==void 0?o.find(n=>n.accountId===s.id):void 0;if(r!==void 0)return O(t,`Pre-flight cascade role check failed for the platform account ${r.accountName} (${r.accountId}): ${r.error}`);for(const n of o)t.onProgress?.({type:"warning",message:g(`Pre-flight cascade role check failed for ${n.accountName} (${n.accountId}) \u2014 its cascade deploy is expected to fail: ${n.error}`)})}t.onCascadeAccountsReconciled?.({hasPlatformAccount:E>0,hasMemberAccounts:L>0});const{id:C,name:b}=B.PREPARE;t.onStepStart?.(C,b,0,i),t.onLog?.("Synthesising organisation infrastructure\u2026","info");const F=await X(e,d,t,"CDK synthesis failed");if(!F.success)return t.onStepComplete?.(C,b,"error",0,i),F;const x=e.awsProvider.getAccountId(),N=e.awsProvider.getRegion();lt(t,[{stackName:R(A.ORGANISATION),...x!==""?{accountId:x}:{},...N!==""?{region:N}:{}},...l&&s!==void 0?[{stackName:R(A.PLATFORM),accountId:s.id,region:s.region??N}]:[],...l?nt(P,rt(a.orgConfig)).map(({account:o,region:r})=>({stackName:R(A.ACCOUNT),accountId:o.id,region:r})):[]]);const G=await Z(e,d,t);if(!G.success)return t.onStepComplete?.(C,b,"error",0,i),G;t.onStepComplete?.(C,b,"completed",0,i);const{id:m,name:S}=B.ORG_DEPLOY,c=R(A.ORGANISATION);let H=!0;const h=await e.hashService.getTemplateHashes(_(d.path,"cdk.out"));if(h.success){const o=await e.hashService.compareWithState(h.data,d.path);o.success?H=o.data.stackChanges.get(c)??!0:t.onLog?.(g(`Org root change detection failed \u2014 deploying to be safe: ${o.error.message}`),"warn")}else t.onLog?.(g(`Org root template hashing failed \u2014 deploying to be safe: ${h.error.message}`),"warn");const $=H||D?.force===!0||!await e.cfnService.stackExists(c,void 0,a.abortSignal);t.onOrgChangesDetected?.({hasOrgChanges:$});let y;if($){t.onStepStart?.(m,S,1,i);const o=await mt(e.awsProvider,{stackNames:[c],cdkOutPath:_(d.path,"cdk.out"),cfnService:e.cfnService,callbacks:t,...a.abortSignal!==void 0?{abortSignal:a.abortSignal}:{}});if(o.kind==="blocked")return t.onStepComplete?.(m,S,"error",1,i),O(t,o.message);const r=await it(a.assertLeaseForStack,c,a.abortSignal);if(r!==void 0)return t.onStepComplete?.(m,S,"error",1,i),t.onError?.(r),v(r);const n=await e.cdkService.runCdkDeploy(d,c,tt(t),et(t),e.awsProvider);if(!n.success)return t.onStepComplete?.(m,S,"error",1,i),O(t,n.error);y=await Y(e,t,c,"Failed to read org stack outputs (non-critical)",a.abortSignal);const j=h.success?h.data.get(c):void 0;if(j!==void 0){const W=await e.hashService.updateStateAfterDeploy(d.path,new Map([[c,j]]));W.success||t.onLog?.(`Warning: failed to update state file \u2014 next deploy may re-deploy the org root: ${g(W.error.message)}`,"warn")}t.onStepComplete?.(m,S,"completed",1,i)}else t.onLog?.("Organisation root: no infrastructure changes \u2014 skipping deploy","info"),y=await Y(e,t,c,"Failed to read org stack outputs (non-critical)",a.abortSignal);const u=[],k=[];let M=$;if(l&&f.length>0){const{anyCascadeDeployHappened:o}=await ft(a,e,I,{providerAccounts:f,effectiveOrgConfig:K,totalSteps:i,cascadeErrors:u,allCascadeOutputs:k});if(o&&(M=!0),await pt(a,e,{orgOutputs:y,allCascadeOutputs:k,orgDetails:p.data,providerAccounts:f,defaultRegion:U}),u.length>0){const r=u.map(n=>` ${n.accountId}: ${n.error}`).join(`
|
|
2
2
|
`);t.onLog?.(g(`Cascade failed for ${u.length} target(s):
|
|
3
|
-
${
|
|
4
|
-
`),
|
|
5
|
-
${o}`);return t.onError?.(
|
|
3
|
+
${r}`),"warn")}}if(u.length>0){const o=u.map(n=>g(`${n.accountId}: ${n.error}`)).join(`
|
|
4
|
+
`),r=new Error(`Organisation root deployed, but the cascade failed for ${u.length} target(s):
|
|
5
|
+
${o}`);return t.onError?.(r),v(r)}return V({target:I.target,deploymentType:"organisation",outputs:y,artefacts:[],...k.length>0?{cascadeOutputs:k}:{},...M?{}:{noChanges:!0},durationMs:Date.now()-z})}export{Et as deployOrgWithCascade};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{join as
|
|
1
|
+
import{join as v}from"path";import{failure as A,success as y}from"@fjall/generator";import{BackupClient as T}from"@aws-sdk/client-backup";import{IAMClient as G}from"@aws-sdk/client-iam";import{logger as M}from"@fjall/util/logger";import{getOrganisationStackName as Y}from"../../types/operations.js";import{accountHasDisasterRecovery as x,describeBackupVaultExists as B}from"../../aws/organisations/backup.js";import{describeAccountGlobalsExist as V,buildMissingAccountGlobalsAdvisory as _}from"../../aws/organisations/accountGlobals.js";import{targetsNonPrimaryRegion as j,reconcileOidcProviderIfCarried as H,synthOrFail as $,bootstrapOrFail as K,forwardOutput as q,forwardResourceProgress as z}from"../contextHelpers.js";import{accountTier as k,getErrorMessage as J,maskSensitiveOutput as N}from"@fjall/util";import{runDriftPreFlightGate as Q}from"../drift/preFlightGate.js";import{completeForgetAfterDeploy as U}from"../remediation/forgetResource.js";import{composePostFailureRepairOffer as W}from"../remediation/postFailureRepairOffer.js";import{buildOrgContext as X,resolveOrgDetailsForMemberComponent as Z,resolveOrgDetailsForSolo as tt}from"./orgContext.js";import{hasOrganisationTierAccount as ot}from"../../aws/organisations/accountGlobals.js";import{INFRA_STEPS as n,INFRA_STEP_TOTAL as a,emitOrgStackTargets as et,failPrepareStep as c,maskAndFail as l,readStackOutputsBestEffort as rt}from"./infraSteps.js";import{assertLeaseBeforeStack as nt}from"../application/leaseGate.js";async function At(o,r,d,g,D){const{callbacks:t}=o;t.onStepComplete?.(n.CONNECT.id,n.CONNECT.name,"completed",0,a),t.onStepStart?.(n.PREPARE.id,n.PREPARE.name,1,a);const R=r.awsProvider.getRegion(),f=o.orgConfig?.primaryRegion;if(f!==void 0&&j(R,f)){const e=await V(r.awsProvider.getClient(G),o.abortSignal);if(!e.success)return c(t),l(t,e.error.message);if(!e.data)return c(t),l(t,_({target:d.target,deployRegion:R,primaryRegion:f,...o.orgConfig!==void 0?{providerAccounts:o.orgConfig.providerAccounts}:{}}))}let m;if(ot(o.orgConfig?.providerAccounts)){const e=await Z(r,o.abortSignal);if(!e.success)return c(t),l(t,e.error.message);m=e.data}else m=await tt(r,o.abortSignal);const s=g==="account"?o.orgConfig?.providerAccounts.find(e=>e.id===r.awsProvider.getAccountId()):o.orgConfig?.providerAccounts.find(e=>k(e)==="platform"),I=g==="account"&&s!==void 0&&k(s)==="organisation",u=X(o,r,d,g,m,g==="account"?s?.name??d.target:void 0,s?.trailLifecycle,I),S=o.orgConfig?.disasterRecoveryRegion;if(g==="account"&&(S!==void 0&&S!=="")&&(s===void 0||x(s.environment,S))){const e=await B(r.awsProvider.getClient(T),o.abortSignal);if(!e.success)return c(t),l(t,e.error.message);u.fjallAdoptBackupVault=e.data}t.onLog?.(`Synthesising ${g} infrastructure\u2026`,"info");const b=await $(r,u,t,"CDK synthesis failed");if(!b.success)return c(t),b;const i=Y(d.type),O=r.awsProvider.getAccountId(),C=r.awsProvider.getRegion();et(t,[{stackName:i,...O!==""?{accountId:O}:{},...C!==""?{region:C}:{}}]);const w=await Q(r.awsProvider,{stackNames:[i],cdkOutPath:v(d.path,"cdk.out"),cfnService:r.cfnService,callbacks:t,...o.abortSignal!==void 0?{abortSignal:o.abortSignal}:{}});if(w.kind==="blocked")return c(t),l(t,w.message);const E=await K(r,u,t);if(!E.success)return c(t),E;t.onStepComplete?.(n.PREPARE.id,n.PREPARE.name,"completed",1,a);const p=await nt(o.assertLeaseForStack,i,o.abortSignal);if(p!==void 0)return t.onError?.(p),A(p);t.onStepStart?.(n.DEPLOY.id,n.DEPLOY.name,2,a);const P=await r.cdkService.runCdkDeploy(u,i,q(t),z(t),r.awsProvider);if(!P.success){t.onStepComplete?.(n.DEPLOY.id,n.DEPLOY.name,"error",2,a);const e=await W(r.awsProvider,{stackNames:[i],repairTarget:`--stack ${i}`,originalError:new Error(N(P.error)),cfnService:r.cfnService,...o.abortSignal!==void 0?{abortSignal:o.abortSignal}:{}});return t.onError?.(e),A(e)}t.onStepComplete?.(n.DEPLOY.id,n.DEPLOY.name,"completed",2,a);const h=await rt(r,t,i,"Failed to read stack outputs (non-critical)",o.abortSignal);await H(u,r.awsProvider,t,o.abortSignal),t.onStepStart?.(n.MONITORING.id,n.MONITORING.name,3,a),t.onStepComplete?.(n.MONITORING.id,n.MONITORING.name,"completed",3,a),await U(r.awsProvider,{stackNames:[i],onLog:(e,L)=>t.onLog?.(e,L),...o.abortSignal!==void 0?{abortSignal:o.abortSignal}:{}}).catch(e=>{M.debug("SingleComponentDeploy","Forget completion sweep failed",{error:N(J(e))})});const F=P.data?.status==="NO_CHANGES"&&o.options?.force!==!0;return y({target:d.target,deploymentType:"organisation",outputs:h,artefacts:[],...F?{noChanges:!0}:{},durationMs:Date.now()-D})}export{At as deploySingleComponent};
|