@fjall/deploy-core 2.25.0 → 2.27.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/.minified +1 -1
- package/dist/src/aws/index.d.ts +1 -1
- package/dist/src/aws/index.js +1 -1
- package/dist/src/aws/organisations/index.d.ts +1 -1
- package/dist/src/aws/organisations/index.js +1 -1
- package/dist/src/aws/organisations/organisationalUnits.d.ts +15 -0
- package/dist/src/aws/organisations/organisationalUnits.js +1 -1
- package/dist/src/index.d.ts +2 -2
- package/dist/src/index.js +1 -1
- package/dist/src/orchestration/application/applicationDeploy.js +1 -1
- package/dist/src/orchestration/application/databaseEndpointReconcile.d.ts +77 -0
- package/dist/src/orchestration/application/databaseEndpointReconcile.js +1 -0
- package/dist/src/orchestration/application/deploySessionPolicy.d.ts +9 -1
- package/dist/src/orchestration/application/deploySessionPolicy.js +1 -1
- package/dist/src/orchestration/application/dockerBuildHelper.js +1 -1
- package/dist/src/orchestration/application/ecrCacheRepository.d.ts +14 -0
- package/dist/src/orchestration/application/ecrCacheRepository.js +1 -0
- package/dist/src/orchestration/organisationDeploy/orgCascadeDeploy.js +3 -3
- package/dist/src/orchestration/organisationDeploy/orgContext.d.ts +7 -0
- package/dist/src/orchestration/organisationDeploy/orgContext.js +1 -1
- package/dist/src/services/infrastructure/CdkArgumentBuilder.js +1 -1
- package/dist/src/services/infrastructure/CdkServiceTypes.d.ts +7 -0
- package/dist/src/services/infrastructure/cdkServiceHelpers.js +1 -1
- package/dist/src/services/supporting/CdkContextBuilder.d.ts +1 -0
- package/dist/src/services/supporting/CdkContextBuilder.js +1 -1
- package/dist/src/types/deployment/DeploymentTypes.d.ts +1 -0
- package/dist/src/types/index.d.ts +1 -1
- package/dist/src/types/params.d.ts +19 -0
- package/package.json +4 -4
package/dist/.minified
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
185 files minified at 2026-07-07T01:04:28.789Z
|
package/dist/src/aws/index.d.ts
CHANGED
|
@@ -6,7 +6,7 @@ export { buildTargetSetUnreadyAdvisory } from "./targetSetAdvisory.js";
|
|
|
6
6
|
export type { TargetSetUnreadyAdvisoryParams } from "./targetSetAdvisory.js";
|
|
7
7
|
export { checkRegionEnabled, regionDisabledMessage } from "./regions/regionOptStatus.js";
|
|
8
8
|
export type { RegionAvailability } from "./regions/regionOptStatus.js";
|
|
9
|
-
export { ensureOrganisationExists, describeOrganisation, enablePolicyTypes, enableServiceAccess, SERVICE_PRINCIPALS, enableRamSharing, activateTrustedAccess, enableIpamDelegatedAdmin, updateBackupGlobalSettings, listAccounts, findAccount, createAccount, ensureOrganisationalUnitsExist, placeAccountsInOUs, buildAccountToOUMap, activateCostAllocationTags, checkIdentityCentreStatus, snapshotIdentityCentre, composeSdkAbortSignal, extractErrorName, isAborted, isOULeaf, DEFAULT_MAX_PAGES, IDENTITY_STORE_PAGE_SIZE, drainPages, registerSecurityDelegates, SECURITY_SERVICE_PRINCIPALS, enableCentralisedRootAccess, ROOT_ACCESS_FEATURES, RootAccessEnablementError } from "./organisations/index.js";
|
|
9
|
+
export { ensureOrganisationExists, describeOrganisation, enablePolicyTypes, enableServiceAccess, SERVICE_PRINCIPALS, enableRamSharing, activateTrustedAccess, enableIpamDelegatedAdmin, updateBackupGlobalSettings, listAccounts, findAccount, createAccount, ensureOrganisationalUnitsExist, placeAccountsInOUs, buildAccountToOUMap, findDevelopmentOuId, activateCostAllocationTags, checkIdentityCentreStatus, snapshotIdentityCentre, composeSdkAbortSignal, extractErrorName, isAborted, isOULeaf, DEFAULT_MAX_PAGES, IDENTITY_STORE_PAGE_SIZE, drainPages, registerSecurityDelegates, SECURITY_SERVICE_PRINCIPALS, enableCentralisedRootAccess, ROOT_ACCESS_FEATURES, RootAccessEnablementError } from "./organisations/index.js";
|
|
10
10
|
export type { OrgDetails, OUMap, OUTree, AccountPlacementResult, AccountInfo, DrainPagesOptions, PagedResult, IdentityCentreStatus, IdentityCentreSnapshot, IdentityCentreSnapshotGroup, IdentityCentreSnapshotOptions, IdentityCentreSnapshotResult, IdentityCentreSnapshotUser, BackupGlobalSettings, CostAllocationTag, CreateAccountResult, RootAccessFeature, RootAccessEnablementSummary } from "./organisations/index.js";
|
|
11
11
|
export { CloudFormationEventMonitor } from "./utils/cloudformationEvents.js";
|
|
12
12
|
export type { AwsClientLike, EventLogWriter, EventFailureAnalyser, EventLogWriterFactory, EventMonitorDeps } from "./utils/cloudformationEvents.js";
|
package/dist/src/aws/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{SimpleAwsProvider as a}from"./SimpleAwsProvider.js";import{checkTargetReadiness as
|
|
1
|
+
import{SimpleAwsProvider as a}from"./SimpleAwsProvider.js";import{checkTargetReadiness as o,describeTargetReadinessReason as s,buildTargetUnreadyAdvisory as n,probeAccountStackExists as i}from"./targetReadiness.js";import{buildTargetSetUnreadyAdvisory as d}from"./targetSetAdvisory.js";import{checkRegionEnabled as A,regionDisabledMessage as E}from"./regions/regionOptStatus.js";import{ensureOrganisationExists as g,describeOrganisation as u,enablePolicyTypes as b,enableServiceAccess as p,SERVICE_PRINCIPALS as m,enableRamSharing as I,activateTrustedAccess as R,enableIpamDelegatedAdmin as T,updateBackupGlobalSettings as C,listAccounts as x,findAccount as O,createAccount as _,ensureOrganisationalUnitsExist as f,placeAccountsInOUs as y,buildAccountToOUMap as P,findDevelopmentOuId as U,activateCostAllocationTags as v,checkIdentityCentreStatus as k,snapshotIdentityCentre as D,composeSdkAbortSignal as h,extractErrorName as L,isAborted as M,isOULeaf as N,DEFAULT_MAX_PAGES as F,IDENTITY_STORE_PAGE_SIZE as G,drainPages as V,registerSecurityDelegates as Y,SECURITY_SERVICE_PRINCIPALS as w,enableCentralisedRootAccess as B,ROOT_ACCESS_FEATURES as X,RootAccessEnablementError as Z}from"./organisations/index.js";import{CloudFormationEventMonitor as q}from"./utils/cloudformationEvents.js";export{q as CloudFormationEventMonitor,F as DEFAULT_MAX_PAGES,G as IDENTITY_STORE_PAGE_SIZE,X as ROOT_ACCESS_FEATURES,Z as RootAccessEnablementError,w as SECURITY_SERVICE_PRINCIPALS,m as SERVICE_PRINCIPALS,a as SimpleAwsProvider,v as activateCostAllocationTags,R as activateTrustedAccess,P as buildAccountToOUMap,d as buildTargetSetUnreadyAdvisory,n as buildTargetUnreadyAdvisory,k as checkIdentityCentreStatus,A as checkRegionEnabled,o as checkTargetReadiness,h as composeSdkAbortSignal,_ as createAccount,u as describeOrganisation,s as describeTargetReadinessReason,V as drainPages,B as enableCentralisedRootAccess,T as enableIpamDelegatedAdmin,b as enablePolicyTypes,I as enableRamSharing,p as enableServiceAccess,g as ensureOrganisationExists,f as ensureOrganisationalUnitsExist,L as extractErrorName,O as findAccount,U as findDevelopmentOuId,M as isAborted,N as isOULeaf,x as listAccounts,y as placeAccountsInOUs,i as probeAccountStackExists,E as regionDisabledMessage,Y as registerSecurityDelegates,D as snapshotIdentityCentre,C as updateBackupGlobalSettings};
|
|
@@ -14,7 +14,7 @@ export { activateTrustedAccess } from "./trustedAccess.js";
|
|
|
14
14
|
export { enableIpamDelegatedAdmin } from "./ipam.js";
|
|
15
15
|
export { updateBackupGlobalSettings } from "./backup.js";
|
|
16
16
|
export { listAccounts, findAccount, createAccount } from "./accounts.js";
|
|
17
|
-
export { ensureOrganisationalUnitsExist, placeAccountsInOUs, buildAccountToOUMap } from "./organisationalUnits.js";
|
|
17
|
+
export { ensureOrganisationalUnitsExist, placeAccountsInOUs, buildAccountToOUMap, findDevelopmentOuId } from "./organisationalUnits.js";
|
|
18
18
|
export { activateCostAllocationTags } from "./costAllocation.js";
|
|
19
19
|
export { checkIdentityCentreStatus } from "./identityCentre.js";
|
|
20
20
|
export { snapshotIdentityCentre } from "./identityCentreSnapshot.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{composeSdkAbortSignal as
|
|
1
|
+
import{composeSdkAbortSignal as o,extractErrorName as r,isAborted as a,isOULeaf as n}from"./types.js";import{DEFAULT_MAX_PAGES as c,IDENTITY_STORE_PAGE_SIZE as i,drainPages as p}from"./pagination.js";import{ensureOrganisationExists as A,describeOrganisation as l}from"./organisation.js";import{enablePolicyTypes as x}from"./policies.js";import{enableServiceAccess as S,SERVICE_PRINCIPALS as d}from"./serviceAccess.js";import{enableRamSharing as u}from"./ram.js";import{activateTrustedAccess as g}from"./trustedAccess.js";import{enableIpamDelegatedAdmin as R}from"./ipam.js";import{updateBackupGlobalSettings as O}from"./backup.js";import{listAccounts as P,findAccount as U,createAccount as y}from"./accounts.js";import{ensureOrganisationalUnitsExist as v,placeAccountsInOUs as L,buildAccountToOUMap as N,findDevelopmentOuId as h}from"./organisationalUnits.js";import{activateCostAllocationTags as G}from"./costAllocation.js";import{checkIdentityCentreStatus as M}from"./identityCentre.js";import{snapshotIdentityCentre as Y}from"./identityCentreSnapshot.js";import{registerSecurityDelegates as X,SECURITY_SERVICE_PRINCIPALS as Z}from"./delegatedAdmin.js";import{enableCentralisedRootAccess as q,ROOT_ACCESS_FEATURES as w,RootAccessEnablementError as z}from"./rootAccess.js";export{c as DEFAULT_MAX_PAGES,i as IDENTITY_STORE_PAGE_SIZE,w as ROOT_ACCESS_FEATURES,z as RootAccessEnablementError,Z as SECURITY_SERVICE_PRINCIPALS,d as SERVICE_PRINCIPALS,G as activateCostAllocationTags,g as activateTrustedAccess,N as buildAccountToOUMap,M as checkIdentityCentreStatus,o as composeSdkAbortSignal,y as createAccount,l as describeOrganisation,p as drainPages,q as enableCentralisedRootAccess,R as enableIpamDelegatedAdmin,x as enablePolicyTypes,u as enableRamSharing,S as enableServiceAccess,A as ensureOrganisationExists,v as ensureOrganisationalUnitsExist,r as extractErrorName,U as findAccount,h as findDevelopmentOuId,a as isAborted,n as isOULeaf,P as listAccounts,L as placeAccountsInOUs,X as registerSecurityDelegates,Y as snapshotIdentityCentre,O as updateBackupGlobalSettings};
|
|
@@ -37,3 +37,18 @@ export declare function buildAccountToOUMap(tree: OUTree, ouMap: OUMap, prefix?:
|
|
|
37
37
|
* Idempotent — accounts already in the correct OU are counted but not moved.
|
|
38
38
|
*/
|
|
39
39
|
export declare function placeAccountsInOUs(client: OrganizationsClient, ouMap: OUMap, accounts: AccountInfo[], accountToOU?: Record<string, string>, abortSignal?: AbortSignal): Promise<Result<AccountPlacementResult>>;
|
|
40
|
+
/**
|
|
41
|
+
* Find the id of the organisational unit named "development" (case-insensitive),
|
|
42
|
+
* searching breadth-first from the root up to DEV_OU_SEARCH_MAX_DEPTH levels.
|
|
43
|
+
* Supports both OU shapes ensureOrganisationalUnitsExist creates: flat
|
|
44
|
+
* (development at root) and nested (workloads.development).
|
|
45
|
+
*
|
|
46
|
+
* Collects EVERY match rather than returning the shallowest one: a flat→nested
|
|
47
|
+
* migration leaves a stray empty "development" OU at the root alongside the
|
|
48
|
+
* real nested one, and silently attaching the dev-isolation SCP to the stray
|
|
49
|
+
* would isolate nothing. Exactly one match → its id; zero → `undefined` (the
|
|
50
|
+
* org has no development OU — the caller treats that as "nothing to isolate",
|
|
51
|
+
* not an error); two or more → a loud failure naming the ids so the operator
|
|
52
|
+
* disambiguates, because a security backstop must never attach to the wrong OU.
|
|
53
|
+
*/
|
|
54
|
+
export declare function findDevelopmentOuId(client: OrganizationsClient, rootId: string, abortSignal?: AbortSignal): Promise<Result<string | undefined>>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{CreateOrganizationalUnitCommand as
|
|
1
|
+
import{CreateOrganizationalUnitCommand as x,ListOrganizationalUnitsForParentCommand as C,ListParentsCommand as b,MoveAccountCommand as _}from"@aws-sdk/client-organizations";import{success as f,failure as u}from"@fjall/generator";import{extractErrorName as N,isOULeaf as A,composeSdkAbortSignal as m,isAborted as p,AWS_ERROR_NAMES as E}from"./types.js";import{accountTier as v,getErrorMessage as y}from"@fjall/util";async function U(o,s,t){const e=await o.send(new C({ParentId:s}),{abortSignal:m(t)});let n=e.OrganizationalUnits??[],a=e.NextToken;for(;a;){if(p(t))throw new Error("Aborted: OU listing cancelled by shutdown signal");const r=await o.send(new C({ParentId:s,NextToken:a}),{abortSignal:m(t)});n=n.concat(r.OrganizationalUnits??[]),a=r.NextToken}return n}async function F(o,s,t){try{return(await o.send(new b({ChildId:s}),{abortSignal:m(t)})).Parents?.[0]?.Id}catch(e){if(N(e)===E.CHILD_NOT_FOUND)return;throw e}}async function L(o,s,t,e,n){const a=e.find(r=>r.Name===t);if(a?.Id)return f(a.Id);try{const c=(await o.send(new x({Name:t,ParentId:s}),{abortSignal:m(n)})).OrganizationalUnit;return c?.Id?f(c.Id):u(new Error(`OU "${t}" was created but has no ID`))}catch(r){return u(new Error(`Failed to create OU "${t}": ${y(r)}`))}}function P(o){return o.charAt(0).toUpperCase()+o.slice(1)}async function D(o,s,t,e){const n={};if(t.length===0)return f(n);const a=await U(o,s,e);for(const r of t){const c=P(r),i=await L(o,s,c,a,e);if(!i.success)return u(i.error);n[r.toLowerCase()]=i.data,a.push({Id:i.data,Name:c})}return f(n)}async function $(o,s,t,e,n,a,r){const c=await U(o,s,r);for(const[i,d]of Object.entries(t)){const l=P(i),h=await L(o,s,l,c,r);if(!h.success)return u(h.error);const O=h.data,g=n?`${n}.${i.toLowerCase()}`:i.toLowerCase();if(e[g]=O,n){const w=i.toLowerCase();a.has(w)||(e[w]=O)}if(c.push({Id:O,Name:l}),!A(d)){const w=await $(o,O,d,e,g,a,r);if(!w.success)return w}}return f(void 0)}async function H(o,s,t,e){if(p(e))return u(new Error("Aborted: OU reconciliation cancelled by shutdown signal"));try{if(Array.isArray(t))return await D(o,s,t,e);const n={},a=new Set(Object.keys(t).map(c=>c.toLowerCase())),r=await $(o,s,t,n,"",a,e);return r.success?f(n):u(r.error)}catch(n){return u(new Error(`Failed to ensure OUs exist: ${y(n)}`))}}function T(o,s,t=""){const e={};for(const[n,a]of Object.entries(o)){const r=t?`${t}.${n.toLowerCase()}`:n.toLowerCase(),c=s[r];if(A(a)){if(c)for(const i of a)e[i.toLowerCase()]=c}else Object.assign(e,T(a,s,r))}return e}async function S(o,s,t,e,n){try{if(t.length===0)return f({moved:0,alreadyPlaced:0});let a=0,r=0;for(const c of t){if(p(n))return u(new Error("Aborted: account placement cancelled by shutdown signal"));if(v(c)==="organisation")continue;const i=e?e[c.name.toLowerCase()]:s[c.environment.toLowerCase()];if(!i)continue;const d=await F(o,c.id,n);if(d){if(d===i){r++;continue}try{await o.send(new _({AccountId:c.id,SourceParentId:d,DestinationParentId:i}),{abortSignal:m(n)}),a++}catch(l){if(N(l)===E.ACCOUNT_NOT_FOUND)continue;throw l}}}return f({moved:a,alreadyPlaced:r})}catch(a){return u(new Error(`Failed to place accounts in OUs: ${y(a)}`))}}const I="development",k=3;async function K(o,s,t){try{const e=[];let n=[s];for(let a=0;a<k;a++){const r=[];for(const c of n){if(p(t))return u(new Error("Aborted: development-OU lookup cancelled by shutdown signal"));const i=await U(o,c,t);for(const d of i)if(d.Id!==void 0){if(d.Name?.toLowerCase()===I){e.push(d.Id);continue}r.push(d.Id)}}if(r.length===0)break;n=r}return e.length===0?f(void 0):e.length>1?u(new Error(`Found ${e.length} organisational units named "${I}" (${e.join(", ")}); cannot safely target the dev-isolation SCP. A flat\u2192nested migration can leave a stray empty "${I}" OU at the root \u2014 remove the duplicate so exactly one remains.`)):f(e[0])}catch(e){return u(new Error(`Failed to locate development OU: ${y(e)}`))}}export{T as buildAccountToOUMap,H as ensureOrganisationalUnitsExist,K as findDevelopmentOuId,S as placeAccountsInOUs};
|
package/dist/src/index.d.ts
CHANGED
|
@@ -15,14 +15,14 @@ export { DeploymentEventSchema, DEPLOYMENT_EVENT_TYPES, DEPLOYMENT_EVENT_RESOURC
|
|
|
15
15
|
export type { DeploymentEvent, DeploymentEventType, DeploymentEventResourceCategory, DeploymentEventCascadePhase, DeploymentEventCascadeAccountStatus } from "./types/index.js";
|
|
16
16
|
export { DeployEventSchema, StepProgressSchema, DeployResourceSchema, PlanChangeSchema, PlanPropertyChangeSchema, ChangeCountsSchema, DEPLOY_EVENT_CONTRACT, DEPLOY_EVENT_CONTRACT_MAJOR, DEPLOY_EVENT_CONTRACT_MINOR, SUPPORTED_CONTRACT_MARKERS, DEPLOY_EVENT_ID_MAX, DEPLOY_EVENT_NAME_MAX, DEPLOY_EVENT_MESSAGE_MAX, DEPLOY_EVENT_STATUS_REASON_MAX, DEPLOY_EVENT_ERROR_MESSAGE_MAX, DEPLOY_EVENT_TRAIL_DETAIL_MAX, DEPLOY_EVENT_OUTPUT_MAX, DEPLOY_EVENT_ARN_MAX, DEPLOY_EVENT_URL_MAX, STEP_COMPLETE_STATUSES, LOG_LEVELS, STACK_CLEANUP_PHASES, CASCADE_ACCOUNT_PHASES, DEPLOY_RESULTS, DETECTION_PHASES, PHASE_STATUSES, BUILDERS, CASCADE_OUTCOME_RESULTS, PLAN_ACTIONS, REPLACEMENT_MODES, APPROVAL_KINDS, APPROVAL_DECISIONS, ALL_PROGRESS_KINDS, createDeployEmitter, parseDeployEvent, parseDeployContractVersion } from "./types/index.js";
|
|
17
17
|
export type { DeployEvent, ProgressKind, StepProgress, DeployResource, PlanChange, PlanPropertyChange, ChangeCounts, PlanAction, ReplacementMode, ApprovalKind, ApprovalDecision, DeployRunResult, DeployEmitter, CreateDeployEmitterOptions, ParsedDeployEvent, StackCleanupPhase, CascadeAccountPhase, LogLevel } from "./types/index.js";
|
|
18
|
-
export type { AwsCredentials, DeployIdentity, ScopedBuildSecretSessionMinter, DeployCallbacks, StepCompleteStatus, StackCleanupQuarantineDetail, StackCleanupRetainedBucketsDetail, ProgressEvent, ProgressEventType, ResourceEvent, AwsAuthResult, CascadeDeploymentResult, CascadePhase, BuildPushStartEvent, BuildPushProgressEvent, BuildPushCompleteEvent, TaskDefRegisteredEvent, ECSCompleteEvent, MigrationsStartEvent, MigrationsCompleteEvent, TrailMigrationPhase, TrailMigrationStatus, TrailMigrationPhaseEvent, ApiClientInterface, EntitlementsData, DeployParams, DeployOptions, DeploymentType, DeployResult, DestroyParams, DestroyOptions, DestroyResult, ApprovalGate, ApprovalGatePlanDetail, ApprovalRequest, GateResolution, OrgConfig, ProviderAccount, RootAccessManagementMode, SSOSession, Entitlements } from "./types/index.js";
|
|
18
|
+
export type { AwsCredentials, DeployIdentity, ScopedBuildSecretSessionMinter, DeployCallbacks, StepCompleteStatus, StackCleanupQuarantineDetail, StackCleanupRetainedBucketsDetail, ProgressEvent, ProgressEventType, ResourceEvent, AwsAuthResult, CascadeDeploymentResult, CascadePhase, BuildPushStartEvent, BuildPushProgressEvent, BuildPushCompleteEvent, TaskDefRegisteredEvent, ECSCompleteEvent, MigrationsStartEvent, MigrationsCompleteEvent, TrailMigrationPhase, TrailMigrationStatus, TrailMigrationPhaseEvent, ApiClientInterface, EntitlementsData, DeployParams, DeployOptions, DatabaseEndpointRestartRequest, DeploymentType, DeployResult, DestroyParams, DestroyOptions, DestroyResult, ApprovalGate, ApprovalGatePlanDetail, ApprovalRequest, GateResolution, OrgConfig, ProviderAccount, RootAccessManagementMode, SSOSession, Entitlements } from "./types/index.js";
|
|
19
19
|
export type { AwsProvider, AwsProviderCredentials, AwsSdkClientConstructor } from "./aws/index.js";
|
|
20
20
|
export { SimpleAwsProvider } from "./aws/index.js";
|
|
21
21
|
export { checkTargetReadiness, describeTargetReadinessReason, buildTargetUnreadyAdvisory, buildTargetSetUnreadyAdvisory, probeAccountStackExists } from "./aws/index.js";
|
|
22
22
|
export type { TargetReadinessAccount, TargetReadinessClients, TargetReadinessReason, TargetReadinessVerdict, TargetSetUnreadyAdvisoryParams } from "./aws/index.js";
|
|
23
23
|
export { checkRegionEnabled, regionDisabledMessage } from "./aws/index.js";
|
|
24
24
|
export type { RegionAvailability } from "./aws/index.js";
|
|
25
|
-
export { ensureOrganisationExists, describeOrganisation, enablePolicyTypes, enableServiceAccess, SERVICE_PRINCIPALS, enableRamSharing, activateTrustedAccess, enableIpamDelegatedAdmin, updateBackupGlobalSettings, listAccounts, findAccount, createAccount, ensureOrganisationalUnitsExist, placeAccountsInOUs, buildAccountToOUMap, activateCostAllocationTags, checkIdentityCentreStatus, snapshotIdentityCentre, composeSdkAbortSignal, extractErrorName, isAborted, DEFAULT_MAX_PAGES, IDENTITY_STORE_PAGE_SIZE, drainPages, isOULeaf, registerSecurityDelegates, SECURITY_SERVICE_PRINCIPALS, enableCentralisedRootAccess, ROOT_ACCESS_FEATURES, RootAccessEnablementError } from "./aws/index.js";
|
|
25
|
+
export { ensureOrganisationExists, describeOrganisation, enablePolicyTypes, enableServiceAccess, SERVICE_PRINCIPALS, enableRamSharing, activateTrustedAccess, enableIpamDelegatedAdmin, updateBackupGlobalSettings, listAccounts, findAccount, createAccount, ensureOrganisationalUnitsExist, placeAccountsInOUs, buildAccountToOUMap, findDevelopmentOuId, activateCostAllocationTags, checkIdentityCentreStatus, snapshotIdentityCentre, composeSdkAbortSignal, extractErrorName, isAborted, DEFAULT_MAX_PAGES, IDENTITY_STORE_PAGE_SIZE, drainPages, isOULeaf, registerSecurityDelegates, SECURITY_SERVICE_PRINCIPALS, enableCentralisedRootAccess, ROOT_ACCESS_FEATURES, RootAccessEnablementError } from "./aws/index.js";
|
|
26
26
|
export type { OrgDetails, OUMap, OUTree, AccountPlacementResult, AccountInfo, DrainPagesOptions, PagedResult, IdentityCentreStatus, IdentityCentreSnapshot, IdentityCentreSnapshotGroup, IdentityCentreSnapshotOptions, IdentityCentreSnapshotResult, IdentityCentreSnapshotUser, BackupGlobalSettings, CostAllocationTag, CreateAccountResult, RootAccessFeature, RootAccessEnablementSummary } from "./aws/index.js";
|
|
27
27
|
export { STEP_IDS, STEP_NAMES, INFRASTRUCTURE_STEP_NAMES, INFRA_STEP_NAME } from "./types/index.js";
|
|
28
28
|
export type { StepId, InfrastructureStepName, StepDefinition, StepDeploymentType, Operation, StepContext } from "./types/index.js";
|
package/dist/src/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{DeploymentEventSchema as t,DEPLOYMENT_EVENT_TYPES as o,DEPLOYMENT_EVENT_RESOURCE_CATEGORIES as a,DEPLOYMENT_EVENT_STATUS_REASON_MAX as E,DEPLOYMENT_EVENT_ERROR_MESSAGE_MAX as i,DEPLOYMENT_EVENT_TRAIL_DETAIL_MAX as s,CASCADE_PHASES as S,CASCADE_ACCOUNT_STATUSES as n}from"./types/index.js";import{DeployEventSchema as A,StepProgressSchema as T,DeployResourceSchema as _,PlanChangeSchema as l,PlanPropertyChangeSchema as p,ChangeCountsSchema as O,DEPLOY_EVENT_CONTRACT as P,DEPLOY_EVENT_CONTRACT_MAJOR as R,DEPLOY_EVENT_CONTRACT_MINOR as u,SUPPORTED_CONTRACT_MARKERS as m,DEPLOY_EVENT_ID_MAX as d,DEPLOY_EVENT_NAME_MAX as C,DEPLOY_EVENT_MESSAGE_MAX as N,DEPLOY_EVENT_STATUS_REASON_MAX as D,DEPLOY_EVENT_ERROR_MESSAGE_MAX as L,DEPLOY_EVENT_TRAIL_DETAIL_MAX as f,DEPLOY_EVENT_OUTPUT_MAX as g,DEPLOY_EVENT_ARN_MAX as I,DEPLOY_EVENT_URL_MAX as x,STEP_COMPLETE_STATUSES as y,LOG_LEVELS as U,STACK_CLEANUP_PHASES as M,CASCADE_ACCOUNT_PHASES as v,DEPLOY_RESULTS as k,DETECTION_PHASES as Y,PHASE_STATUSES as h,BUILDERS as b,CASCADE_OUTCOME_RESULTS as V,PLAN_ACTIONS as F,REPLACEMENT_MODES as B,APPROVAL_KINDS as G,APPROVAL_DECISIONS as K,ALL_PROGRESS_KINDS as X,createDeployEmitter as H,parseDeployEvent as w,parseDeployContractVersion as j}from"./types/index.js";import{SimpleAwsProvider as q}from"./aws/index.js";import{checkTargetReadiness as W,describeTargetReadinessReason as Z,buildTargetUnreadyAdvisory as z,buildTargetSetUnreadyAdvisory as $,probeAccountStackExists as ee}from"./aws/index.js";import{checkRegionEnabled as te,regionDisabledMessage as oe}from"./aws/index.js";import{ensureOrganisationExists as Ee,describeOrganisation as ie,enablePolicyTypes as se,enableServiceAccess as Se,SERVICE_PRINCIPALS as ne,enableRamSharing as ce,activateTrustedAccess as Ae,enableIpamDelegatedAdmin as Te,updateBackupGlobalSettings as _e,listAccounts as le,findAccount as pe,createAccount as Oe,ensureOrganisationalUnitsExist as Pe,placeAccountsInOUs as Re,buildAccountToOUMap as ue,activateCostAllocationTags as me,checkIdentityCentreStatus as de,snapshotIdentityCentre as Ce,composeSdkAbortSignal as Ne,extractErrorName as De,isAborted as Le,DEFAULT_MAX_PAGES as fe,IDENTITY_STORE_PAGE_SIZE as ge,drainPages as Ie,isOULeaf as xe,registerSecurityDelegates as ye,SECURITY_SERVICE_PRINCIPALS as Ue,enableCentralisedRootAccess as Me,ROOT_ACCESS_FEATURES as ve,RootAccessEnablementError as ke}from"./aws/index.js";import{STEP_IDS as he,STEP_NAMES as be,INFRASTRUCTURE_STEP_NAMES as Ve,INFRA_STEP_NAME as Fe}from"./types/index.js";import{ProgressReporter as Ge,APPLICATION_STACKS as Ke,ORGANISATION_TYPES as Xe,APPLICATION_DEPLOY_ORDER as He,APPLICATION_DESTROY_ORDER as we,OPENNEXT_DEPLOY_ORDER as je,OPENNEXT_DESTROY_ORDER as Qe,PARALLEL_DEPLOY_GROUPS as qe,PARALLEL_DESTROY_GROUPS as Je,OPENNEXT_PARALLEL_GROUPS as We,PARALLEL_OPERATION_TYPES as Ze,isApplicationOperation as ze,isOrganisationOperation as $e,getParallelDeployGroups as er,getParallelDestroyGroups as rr,getApplicationDeployOrder as tr,getApplicationDestroyOrder as or,getApplicationStackName as ar,getOrganisationStackName as Er,isApplicationStack as ir,isQuarantineDetail as sr,isRetainedBucketsDetail as Sr,getApplicationStepName as nr,getApplicationStepId as cr,toPascalCase as Ar,isOpenNextPattern as Tr,OPENNEXT_PATTERNS as _r,deriveResourcesFromManifestStacks as lr,STACK_NOT_FOUND_PATTERN as pr,STACK_FAILED_STATE_PATTERN as Or,CDK_NO_STACKS_MATCH as Pr,INFRASTRUCTURE_FILENAME as Rr,ApplicationError as ur,wrapApplicationError as mr,FjallStateFileSchema as dr,readStateFile as Cr,writeStateFile as Nr,createEmptyState as Dr,deleteStateFile as Lr,updateTemplateHash as fr,getStateFilePath as gr,stubCallerIdentity as Ir}from"./types/index.js";import{CloudFormationEventMonitor as yr}from"./aws/index.js";import{CdkService as Mr,CdkArgumentBuilder as vr,CdkProcessManager as kr,CdkEventMonitor as Yr,startStackMonitoring as hr,DEFAULT_DEPLOY_TIMEOUT_MS as br,isCdkError as Vr,formatInfrastructureError as Fr,getStructuralHint as Br,getSourceContext as Gr,hasCdkDifferences as Kr,parseDiffOutput as Xr,CloudFormationService as Hr,CloudFormationError as wr,EcsService as jr,EcsError as Qr,EcsServiceResolver as qr,TemplateHashService as Jr,TemplateHashError as Wr,CdkContextBuilder as Zr,emitProgress as zr,PROGRESS_MESSAGES as $r,parseBuildPhase as et,buildStepContextBuildConfig as rt,convertCloudFormationOutputsToRecord as tt,ApplicationStackService as ot}from"./services/index.js";import{CdkError as Et}from"./types/errors/index.js";import{BaseServiceError as st,ValidationError as St,AuthError as nt,AwsError as ct,DeploymentError as At,NetworkError as Tt,FileSystemError as _t,ConfigError as lt,toServiceError as pt}from"./types/errors/index.js";import{filterDangerousEnvVars as Pt,maskSensitiveOutput as Rt,parseShellArgs as ut,sleep as mt}from"@fjall/util";import{artefactOutputKey as Ct,DeployModeSchema as Nt,ServiceArtefactSchema as Dt,ServiceArtefactsSchema as Lt}from"@fjall/util";import{hasDockerfile as gt}from"./util/dockerfileDetection.js";import{sleepAbortable as xt}from"./util/sleepAbortable.js";import{createSequencedCallbacks as Ut}from"./util/sequencedCallbacks.js";import{fileExists as vt}from"@fjall/util/fsHelpers";import{success as Yt,failure as ht,isSuccess as bt,isFailure as Vt}from"@fjall/generator";import{deploy as Bt}from"./orchestration/index.js";import{destroy as Kt}from"./orchestration/index.js";import{restart as Ht,probeSecretsDrift as wt,computeSecretsDrift as jt,deriveStaleParameters as Qt,extractConsumedSsmParameters as qt}from"./orchestration/index.js";import{partitionAccounts as Wt}from"./orchestration/index.js";import{buildRegionList as zt,buildAccountRegionPairs as $t,cascadeHomeRegion as eo,cascadeOperationKey as ro}from"./orchestration/index.js";import{projectScalarSummary as oo,projectAccountRows as ao}from"./orchestration/index.js";import{reconcileProviderAccounts as io,mergeReconciledProviderAccounts as so}from"./orchestration/index.js";import{decideNextTransition as no,reconcileTrailMigration as co,decommissionMemberTrailStorage as Ao,ORG_TRAIL_BUCKET_OUTPUT_KEY as To,TRAIL_BUCKET_OUTPUT_KEY as _o,TRAIL_KEY_ARN_OUTPUT_KEY as lo}from"./orchestration/index.js";import{unlockBucket as Oo,unlockQueue as Po}from"./orchestration/index.js";import{triageBucketPolicy as uo,isEnforceSslStatement as mo,toResourcePolicyStatements as Co,restoreBucketPolicy as No,synthesiseEnforceSslDocument as Do,ensureEnforceSsl as Lo,restoreAndReconcileQuarantinedBucket as fo}from"./orchestration/index.js";import{verifyOrgTrailDelivery as Io}from"./aws/cloudtrail/orgTrailDelivery.js";import{assumeRootForTask as yo,isRootTaskPolicyArn as Uo,ROOT_TASK_POLICY_ARNS as Mo,MAX_ROOT_SESSION_SECONDS as vo}from"./aws/sts/assumeRoot.js";import{parseAccountsConfiguration as Yo,flattenAccountsToEnvironments as ho,extractAllAccountNames as bo,accountsConfigToOUTree as Vo,isStringArray as Fo,isAccountsConfig as Bo,isOuOnlyAccountBucket as Go,OU_ONLY_ACCOUNT_BUCKETS as Ko}from"./orchestration/index.js";import{runOpenNextBuild as Ho}from"./orchestration/index.js";import{runOrganisationSetup as jo,ORG_SETUP_PHASES as Qo}from"./orchestration/index.js";import{resolveBuildSecrets as Jo,resolveSecretRefValue as Wo,resolveBuildSecretSessionProvider as Zo,sourcedRefsFromBuildArgs as zo,resolveBuildArgs as $o,buildBuildSecretSessionPolicy as ea,buildDeploySessionPolicy as ra,partitionForRegion as ta,validateBuildGroup as oa}from"./orchestration/index.js";import{FrameworkRegistry as Ea}from"./orchestration/index.js";import{openNextBuilder as sa,dockerBuilder as Sa}from"./orchestration/index.js";import{StepRegistry as ca,getDestroyStepId as Aa}from"./steps/index.js";import{buildDeployPlan as _a,computeDeployPlan as la,computeAssemblyDigest as pa,digestHead as Oa,classifyImpact as Pa,derivePropertyChanges as Ra,isDataLoss as ua,isStatefulResourceType as ma,renderPlanSummary as da,renderPlanLines as Ca,toWirePlanChanges as Na,signApprovalToken as Da,verifyApprovalToken as La,DEFAULT_APPROVAL_TTL_MS as fa,APPROVAL_TOKEN_PATTERN as ga,runApprovalGate as Ia}from"./orchestration/index.js";export{X as ALL_PROGRESS_KINDS,He as APPLICATION_DEPLOY_ORDER,we as APPLICATION_DESTROY_ORDER,Ke as APPLICATION_STACKS,K as APPROVAL_DECISIONS,G as APPROVAL_KINDS,ga as APPROVAL_TOKEN_PATTERN,ur as ApplicationError,ot as ApplicationStackService,nt as AuthError,ct as AwsError,b as BUILDERS,st as BaseServiceError,v as CASCADE_ACCOUNT_PHASES,n as CASCADE_ACCOUNT_STATUSES,V as CASCADE_OUTCOME_RESULTS,S as CASCADE_PHASES,Pr as CDK_NO_STACKS_MATCH,vr as CdkArgumentBuilder,Zr as CdkContextBuilder,Et as CdkError,Yr as CdkEventMonitor,kr as CdkProcessManager,Mr as CdkService,O as ChangeCountsSchema,wr as CloudFormationError,yr as CloudFormationEventMonitor,Hr as CloudFormationService,lt as ConfigError,fa as DEFAULT_APPROVAL_TTL_MS,br as DEFAULT_DEPLOY_TIMEOUT_MS,fe as DEFAULT_MAX_PAGES,i as DEPLOYMENT_EVENT_ERROR_MESSAGE_MAX,a as DEPLOYMENT_EVENT_RESOURCE_CATEGORIES,E as DEPLOYMENT_EVENT_STATUS_REASON_MAX,s as DEPLOYMENT_EVENT_TRAIL_DETAIL_MAX,o as DEPLOYMENT_EVENT_TYPES,I as DEPLOY_EVENT_ARN_MAX,P as DEPLOY_EVENT_CONTRACT,R as DEPLOY_EVENT_CONTRACT_MAJOR,u as DEPLOY_EVENT_CONTRACT_MINOR,L as DEPLOY_EVENT_ERROR_MESSAGE_MAX,d as DEPLOY_EVENT_ID_MAX,N as DEPLOY_EVENT_MESSAGE_MAX,C as DEPLOY_EVENT_NAME_MAX,g as DEPLOY_EVENT_OUTPUT_MAX,D as DEPLOY_EVENT_STATUS_REASON_MAX,f as DEPLOY_EVENT_TRAIL_DETAIL_MAX,x as DEPLOY_EVENT_URL_MAX,k as DEPLOY_RESULTS,Y as DETECTION_PHASES,A as DeployEventSchema,Nt as DeployModeSchema,_ as DeployResourceSchema,At as DeploymentError,t as DeploymentEventSchema,Qr as EcsError,jr as EcsService,qr as EcsServiceResolver,_t as FileSystemError,dr as FjallStateFileSchema,Ea as FrameworkRegistry,ge as IDENTITY_STORE_PAGE_SIZE,Rr as INFRASTRUCTURE_FILENAME,Ve as INFRASTRUCTURE_STEP_NAMES,Fe as INFRA_STEP_NAME,U as LOG_LEVELS,vo as MAX_ROOT_SESSION_SECONDS,Tt as NetworkError,je as OPENNEXT_DEPLOY_ORDER,Qe as OPENNEXT_DESTROY_ORDER,We as OPENNEXT_PARALLEL_GROUPS,_r as OPENNEXT_PATTERNS,Xe as ORGANISATION_TYPES,Qo as ORG_SETUP_PHASES,To as ORG_TRAIL_BUCKET_OUTPUT_KEY,Ko as OU_ONLY_ACCOUNT_BUCKETS,qe as PARALLEL_DEPLOY_GROUPS,Je as PARALLEL_DESTROY_GROUPS,Ze as PARALLEL_OPERATION_TYPES,h as PHASE_STATUSES,F as PLAN_ACTIONS,$r as PROGRESS_MESSAGES,l as PlanChangeSchema,p as PlanPropertyChangeSchema,Ge as ProgressReporter,B as REPLACEMENT_MODES,ve as ROOT_ACCESS_FEATURES,Mo as ROOT_TASK_POLICY_ARNS,ke as RootAccessEnablementError,Ue as SECURITY_SERVICE_PRINCIPALS,ne as SERVICE_PRINCIPALS,M as STACK_CLEANUP_PHASES,Or as STACK_FAILED_STATE_PATTERN,pr as STACK_NOT_FOUND_PATTERN,y as STEP_COMPLETE_STATUSES,he as STEP_IDS,be as STEP_NAMES,m as SUPPORTED_CONTRACT_MARKERS,Dt as ServiceArtefactSchema,Lt as ServiceArtefactsSchema,q as SimpleAwsProvider,T as StepProgressSchema,ca as StepRegistry,_o as TRAIL_BUCKET_OUTPUT_KEY,lo as TRAIL_KEY_ARN_OUTPUT_KEY,Wr as TemplateHashError,Jr as TemplateHashService,St as ValidationError,Vo as accountsConfigToOUTree,me as activateCostAllocationTags,Ae as activateTrustedAccess,Ct as artefactOutputKey,yo as assumeRootForTask,$t as buildAccountRegionPairs,ue as buildAccountToOUMap,ea as buildBuildSecretSessionPolicy,_a as buildDeployPlan,ra as buildDeploySessionPolicy,zt as buildRegionList,rt as buildStepContextBuildConfig,$ as buildTargetSetUnreadyAdvisory,z as buildTargetUnreadyAdvisory,eo as cascadeHomeRegion,ro as cascadeOperationKey,de as checkIdentityCentreStatus,te as checkRegionEnabled,W as checkTargetReadiness,Pa as classifyImpact,Ne as composeSdkAbortSignal,pa as computeAssemblyDigest,la as computeDeployPlan,jt as computeSecretsDrift,tt as convertCloudFormationOutputsToRecord,Oe as createAccount,H as createDeployEmitter,Dr as createEmptyState,Ut as createSequencedCallbacks,no as decideNextTransition,Ao as decommissionMemberTrailStorage,Lr as deleteStateFile,Bt as deploy,Ra as derivePropertyChanges,lr as deriveResourcesFromManifestStacks,Qt as deriveStaleParameters,ie as describeOrganisation,Z as describeTargetReadinessReason,Kt as destroy,Oa as digestHead,Sa as dockerBuilder,Ie as drainPages,zr as emitProgress,Me as enableCentralisedRootAccess,Te as enableIpamDelegatedAdmin,se as enablePolicyTypes,ce as enableRamSharing,Se as enableServiceAccess,Lo as ensureEnforceSsl,Ee as ensureOrganisationExists,Pe as ensureOrganisationalUnitsExist,bo as extractAllAccountNames,qt as extractConsumedSsmParameters,De as extractErrorName,ht as failure,vt as fileExists,Pt as filterDangerousEnvVars,pe as findAccount,ho as flattenAccountsToEnvironments,Fr as formatInfrastructureError,tr as getApplicationDeployOrder,or as getApplicationDestroyOrder,ar as getApplicationStackName,cr as getApplicationStepId,nr as getApplicationStepName,Aa as getDestroyStepId,Er as getOrganisationStackName,er as getParallelDeployGroups,rr as getParallelDestroyGroups,Gr as getSourceContext,gr as getStateFilePath,Br as getStructuralHint,Kr as hasCdkDifferences,gt as hasDockerfile,Le as isAborted,Bo as isAccountsConfig,ze as isApplicationOperation,ir as isApplicationStack,Vr as isCdkError,ua as isDataLoss,mo as isEnforceSslStatement,Vt as isFailure,xe as isOULeaf,Tr as isOpenNextPattern,$e as isOrganisationOperation,Go as isOuOnlyAccountBucket,sr as isQuarantineDetail,Sr as isRetainedBucketsDetail,Uo as isRootTaskPolicyArn,ma as isStatefulResourceType,Fo as isStringArray,bt as isSuccess,le as listAccounts,Rt as maskSensitiveOutput,so as mergeReconciledProviderAccounts,sa as openNextBuilder,Yo as parseAccountsConfiguration,et as parseBuildPhase,j as parseDeployContractVersion,w as parseDeployEvent,Xr as parseDiffOutput,ut as parseShellArgs,Wt as partitionAccounts,ta as partitionForRegion,Re as placeAccountsInOUs,ee as probeAccountStackExists,wt as probeSecretsDrift,ao as projectAccountRows,oo as projectScalarSummary,Cr as readStateFile,io as reconcileProviderAccounts,co as reconcileTrailMigration,oe as regionDisabledMessage,ye as registerSecurityDelegates,Ca as renderPlanLines,da as renderPlanSummary,$o as resolveBuildArgs,Zo as resolveBuildSecretSessionProvider,Jo as resolveBuildSecrets,Wo as resolveSecretRefValue,Ht as restart,fo as restoreAndReconcileQuarantinedBucket,No as restoreBucketPolicy,Ia as runApprovalGate,Ho as runOpenNextBuild,jo as runOrganisationSetup,Da as signApprovalToken,mt as sleep,xt as sleepAbortable,Ce as snapshotIdentityCentre,zo as sourcedRefsFromBuildArgs,hr as startStackMonitoring,Ir as stubCallerIdentity,Yt as success,Do as synthesiseEnforceSslDocument,Ar as toPascalCase,Co as toResourcePolicyStatements,pt as toServiceError,Na as toWirePlanChanges,uo as triageBucketPolicy,Oo as unlockBucket,Po as unlockQueue,_e as updateBackupGlobalSettings,fr as updateTemplateHash,oa as validateBuildGroup,La as verifyApprovalToken,Io as verifyOrgTrailDelivery,mr as wrapApplicationError,Nr as writeStateFile};
|
|
1
|
+
import{DeploymentEventSchema as t,DEPLOYMENT_EVENT_TYPES as o,DEPLOYMENT_EVENT_RESOURCE_CATEGORIES as a,DEPLOYMENT_EVENT_STATUS_REASON_MAX as E,DEPLOYMENT_EVENT_ERROR_MESSAGE_MAX as i,DEPLOYMENT_EVENT_TRAIL_DETAIL_MAX as s,CASCADE_PHASES as S,CASCADE_ACCOUNT_STATUSES as n}from"./types/index.js";import{DeployEventSchema as A,StepProgressSchema as l,DeployResourceSchema as T,PlanChangeSchema as _,PlanPropertyChangeSchema as p,ChangeCountsSchema as O,DEPLOY_EVENT_CONTRACT as P,DEPLOY_EVENT_CONTRACT_MAJOR as u,DEPLOY_EVENT_CONTRACT_MINOR as R,SUPPORTED_CONTRACT_MARKERS as m,DEPLOY_EVENT_ID_MAX as d,DEPLOY_EVENT_NAME_MAX as C,DEPLOY_EVENT_MESSAGE_MAX as N,DEPLOY_EVENT_STATUS_REASON_MAX as D,DEPLOY_EVENT_ERROR_MESSAGE_MAX as L,DEPLOY_EVENT_TRAIL_DETAIL_MAX as f,DEPLOY_EVENT_OUTPUT_MAX as g,DEPLOY_EVENT_ARN_MAX as I,DEPLOY_EVENT_URL_MAX as x,STEP_COMPLETE_STATUSES as y,LOG_LEVELS as U,STACK_CLEANUP_PHASES as M,CASCADE_ACCOUNT_PHASES as v,DEPLOY_RESULTS as k,DETECTION_PHASES as Y,PHASE_STATUSES as h,BUILDERS as b,CASCADE_OUTCOME_RESULTS as V,PLAN_ACTIONS as F,REPLACEMENT_MODES as B,APPROVAL_KINDS as G,APPROVAL_DECISIONS as K,ALL_PROGRESS_KINDS as X,createDeployEmitter as H,parseDeployEvent as w,parseDeployContractVersion as j}from"./types/index.js";import{SimpleAwsProvider as q}from"./aws/index.js";import{checkTargetReadiness as W,describeTargetReadinessReason as Z,buildTargetUnreadyAdvisory as z,buildTargetSetUnreadyAdvisory as $,probeAccountStackExists as ee}from"./aws/index.js";import{checkRegionEnabled as te,regionDisabledMessage as oe}from"./aws/index.js";import{ensureOrganisationExists as Ee,describeOrganisation as ie,enablePolicyTypes as se,enableServiceAccess as Se,SERVICE_PRINCIPALS as ne,enableRamSharing as ce,activateTrustedAccess as Ae,enableIpamDelegatedAdmin as le,updateBackupGlobalSettings as Te,listAccounts as _e,findAccount as pe,createAccount as Oe,ensureOrganisationalUnitsExist as Pe,placeAccountsInOUs as ue,buildAccountToOUMap as Re,findDevelopmentOuId as me,activateCostAllocationTags as de,checkIdentityCentreStatus as Ce,snapshotIdentityCentre as Ne,composeSdkAbortSignal as De,extractErrorName as Le,isAborted as fe,DEFAULT_MAX_PAGES as ge,IDENTITY_STORE_PAGE_SIZE as Ie,drainPages as xe,isOULeaf as ye,registerSecurityDelegates as Ue,SECURITY_SERVICE_PRINCIPALS as Me,enableCentralisedRootAccess as ve,ROOT_ACCESS_FEATURES as ke,RootAccessEnablementError as Ye}from"./aws/index.js";import{STEP_IDS as be,STEP_NAMES as Ve,INFRASTRUCTURE_STEP_NAMES as Fe,INFRA_STEP_NAME as Be}from"./types/index.js";import{ProgressReporter as Ke,APPLICATION_STACKS as Xe,ORGANISATION_TYPES as He,APPLICATION_DEPLOY_ORDER as we,APPLICATION_DESTROY_ORDER as je,OPENNEXT_DEPLOY_ORDER as Qe,OPENNEXT_DESTROY_ORDER as qe,PARALLEL_DEPLOY_GROUPS as Je,PARALLEL_DESTROY_GROUPS as We,OPENNEXT_PARALLEL_GROUPS as Ze,PARALLEL_OPERATION_TYPES as ze,isApplicationOperation as $e,isOrganisationOperation as er,getParallelDeployGroups as rr,getParallelDestroyGroups as tr,getApplicationDeployOrder as or,getApplicationDestroyOrder as ar,getApplicationStackName as Er,getOrganisationStackName as ir,isApplicationStack as sr,isQuarantineDetail as Sr,isRetainedBucketsDetail as nr,getApplicationStepName as cr,getApplicationStepId as Ar,toPascalCase as lr,isOpenNextPattern as Tr,OPENNEXT_PATTERNS as _r,deriveResourcesFromManifestStacks as pr,STACK_NOT_FOUND_PATTERN as Or,STACK_FAILED_STATE_PATTERN as Pr,CDK_NO_STACKS_MATCH as ur,INFRASTRUCTURE_FILENAME as Rr,ApplicationError as mr,wrapApplicationError as dr,FjallStateFileSchema as Cr,readStateFile as Nr,writeStateFile as Dr,createEmptyState as Lr,deleteStateFile as fr,updateTemplateHash as gr,getStateFilePath as Ir,stubCallerIdentity as xr}from"./types/index.js";import{CloudFormationEventMonitor as Ur}from"./aws/index.js";import{CdkService as vr,CdkArgumentBuilder as kr,CdkProcessManager as Yr,CdkEventMonitor as hr,startStackMonitoring as br,DEFAULT_DEPLOY_TIMEOUT_MS as Vr,isCdkError as Fr,formatInfrastructureError as Br,getStructuralHint as Gr,getSourceContext as Kr,hasCdkDifferences as Xr,parseDiffOutput as Hr,CloudFormationService as wr,CloudFormationError as jr,EcsService as Qr,EcsError as qr,EcsServiceResolver as Jr,TemplateHashService as Wr,TemplateHashError as Zr,CdkContextBuilder as zr,emitProgress as $r,PROGRESS_MESSAGES as et,parseBuildPhase as rt,buildStepContextBuildConfig as tt,convertCloudFormationOutputsToRecord as ot,ApplicationStackService as at}from"./services/index.js";import{CdkError as it}from"./types/errors/index.js";import{BaseServiceError as St,ValidationError as nt,AuthError as ct,AwsError as At,DeploymentError as lt,NetworkError as Tt,FileSystemError as _t,ConfigError as pt,toServiceError as Ot}from"./types/errors/index.js";import{filterDangerousEnvVars as ut,maskSensitiveOutput as Rt,parseShellArgs as mt,sleep as dt}from"@fjall/util";import{artefactOutputKey as Nt,DeployModeSchema as Dt,ServiceArtefactSchema as Lt,ServiceArtefactsSchema as ft}from"@fjall/util";import{hasDockerfile as It}from"./util/dockerfileDetection.js";import{sleepAbortable as yt}from"./util/sleepAbortable.js";import{createSequencedCallbacks as Mt}from"./util/sequencedCallbacks.js";import{fileExists as kt}from"@fjall/util/fsHelpers";import{success as ht,failure as bt,isSuccess as Vt,isFailure as Ft}from"@fjall/generator";import{deploy as Gt}from"./orchestration/index.js";import{destroy as Xt}from"./orchestration/index.js";import{restart as wt,probeSecretsDrift as jt,computeSecretsDrift as Qt,deriveStaleParameters as qt,extractConsumedSsmParameters as Jt}from"./orchestration/index.js";import{partitionAccounts as Zt}from"./orchestration/index.js";import{buildRegionList as $t,buildAccountRegionPairs as eo,cascadeHomeRegion as ro,cascadeOperationKey as to}from"./orchestration/index.js";import{projectScalarSummary as ao,projectAccountRows as Eo}from"./orchestration/index.js";import{reconcileProviderAccounts as so,mergeReconciledProviderAccounts as So}from"./orchestration/index.js";import{decideNextTransition as co,reconcileTrailMigration as Ao,decommissionMemberTrailStorage as lo,ORG_TRAIL_BUCKET_OUTPUT_KEY as To,TRAIL_BUCKET_OUTPUT_KEY as _o,TRAIL_KEY_ARN_OUTPUT_KEY as po}from"./orchestration/index.js";import{unlockBucket as Po,unlockQueue as uo}from"./orchestration/index.js";import{triageBucketPolicy as mo,isEnforceSslStatement as Co,toResourcePolicyStatements as No,restoreBucketPolicy as Do,synthesiseEnforceSslDocument as Lo,ensureEnforceSsl as fo,restoreAndReconcileQuarantinedBucket as go}from"./orchestration/index.js";import{verifyOrgTrailDelivery as xo}from"./aws/cloudtrail/orgTrailDelivery.js";import{assumeRootForTask as Uo,isRootTaskPolicyArn as Mo,ROOT_TASK_POLICY_ARNS as vo,MAX_ROOT_SESSION_SECONDS as ko}from"./aws/sts/assumeRoot.js";import{parseAccountsConfiguration as ho,flattenAccountsToEnvironments as bo,extractAllAccountNames as Vo,accountsConfigToOUTree as Fo,isStringArray as Bo,isAccountsConfig as Go,isOuOnlyAccountBucket as Ko,OU_ONLY_ACCOUNT_BUCKETS as Xo}from"./orchestration/index.js";import{runOpenNextBuild as wo}from"./orchestration/index.js";import{runOrganisationSetup as Qo,ORG_SETUP_PHASES as qo}from"./orchestration/index.js";import{resolveBuildSecrets as Wo,resolveSecretRefValue as Zo,resolveBuildSecretSessionProvider as zo,sourcedRefsFromBuildArgs as $o,resolveBuildArgs as ea,buildBuildSecretSessionPolicy as ra,buildDeploySessionPolicy as ta,partitionForRegion as oa,validateBuildGroup as aa}from"./orchestration/index.js";import{FrameworkRegistry as ia}from"./orchestration/index.js";import{openNextBuilder as Sa,dockerBuilder as na}from"./orchestration/index.js";import{StepRegistry as Aa,getDestroyStepId as la}from"./steps/index.js";import{buildDeployPlan as _a,computeDeployPlan as pa,computeAssemblyDigest as Oa,digestHead as Pa,classifyImpact as ua,derivePropertyChanges as Ra,isDataLoss as ma,isStatefulResourceType as da,renderPlanSummary as Ca,renderPlanLines as Na,toWirePlanChanges as Da,signApprovalToken as La,verifyApprovalToken as fa,DEFAULT_APPROVAL_TTL_MS as ga,APPROVAL_TOKEN_PATTERN as Ia,runApprovalGate as xa}from"./orchestration/index.js";export{X as ALL_PROGRESS_KINDS,we as APPLICATION_DEPLOY_ORDER,je as APPLICATION_DESTROY_ORDER,Xe as APPLICATION_STACKS,K as APPROVAL_DECISIONS,G as APPROVAL_KINDS,Ia as APPROVAL_TOKEN_PATTERN,mr as ApplicationError,at as ApplicationStackService,ct as AuthError,At as AwsError,b as BUILDERS,St as BaseServiceError,v as CASCADE_ACCOUNT_PHASES,n as CASCADE_ACCOUNT_STATUSES,V as CASCADE_OUTCOME_RESULTS,S as CASCADE_PHASES,ur as CDK_NO_STACKS_MATCH,kr as CdkArgumentBuilder,zr as CdkContextBuilder,it as CdkError,hr as CdkEventMonitor,Yr as CdkProcessManager,vr as CdkService,O as ChangeCountsSchema,jr as CloudFormationError,Ur as CloudFormationEventMonitor,wr as CloudFormationService,pt as ConfigError,ga as DEFAULT_APPROVAL_TTL_MS,Vr as DEFAULT_DEPLOY_TIMEOUT_MS,ge as DEFAULT_MAX_PAGES,i as DEPLOYMENT_EVENT_ERROR_MESSAGE_MAX,a as DEPLOYMENT_EVENT_RESOURCE_CATEGORIES,E as DEPLOYMENT_EVENT_STATUS_REASON_MAX,s as DEPLOYMENT_EVENT_TRAIL_DETAIL_MAX,o as DEPLOYMENT_EVENT_TYPES,I as DEPLOY_EVENT_ARN_MAX,P as DEPLOY_EVENT_CONTRACT,u as DEPLOY_EVENT_CONTRACT_MAJOR,R as DEPLOY_EVENT_CONTRACT_MINOR,L as DEPLOY_EVENT_ERROR_MESSAGE_MAX,d as DEPLOY_EVENT_ID_MAX,N as DEPLOY_EVENT_MESSAGE_MAX,C as DEPLOY_EVENT_NAME_MAX,g as DEPLOY_EVENT_OUTPUT_MAX,D as DEPLOY_EVENT_STATUS_REASON_MAX,f as DEPLOY_EVENT_TRAIL_DETAIL_MAX,x as DEPLOY_EVENT_URL_MAX,k as DEPLOY_RESULTS,Y as DETECTION_PHASES,A as DeployEventSchema,Dt as DeployModeSchema,T as DeployResourceSchema,lt as DeploymentError,t as DeploymentEventSchema,qr as EcsError,Qr as EcsService,Jr as EcsServiceResolver,_t as FileSystemError,Cr as FjallStateFileSchema,ia as FrameworkRegistry,Ie as IDENTITY_STORE_PAGE_SIZE,Rr as INFRASTRUCTURE_FILENAME,Fe as INFRASTRUCTURE_STEP_NAMES,Be as INFRA_STEP_NAME,U as LOG_LEVELS,ko as MAX_ROOT_SESSION_SECONDS,Tt as NetworkError,Qe as OPENNEXT_DEPLOY_ORDER,qe as OPENNEXT_DESTROY_ORDER,Ze as OPENNEXT_PARALLEL_GROUPS,_r as OPENNEXT_PATTERNS,He as ORGANISATION_TYPES,qo as ORG_SETUP_PHASES,To as ORG_TRAIL_BUCKET_OUTPUT_KEY,Xo as OU_ONLY_ACCOUNT_BUCKETS,Je as PARALLEL_DEPLOY_GROUPS,We as PARALLEL_DESTROY_GROUPS,ze as PARALLEL_OPERATION_TYPES,h as PHASE_STATUSES,F as PLAN_ACTIONS,et as PROGRESS_MESSAGES,_ as PlanChangeSchema,p as PlanPropertyChangeSchema,Ke as ProgressReporter,B as REPLACEMENT_MODES,ke as ROOT_ACCESS_FEATURES,vo as ROOT_TASK_POLICY_ARNS,Ye as RootAccessEnablementError,Me as SECURITY_SERVICE_PRINCIPALS,ne as SERVICE_PRINCIPALS,M as STACK_CLEANUP_PHASES,Pr as STACK_FAILED_STATE_PATTERN,Or as STACK_NOT_FOUND_PATTERN,y as STEP_COMPLETE_STATUSES,be as STEP_IDS,Ve as STEP_NAMES,m as SUPPORTED_CONTRACT_MARKERS,Lt as ServiceArtefactSchema,ft as ServiceArtefactsSchema,q as SimpleAwsProvider,l as StepProgressSchema,Aa as StepRegistry,_o as TRAIL_BUCKET_OUTPUT_KEY,po as TRAIL_KEY_ARN_OUTPUT_KEY,Zr as TemplateHashError,Wr as TemplateHashService,nt as ValidationError,Fo as accountsConfigToOUTree,de as activateCostAllocationTags,Ae as activateTrustedAccess,Nt as artefactOutputKey,Uo as assumeRootForTask,eo as buildAccountRegionPairs,Re as buildAccountToOUMap,ra as buildBuildSecretSessionPolicy,_a as buildDeployPlan,ta as buildDeploySessionPolicy,$t as buildRegionList,tt as buildStepContextBuildConfig,$ as buildTargetSetUnreadyAdvisory,z as buildTargetUnreadyAdvisory,ro as cascadeHomeRegion,to as cascadeOperationKey,Ce as checkIdentityCentreStatus,te as checkRegionEnabled,W as checkTargetReadiness,ua as classifyImpact,De as composeSdkAbortSignal,Oa as computeAssemblyDigest,pa as computeDeployPlan,Qt as computeSecretsDrift,ot as convertCloudFormationOutputsToRecord,Oe as createAccount,H as createDeployEmitter,Lr as createEmptyState,Mt as createSequencedCallbacks,co as decideNextTransition,lo as decommissionMemberTrailStorage,fr as deleteStateFile,Gt as deploy,Ra as derivePropertyChanges,pr as deriveResourcesFromManifestStacks,qt as deriveStaleParameters,ie as describeOrganisation,Z as describeTargetReadinessReason,Xt as destroy,Pa as digestHead,na as dockerBuilder,xe as drainPages,$r as emitProgress,ve as enableCentralisedRootAccess,le as enableIpamDelegatedAdmin,se as enablePolicyTypes,ce as enableRamSharing,Se as enableServiceAccess,fo as ensureEnforceSsl,Ee as ensureOrganisationExists,Pe as ensureOrganisationalUnitsExist,Vo as extractAllAccountNames,Jt as extractConsumedSsmParameters,Le as extractErrorName,bt as failure,kt as fileExists,ut as filterDangerousEnvVars,pe as findAccount,me as findDevelopmentOuId,bo as flattenAccountsToEnvironments,Br as formatInfrastructureError,or as getApplicationDeployOrder,ar as getApplicationDestroyOrder,Er as getApplicationStackName,Ar as getApplicationStepId,cr as getApplicationStepName,la as getDestroyStepId,ir as getOrganisationStackName,rr as getParallelDeployGroups,tr as getParallelDestroyGroups,Kr as getSourceContext,Ir as getStateFilePath,Gr as getStructuralHint,Xr as hasCdkDifferences,It as hasDockerfile,fe as isAborted,Go as isAccountsConfig,$e as isApplicationOperation,sr as isApplicationStack,Fr as isCdkError,ma as isDataLoss,Co as isEnforceSslStatement,Ft as isFailure,ye as isOULeaf,Tr as isOpenNextPattern,er as isOrganisationOperation,Ko as isOuOnlyAccountBucket,Sr as isQuarantineDetail,nr as isRetainedBucketsDetail,Mo as isRootTaskPolicyArn,da as isStatefulResourceType,Bo as isStringArray,Vt as isSuccess,_e as listAccounts,Rt as maskSensitiveOutput,So as mergeReconciledProviderAccounts,Sa as openNextBuilder,ho as parseAccountsConfiguration,rt as parseBuildPhase,j as parseDeployContractVersion,w as parseDeployEvent,Hr as parseDiffOutput,mt as parseShellArgs,Zt as partitionAccounts,oa as partitionForRegion,ue as placeAccountsInOUs,ee as probeAccountStackExists,jt as probeSecretsDrift,Eo as projectAccountRows,ao as projectScalarSummary,Nr as readStateFile,so as reconcileProviderAccounts,Ao as reconcileTrailMigration,oe as regionDisabledMessage,Ue as registerSecurityDelegates,Na as renderPlanLines,Ca as renderPlanSummary,ea as resolveBuildArgs,zo as resolveBuildSecretSessionProvider,Wo as resolveBuildSecrets,Zo as resolveSecretRefValue,wt as restart,go as restoreAndReconcileQuarantinedBucket,Do as restoreBucketPolicy,xa as runApprovalGate,wo as runOpenNextBuild,Qo as runOrganisationSetup,La as signApprovalToken,dt as sleep,yt as sleepAbortable,Ne as snapshotIdentityCentre,$o as sourcedRefsFromBuildArgs,br as startStackMonitoring,xr as stubCallerIdentity,ht as success,Lo as synthesiseEnforceSslDocument,lr as toPascalCase,No as toResourcePolicyStatements,Ot as toServiceError,Da as toWirePlanChanges,mo as triageBucketPolicy,Po as unlockBucket,uo as unlockQueue,Te as updateBackupGlobalSettings,gr as updateTemplateHash,aa as validateBuildGroup,fa as verifyApprovalToken,xo as verifyOrgTrailDelivery,dr as wrapApplicationError,Dr as writeStateFile};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{join as q}from"path";import{CloudFormationClient as X}from"@aws-sdk/client-cloudformation";import{EC2Client as Z}from"@aws-sdk/client-ec2";import{success as S,failure as l}from"@fjall/generator";import{logger as I}from"@fjall/util/logger";import{ImageTagSchema as ee,imageTagParameterName as te,maskSensitiveOutput as k}from"@fjall/util";import{parseDockerServicesFromManifest as re}from"@fjall/util/manifest";import{stubCallerIdentity as ae}from"../../types/deployment/index.js";import{getApplicationDeployOrder as oe,getApplicationStackName as ne,getApplicationStepName as W,getApplicationStepId as z,APPLICATION_STACKS as O}from"../../types/operations.js";import{CdkContextBuilder as ie}from"../../services/supporting/CdkContextBuilder.js";import{buildParamsContext as se,bootstrapOrFail as ce}from"../contextHelpers.js";import{runDetectionPipeline as le}from"./detectionPipeline.js";import{STEP_IDS as P,STEP_NAMES as de}from"../../types/stepDefinitions.js";import{StepRegistry as pe}from"../../steps/stepRegistry.js";import{checkTargetReadiness as ue}from"../../aws/targetReadiness.js";import{cascadeHomeRegion as ge}from"../organisation/cascadeHelpers.js";import{withStepLifecycle as me}from"../stepLifecycle.js";import{DOCKER_BUILD_STEP_NAME as Y,runDockerBuild as fe}from"./dockerBuildHelper.js";import{getParallelPhase2Stacks as ke,deployParallelPhase as ye,deployStackSequential as he,runDockerPreCompute as be,createBuildCallbacks as Te}from"./applicationDeployHelpers.js";import{deployCodeOnly as we}from"./codeOnlyDeploy.js";import{collectFullDeployArtefacts as Se}from"./artefactCollection.js";import{runApprovalGate as ve}from"./approvalGate.js";const N="applicationDeploy";function De(t){const s=Object.entries(t);if(s.length===0)return;const n={};for(const[r,i]of s){const h=te(r);n[h]=i}return n}function Ee(t){if(t?.mode!==void 0&&t.mode!=="rollback"){if(t.imageTag)return l(new Error(`--image-tag requires rollback mode (explicit mode '${t.mode}' was supplied alongside an image tag)`));if(t.serviceImageTags)return l(new Error(`serviceImageTags requires rollback mode (explicit mode '${t.mode}' was supplied alongside serviceImageTags)`))}if(t?.mode==="rollback"){if(t.imageTag&&t.serviceImageTags)return l(new Error("rollback mode accepts exactly one of --image-tag or serviceImageTags \u2014 they are mutually exclusive"));if(!t.imageTag&&!t.serviceImageTags)return l(new Error("rollback mode requires --image-tag <tag> or serviceImageTags naming the image(s) to roll to"))}if(t?.serviceImageTags!==void 0){if(t.serviceName)return l(new Error("serviceImageTags already names its services \u2014 --service cannot be combined with it"));const s=Object.entries(t.serviceImageTags);if(s.length===0)return l(new Error("serviceImageTags must name at least one service"));for(const[n,r]of s){const i=ee.safeParse(r);if(!i.success)return l(new Error(`serviceImageTags.${n} is not a valid image tag: ${i.error.message}`))}}return S(void 0)}async function Je(t,s,n){const{callbacks:r,options:i}=t,h=Date.now(),v=i?.mode??(i?.imageTag||i?.serviceImageTags?"rollback":i?.deployOnly?"code-only":"full");if(v==="restart"){const e=new Error('mode "restart" is not a deploy \u2014 call the restart() entry point (fjall rollout) instead');return r.onError?.(e),l(e)}const M=Ee(i);if(!M.success){const e=k(M.error.message),a=new Error(e);return r.onError?.(a),l(a)}const D=ie.buildDeploymentContext({deployType:"application",target:n.appName,path:n.path,region:s.awsProvider.getRegion(),callerIdentity:ae(s.awsProvider.getAccountId()),...se({orgConfig:t.orgConfig,identity:t.identity,skipOidc:t.options?.skipOidc})},{verbose:i?.verbose,infraOnly:i?.infraOnly},t.orgConfig),u=s.frameworkRegistry.resolve({appPath:n.path});let E;const T=v==="rollback";if(u&&!T){E=u.builder.plan({appPath:n.path},u.detection);const e=Te(r),a=await u.builder.build(n.path,E,e,{skipBuild:i?.skipBuild,infraOnly:i?.infraOnly});if(!a.success){const o=new Error(k(a.error.message));return r.onError?.(o),l(o)}}if(v==="code-only"||T){r.onLog?.(T?i?.serviceImageTags?`Rollback mode \u2014 rolling ${Object.keys(i.serviceImageTags).length} service(s) to their release-recorded image tags`:`Rollback mode \u2014 rolling to image tag ${i?.imageTag}`:"Deploy-only mode \u2014 skipping infrastructure pipeline","info");const e=q(n.path,"cdk.out"),a=re(e),o=a.length>0,p=u?.detection.hasDockerfile===!0,d=o||p;I.debug(N,"Deploy-only branch entered",{mode:v,imageTag:i?.imageTag,serviceImageTags:i?.serviceImageTags,appName:n.appName,appPath:n.path,cdkOutPath:e,dockerProviderAvailable:t.dockerProvider!==void 0,builderName:u?.builder.name,hasDockerfileFromManifest:o,hasDockerfileFromDisk:p,hasDockerfile:d,manifestDockerServiceCount:a.length,manifestDockerPaths:a.map(y=>y.docker.path)}),!o&&!p&&r.onLog?.("No Dockerfile detected via manifest or appPath \u2014 skipping Docker build. If this app uses a cross-repo Dockerfile, ensure a full deploy has run first to populate cdk.out/fjall-manifest.json.","warn");let g={};if(!T&&t.dockerProvider!==void 0&&d){I.debug(N,"Running Docker build before code-only deploy",{source:o?"manifest":"disk"});const y=await fe(t,s,n,r);if(!y.success)return l(y.error);g=y.data.contentHashTagsByService}else I.debug(N,"Skipping Docker build",{reason:T?"rollback":t.dockerProvider===void 0?"no dockerProvider":"no Dockerfile detected"});return we(t,s,n,g)}r.onLog?.("Analysing infrastructure\u2026","info");const R=await le(n,s,D,r);if(!R.success){const e=new Error(k(R.error.message));return r.onError?.(e),l(e)}const c=R.data;try{await r.onDetectionComplete?.({...c,builderName:u?.builder.name??"unknown"})}catch(e){const a=e instanceof Error?e.message:String(e),o=new Error(k(a));return r.onError?.(o),l(o)}const J={deploymentType:"application",operation:"deploy",deployOnly:!1,infraOnly:i?.infraOnly??!1,hasDockerfile:c.hasDockerfile,pattern:c.pattern,resources:c.resources,...u&&{builderName:u.builder.name}},x=pe.getSteps(J),L=x.findIndex(e=>e.id===P.TARGET_READINESS),_=await me(r,{stepId:P.TARGET_READINESS,stepName:de.TARGET_READINESS,...L>=0&&{stepIndex:L,totalSteps:x.length}},async()=>{if(i?.skipReadinessCheck)return r.onLog?.("Skipping target readiness check (--skip-readiness-check)","warn"),{kind:"skipped",data:void 0};const e=s.awsProvider.getAccountId(),a=t.orgConfig?.providerAccounts.find(g=>g.id===e)?.name,o=s.awsProvider.getCredentials(),p=ge(t.orgConfig),d=await ue({cloudFormation:s.awsProvider.getClient(X),ec2:new Z({region:p,...o!==void 0&&{credentials:o}})},{id:e,...a!==void 0&&{name:a}},s.awsProvider.getRegion(),t.orgConfig,t.abortSignal);return d.success?d.data.ready?{kind:"completed",data:void 0}:{kind:"error",error:new Error(k(d.data.advisory))}:{kind:"error",error:new Error(k(d.error.message))}},t.abortSignal);if(!_.success)return _;const f=E?E.deployOrder:oe({pattern:c.pattern,resources:c.resources}),b=f.length,j=c.hasDockerfile&&t.dockerProvider!==void 0&&f.includes(O.COMPUTE)&&i?.infraOnly!==!0;if(!c.hasDifferences&&!i?.force&&!j){r.onLog?.("No infrastructure changes detected","info"),r.onLog?.(i?.infraOnly===!0?"Infrastructure-only mode \u2014 application code was not rebuilt. Run with --deploy-only to deploy code changes.":"Nothing to deploy. Run with --deploy-only to redeploy application code.","info");const e=c.hasDockerfile&&t.dockerProvider!==void 0&&f.includes(O.COMPUTE);for(let o=0;o<f.length;o++){const p=f[o];e&&p===O.COMPUTE&&(r.onStepStart?.(P.DOCKER_OPERATIONS,Y),r.onStepComplete?.(P.DOCKER_OPERATIONS,Y,"skipped"));const d=z(p,"deploy"),g=W(p,"deploy");r.onStepStart?.(d,g,o,b),r.onStepComplete?.(d,g,"skipped",o,b)}const a=await s.stackService.resolveWebsiteUrl(n.appName);return S({target:n.appName,deploymentType:"application",outputs:a?{websiteUrl:a}:void 0,noChanges:!0,artefacts:[],durationMs:Date.now()-h})}if(t.approvalGate!==void 0||i?.approvalToken!==void 0&&i.approvalToken!==""||i?.planOnly===!0){const e=q(n.path,"cdk.out"),a=await ve({approvalGate:t.approvalGate,options:i??{},target:n.appName,cdkOutPath:e,detection:{stackChanges:c.stackChanges,currentHashes:c.currentHashes},cfnService:s.cfnService,callbacks:r});if(!a.success)return a;const o=a.data;if(o.kind==="rejected")return r.onLog?.(`Deploy rejected: ${k(o.reason)}`,"warn"),S({target:n.appName,deploymentType:"application",rejected:!0,artefacts:[],...o.planDetail!==void 0?{planDetail:o.planDetail}:{},durationMs:Date.now()-h});if(o.kind==="awaiting")return S({target:n.appName,deploymentType:"application",awaitingApproval:!0,approvalToken:o.approvalToken,assemblyDigest:o.assemblyDigest,planDetail:o.planDetail,artefacts:[],durationMs:Date.now()-h})}const B=await ce(s,D,r);if(!B.success)return B;const w={},C=new Map;let H;for(let e=0;e<f.length;e++){const a=f[e],o=ne(n.appName,a),p=z(a,"deploy"),d=W(a,"deploy"),g=c.stackChanges.get(o)??!0,y=a===O.COMPUTE&&j;if(!g&&!i?.force&&!y){r.onStepStart?.(p,d,e,b),r.onLog?.(`Skipping ${a} \u2014 no changes detected`,"info"),r.onStepComplete?.(p,d,"skipped",e,b);continue}const A=ke(f,e,c.stackChanges,n.appName,i?.force);if(A.length>=2){const K=await ye(A,n,s,D,r,e,b,c,w,C);if(!K.success)return l(K.error);e+=A.length-1;continue}const m=await be(a,t,s,n,r,c.hasDockerfile);if(m!==null&&!m.success)return l(m.error);m!==null&&m.success&&(H=m.data);const V=m!==null&&m.success?m.data.contentHashTagsByService:{},G=De(V),$=await he(a,s,D,r,e,b,w,G!==void 0?{parameters:G}:void 0);if(!$.success)return l($.error);const F=c.currentHashes.get(o);F&&C.set(o,F)}if(C.size>0){const e=await s.hashService.updateStateAfterDeploy(n.path,C);if(!e.success){const a=k(e.error.message);I.debug(N,"Failed to update state file",{error:a}),r.onLog?.(`Warning: failed to update state file \u2014 next deploy may re-deploy unchanged stacks: ${a}`,"warn")}}const U=await s.stackService.resolveWebsiteUrl(n.appName);U&&(w.websiteUrl=U);const Q=await Se(s,n.appName,H,r);return S({target:n.appName,deploymentType:"application",outputs:Object.keys(w).length>0?w:void 0,artefacts:Q,durationMs:Date.now()-h})}export{Je as deployApplication};
|
|
1
|
+
import{join as Y}from"path";import{CloudFormationClient as ne}from"@aws-sdk/client-cloudformation";import{EC2Client as oe}from"@aws-sdk/client-ec2";import{success as v,failure as l}from"@fjall/generator";import{logger as N}from"@fjall/util/logger";import{ImageTagSchema as ie,imageTagParameterName as se,maskSensitiveOutput as k}from"@fjall/util";import{parseDockerServicesFromManifest as ce}from"@fjall/util/manifest";import{stubCallerIdentity as le}from"../../types/deployment/index.js";import{getApplicationDeployOrder as de,getApplicationStackName as J,getApplicationStepName as Q,getApplicationStepId as V,APPLICATION_STACKS as S}from"../../types/operations.js";import{CdkContextBuilder as pe}from"../../services/supporting/CdkContextBuilder.js";import{buildParamsContext as ue,bootstrapOrFail as ge}from"../contextHelpers.js";import{runDetectionPipeline as fe}from"./detectionPipeline.js";import{STEP_IDS as R,STEP_NAMES as me}from"../../types/stepDefinitions.js";import{StepRegistry as ke}from"../../steps/stepRegistry.js";import{checkTargetReadiness as ye}from"../../aws/targetReadiness.js";import{cascadeHomeRegion as he}from"../organisation/cascadeHelpers.js";import{withStepLifecycle as be}from"../stepLifecycle.js";import{DOCKER_BUILD_STEP_NAME as X,runDockerBuild as Se}from"./dockerBuildHelper.js";import{getParallelPhase2Stacks as we,deployParallelPhase as Te,deployStackSequential as De,runDockerPreCompute as ve,createBuildCallbacks as Ee}from"./applicationDeployHelpers.js";import{captureDatabaseConnectionOutputs as Ce,captureServiceTaskDefinitions as Oe,reconcileDatabaseEndpointChange as Ie}from"./databaseEndpointReconcile.js";import{deployCodeOnly as Ne}from"./codeOnlyDeploy.js";import{collectFullDeployArtefacts as Re}from"./artefactCollection.js";import{runApprovalGate as Pe}from"./approvalGate.js";const P="applicationDeploy";function Ae(e){const i=Object.entries(e);if(i.length===0)return;const a={};for(const[r,s]of i){const h=se(r);a[h]=s}return a}function Me(e){if(e?.mode!==void 0&&e.mode!=="rollback"){if(e.imageTag)return l(new Error(`--image-tag requires rollback mode (explicit mode '${e.mode}' was supplied alongside an image tag)`));if(e.serviceImageTags)return l(new Error(`serviceImageTags requires rollback mode (explicit mode '${e.mode}' was supplied alongside serviceImageTags)`))}if(e?.mode==="rollback"){if(e.imageTag&&e.serviceImageTags)return l(new Error("rollback mode accepts exactly one of --image-tag or serviceImageTags \u2014 they are mutually exclusive"));if(!e.imageTag&&!e.serviceImageTags)return l(new Error("rollback mode requires --image-tag <tag> or serviceImageTags naming the image(s) to roll to"))}if(e?.serviceImageTags!==void 0){if(e.serviceName)return l(new Error("serviceImageTags already names its services \u2014 --service cannot be combined with it"));const i=Object.entries(e.serviceImageTags);if(i.length===0)return l(new Error("serviceImageTags must name at least one service"));for(const[a,r]of i){const s=ie.safeParse(r);if(!s.success)return l(new Error(`serviceImageTags.${a} is not a valid image tag: ${s.error.message}`))}}return v(void 0)}async function nt(e,i,a){const{callbacks:r,options:s}=e,h=Date.now(),E=s?.mode??(s?.imageTag||s?.serviceImageTags?"rollback":s?.deployOnly?"code-only":"full");if(E==="restart"){const t=new Error('mode "restart" is not a deploy \u2014 call the restart() entry point (fjall rollout) instead');return r.onError?.(t),l(t)}const B=Me(s);if(!B.success){const t=k(B.error.message),n=new Error(t);return r.onError?.(n),l(n)}const C=pe.buildDeploymentContext({deployType:"application",target:a.appName,path:a.path,region:i.awsProvider.getRegion(),callerIdentity:le(i.awsProvider.getAccountId()),...ue({orgConfig:e.orgConfig,identity:e.identity,skipOidc:e.options?.skipOidc})},{verbose:s?.verbose,infraOnly:s?.infraOnly},e.orgConfig),u=i.frameworkRegistry.resolve({appPath:a.path});let O;const w=E==="rollback";if(u&&!w){O=u.builder.plan({appPath:a.path},u.detection);const t=Ee(r),n=await u.builder.build(a.path,O,t,{skipBuild:s?.skipBuild,infraOnly:s?.infraOnly});if(!n.success){const o=new Error(k(n.error.message));return r.onError?.(o),l(o)}}if(E==="code-only"||w){r.onLog?.(w?s?.serviceImageTags?`Rollback mode \u2014 rolling ${Object.keys(s.serviceImageTags).length} service(s) to their release-recorded image tags`:`Rollback mode \u2014 rolling to image tag ${s?.imageTag}`:"Deploy-only mode \u2014 skipping infrastructure pipeline","info");const t=Y(a.path,"cdk.out"),n=ce(t),o=n.length>0,p=u?.detection.hasDockerfile===!0,d=o||p;N.debug(P,"Deploy-only branch entered",{mode:E,imageTag:s?.imageTag,serviceImageTags:s?.serviceImageTags,appName:a.appName,appPath:a.path,cdkOutPath:t,dockerProviderAvailable:e.dockerProvider!==void 0,builderName:u?.builder.name,hasDockerfileFromManifest:o,hasDockerfileFromDisk:p,hasDockerfile:d,manifestDockerServiceCount:n.length,manifestDockerPaths:n.map(y=>y.docker.path)}),!o&&!p&&r.onLog?.("No Dockerfile detected via manifest or appPath \u2014 skipping Docker build. If this app uses a cross-repo Dockerfile, ensure a full deploy has run first to populate cdk.out/fjall-manifest.json.","warn");let f={};if(!w&&e.dockerProvider!==void 0&&d){N.debug(P,"Running Docker build before code-only deploy",{source:o?"manifest":"disk"});const y=await Se(e,i,a,r);if(!y.success)return l(y.error);f=y.data.contentHashTagsByService}else N.debug(P,"Skipping Docker build",{reason:w?"rollback":e.dockerProvider===void 0?"no dockerProvider":"no Dockerfile detected"});return Ne(e,i,a,f)}r.onLog?.("Analysing infrastructure\u2026","info");const A=await fe(a,i,C,r);if(!A.success){const t=new Error(k(A.error.message));return r.onError?.(t),l(t)}const c=A.data;try{await r.onDetectionComplete?.({...c,builderName:u?.builder.name??"unknown"})}catch(t){const n=t instanceof Error?t.message:String(t),o=new Error(k(n));return r.onError?.(o),l(o)}const Z={deploymentType:"application",operation:"deploy",deployOnly:!1,infraOnly:s?.infraOnly??!1,hasDockerfile:c.hasDockerfile,pattern:c.pattern,resources:c.resources,...u&&{builderName:u.builder.name}},L=ke.getSteps(Z),_=L.findIndex(t=>t.id===R.TARGET_READINESS),j=await be(r,{stepId:R.TARGET_READINESS,stepName:me.TARGET_READINESS,..._>=0&&{stepIndex:_,totalSteps:L.length}},async()=>{if(s?.skipReadinessCheck)return r.onLog?.("Skipping target readiness check (--skip-readiness-check)","warn"),{kind:"skipped",data:void 0};const t=i.awsProvider.getAccountId(),n=e.orgConfig?.providerAccounts.find(f=>f.id===t)?.name,o=i.awsProvider.getCredentials(),p=he(e.orgConfig),d=await ye({cloudFormation:i.awsProvider.getClient(ne),ec2:new oe({region:p,...o!==void 0&&{credentials:o}})},{id:t,...n!==void 0&&{name:n}},i.awsProvider.getRegion(),e.orgConfig,e.abortSignal);return d.success?d.data.ready?{kind:"completed",data:void 0}:{kind:"error",error:new Error(k(d.data.advisory))}:{kind:"error",error:new Error(k(d.error.message))}},e.abortSignal);if(!j.success)return j;const g=O?O.deployOrder:de({pattern:c.pattern,resources:c.resources}),b=g.length,H=c.hasDockerfile&&e.dockerProvider!==void 0&&g.includes(S.COMPUTE)&&s?.infraOnly!==!0;if(!c.hasDifferences&&!s?.force&&!H){r.onLog?.("No infrastructure changes detected","info"),r.onLog?.(s?.infraOnly===!0?"Infrastructure-only mode \u2014 application code was not rebuilt. Run with --deploy-only to deploy code changes.":"Nothing to deploy. Run with --deploy-only to redeploy application code.","info");const t=c.hasDockerfile&&e.dockerProvider!==void 0&&g.includes(S.COMPUTE);for(let o=0;o<g.length;o++){const p=g[o];t&&p===S.COMPUTE&&(r.onStepStart?.(R.DOCKER_OPERATIONS,X),r.onStepComplete?.(R.DOCKER_OPERATIONS,X,"skipped"));const d=V(p,"deploy"),f=Q(p,"deploy");r.onStepStart?.(d,f,o,b),r.onStepComplete?.(d,f,"skipped",o,b)}const n=await i.stackService.resolveWebsiteUrl(a.appName);return v({target:a.appName,deploymentType:"application",outputs:n?{websiteUrl:n}:void 0,noChanges:!0,artefacts:[],durationMs:Date.now()-h})}if(e.approvalGate!==void 0||s?.approvalToken!==void 0&&s.approvalToken!==""||s?.planOnly===!0){const t=Y(a.path,"cdk.out"),n=await Pe({approvalGate:e.approvalGate,options:s??{},target:a.appName,cdkOutPath:t,detection:{stackChanges:c.stackChanges,currentHashes:c.currentHashes},cfnService:i.cfnService,callbacks:r});if(!n.success)return n;const o=n.data;if(o.kind==="rejected")return r.onLog?.(`Deploy rejected: ${k(o.reason)}`,"warn"),v({target:a.appName,deploymentType:"application",rejected:!0,artefacts:[],...o.planDetail!==void 0?{planDetail:o.planDetail}:{},durationMs:Date.now()-h});if(o.kind==="awaiting")return v({target:a.appName,deploymentType:"application",awaitingApproval:!0,approvalToken:o.approvalToken,assemblyDigest:o.assemblyDigest,planDetail:o.planDetail,artefacts:[],durationMs:Date.now()-h})}const U=await ge(i,C,r);if(!U.success)return U;const G=J(a.appName,S.DATABASE),M=g.includes(S.DATABASE)&&((c.stackChanges.get(G)??!0)||s?.force===!0),ee=M?await Ce(i.cfnService,a.appName):void 0,te=M?await Oe(i.ecsResolver,i.ecsService,a.appName):void 0,T={},D=new Map;let $;const I=async()=>{!M||!D.has(G)||await Ie({appName:a.appName,connectionOutputsBefore:ee,serviceTaskDefinitionsBefore:te,services:{cfnService:i.cfnService,ecsService:i.ecsService,ecsResolver:i.ecsResolver},callbacks:r,...e.confirmDatabaseEndpointRestart!==void 0?{confirmRestart:e.confirmDatabaseEndpointRestart}:{},...e.abortSignal!==void 0?{abortSignal:e.abortSignal}:{}})};for(let t=0;t<g.length;t++){const n=g[t],o=J(a.appName,n),p=V(n,"deploy"),d=Q(n,"deploy"),f=c.stackChanges.get(o)??!0,y=n===S.COMPUTE&&H;if(!f&&!s?.force&&!y){r.onStepStart?.(p,d,t,b),r.onLog?.(`Skipping ${n} \u2014 no changes detected`,"info"),r.onStepComplete?.(p,d,"skipped",t,b);continue}const x=we(g,t,c.stackChanges,a.appName,s?.force);if(x.length>=2){const z=await Te(x,a,i,C,r,t,b,c,T,D);if(!z.success)return await I(),l(z.error);t+=x.length-1;continue}const m=await ve(n,e,i,a,r,c.hasDockerfile);if(m!==null&&!m.success)return await I(),l(m.error);m!==null&&m.success&&($=m.data);const ae=m!==null&&m.success?m.data.contentHashTagsByService:{},K=Ae(ae),W=await De(n,i,C,r,t,b,T,K!==void 0?{parameters:K}:void 0);if(!W.success)return await I(),l(W.error);const q=c.currentHashes.get(o);q&&D.set(o,q)}if(await I(),D.size>0){const t=await i.hashService.updateStateAfterDeploy(a.path,D);if(!t.success){const n=k(t.error.message);N.debug(P,"Failed to update state file",{error:n}),r.onLog?.(`Warning: failed to update state file \u2014 next deploy may re-deploy unchanged stacks: ${n}`,"warn")}}const F=await i.stackService.resolveWebsiteUrl(a.appName);F&&(T.websiteUrl=F);const re=await Re(i,a.appName,$,r);return v({target:a.appName,deploymentType:"application",outputs:Object.keys(T).length>0?T:void 0,artefacts:re,durationMs:Date.now()-h})}export{nt as deployApplication};
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import type { DeployCallbacks } from "../../types/callbacks.js";
|
|
2
|
+
import type { DeployParams } from "../../types/params.js";
|
|
3
|
+
import type { CloudFormationService } from "../../services/infrastructure/CloudFormationService.js";
|
|
4
|
+
import type { EcsService } from "../../services/infrastructure/EcsService.js";
|
|
5
|
+
import type { EcsServiceResolver } from "../../services/infrastructure/EcsServiceResolver.js";
|
|
6
|
+
/**
|
|
7
|
+
* Read the application's Database-stack connection outputs (endpoint/port
|
|
8
|
+
* keys only). Returns `{}` when the stack or its outputs do not exist, and
|
|
9
|
+
* `undefined` when the read itself failed — callers use that distinction to
|
|
10
|
+
* warn that endpoint stability could not be verified rather than silently
|
|
11
|
+
* treating a failed read as "no endpoints".
|
|
12
|
+
*/
|
|
13
|
+
export declare function captureDatabaseConnectionOutputs(cfnService: CloudFormationService, appName: string): Promise<Record<string, string> | undefined>;
|
|
14
|
+
/**
|
|
15
|
+
* Snapshot each live ECS service's current task-definition ARN before the
|
|
16
|
+
* stack loop runs. The post-deploy reconcile compares these against the
|
|
17
|
+
* post-deploy ARNs to tell which services CloudFormation already rolled
|
|
18
|
+
* during the deploy (ARN changed ⇒ tasks relaunched after the Database phase
|
|
19
|
+
* and re-resolved the updated secret) versus which still run pre-deploy tasks
|
|
20
|
+
* (ARN unchanged ⇒ stale connection values). Inferring this from build state
|
|
21
|
+
* is wrong in the primary scenario: a config-only database replacement
|
|
22
|
+
* rebuilds the same content-hash image, so the Compute stack no-ops and never
|
|
23
|
+
* rolls anything. Returns `{}` when no services exist yet and `undefined`
|
|
24
|
+
* when the read failed. The resolver reports export-read failures as an empty
|
|
25
|
+
* service list, so the reconcile treats an empty baseline like a failed read:
|
|
26
|
+
* both restart without the baseline — an extra roll is harmless, a missed one
|
|
27
|
+
* is not.
|
|
28
|
+
*/
|
|
29
|
+
export declare function captureServiceTaskDefinitions(ecsResolver: EcsServiceResolver, ecsService: EcsService, appName: string): Promise<Record<string, string> | undefined>;
|
|
30
|
+
export interface DatabaseEndpointReconcileParams {
|
|
31
|
+
appName: string;
|
|
32
|
+
/** Pre-deploy connection-output capture; `undefined` when the read failed. */
|
|
33
|
+
connectionOutputsBefore: Record<string, string> | undefined;
|
|
34
|
+
/**
|
|
35
|
+
* Pre-deploy serviceArn → task-definition ARN baseline; `undefined` when
|
|
36
|
+
* the read failed (every service is then restarted — the safe direction).
|
|
37
|
+
*/
|
|
38
|
+
serviceTaskDefinitionsBefore: Record<string, string> | undefined;
|
|
39
|
+
services: {
|
|
40
|
+
cfnService: CloudFormationService;
|
|
41
|
+
ecsService: EcsService;
|
|
42
|
+
ecsResolver: EcsServiceResolver;
|
|
43
|
+
};
|
|
44
|
+
callbacks: DeployCallbacks;
|
|
45
|
+
confirmRestart?: DeployParams["confirmDatabaseEndpointRestart"];
|
|
46
|
+
abortSignal?: AbortSignal;
|
|
47
|
+
}
|
|
48
|
+
export type DatabaseEndpointReconcileOutcome = {
|
|
49
|
+
kind: "not-needed";
|
|
50
|
+
} | {
|
|
51
|
+
kind: "unverified";
|
|
52
|
+
} | {
|
|
53
|
+
kind: "already-current";
|
|
54
|
+
} | {
|
|
55
|
+
kind: "declined";
|
|
56
|
+
} | {
|
|
57
|
+
kind: "reconciled";
|
|
58
|
+
services: string[];
|
|
59
|
+
} | {
|
|
60
|
+
kind: "failed";
|
|
61
|
+
} | {
|
|
62
|
+
kind: "cancelled";
|
|
63
|
+
};
|
|
64
|
+
/**
|
|
65
|
+
* Post-deploy reconcile for a replaced database. A create-only property
|
|
66
|
+
* change (e.g. `snapshotIdentifier`) replaces the database: the credentials
|
|
67
|
+
* secret updates in place (same ARN, new host), so the ECS task definition is
|
|
68
|
+
* byte-identical and CloudFormation makes no ECS change — running tasks keep
|
|
69
|
+
* the stale connection values injected at launch and silently write to the
|
|
70
|
+
* doomed old database until CloudFormation deletes it. This step diffs the
|
|
71
|
+
* Database stack's connection outputs across the deploy and restarts the
|
|
72
|
+
* services whose task-definition ARN did not change during the deploy (those
|
|
73
|
+
* whose ARN moved were already relaunched after the Database phase).
|
|
74
|
+
* Best-effort: every failure warns with a `fjall rollout` hint and never
|
|
75
|
+
* fails the deploy.
|
|
76
|
+
*/
|
|
77
|
+
export declare function reconcileDatabaseEndpointChange(params: DatabaseEndpointReconcileParams): Promise<DatabaseEndpointReconcileOutcome>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{maskSensitiveOutput as g}from"@fjall/util";import{logger as b}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 w="databaseEndpointReconcile",j=["Endpoint","Port"];async function I(a,t){const i=R(t,T.DATABASE),o=await a.getStackOutputs(i);if(!o.success){b.debug(w,"Could not read database stack outputs",{stackName:i,error:g(o.error.message)});return}const e={};for(const d of o.data){const r=d.OutputKey,l=d.OutputValue;r===void 0||l===void 0||j.some(u=>r.endsWith(u))&&(e[r]=l)}return e}async function W(a,t,i){const o=await a.getDeployableClusterAndServices(i);if(!o.success){b.debug(w,"Could not discover services pre-deploy",{appName:i,error:g(o.error.message)});return}const{clusterArn:e,serviceArns:d}=o.data;if(e===void 0||d.length===0)return{};const r=await t.describeServices(e,d);if(!r.success){b.debug(w,"Could not describe services pre-deploy",{appName:i,error:g(r.error.message)});return}const l={};for(const u of r.data)u.serviceArn!==void 0&&u.taskDefinition!==void 0&&(l[u.serviceArn]=u.taskDefinition);return l}function x(a,t){return Object.keys(a).filter(i=>t[i]!==void 0&&t[i]!==a[i]).sort()}async function q(a){const{appName:t,connectionOutputsBefore:i,services:o,callbacks:e,abortSignal:d}=a,r=`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(i===void 0)return e.onLog?.(l,"warn"),{kind:"unverified"};const u=await I(o.cfnService,t);if(u===void 0)return e.onLog?.(l,"warn"),{kind:"unverified"};const h=x(i,u);if(h.length===0)return{kind:"not-needed"};if(e.onLog?.(`Database connection outputs changed this deploy (${h.join(", ")}) \u2014 the database was replaced.`,"info"),C(d))return e.onLog?.(`Deploy cancelled before services were restarted onto the new database endpoint. ${r}`,"warn"),{kind:"cancelled"};const k=await o.ecsResolver.getDeployableClusterAndServices(t);if(!k.success)return e.onLog?.(`Could not discover this application's ECS services to restart them onto the new database endpoint: ${g(k.error.message)}. ${r}`,"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=a.serviceTaskDefinitionsBefore,L=A!==void 0&&Object.keys(A).length>0,v=[];let S=0,D=0;const y=(n,s)=>{e.onLog?.(`Could not restart ${n} onto the new database endpoint: ${g(s)}. ${r}`,"warn"),D+=1};for(const n of $){if(C(d))return e.onLog?.(`Deploy cancelled before services were restarted onto the new database endpoint. ${r}`,"warn"),{kind:"cancelled"};const s=N(n)??n,c=await O({ecsService:o.ecsService},m,n,s);if(!c.success){y(s,c.error.message);continue}const f=A?.[n];if(L&&f===void 0){S+=1;continue}if(f!==void 0&&f!==c.data.previousTaskDefArn){S+=1;continue}v.push({serviceArn:n,serviceName:s,resolved:c.data})}if(v.length===0)return D>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(a.confirmRestart!==void 0){const n={appName:t,changedOutputs:h,serviceCount:v.length};let s;try{s=await a.confirmRestart(n)}catch(c){return e.onLog?.(`Restart confirmation failed: ${g(c instanceof Error?c.message:String(c))}. ${r}`,"warn"),{kind:"failed"}}if(!s)return e.onLog?.(`Restart declined \u2014 running containers are still connected to the old database endpoint. ${r}`,"warn"),{kind:"declined"}}e.onLog?.(`Restarting ${v.length} service${v.length===1?"":"s"} so running containers reconnect to the new database\u2026`,"info");const p=[];for(const n of v){if(C(d))return e.onLog?.(`Deploy cancelled before all services were restarted onto the new database endpoint. ${r}`,"warn"),{kind:"cancelled"};let s=n.resolved;if(a.confirmRestart!==void 0){const f=await O({ecsService:o.ecsService},m,n.serviceArn,n.serviceName);if(!f.success){y(n.serviceName,f.error.message);continue}if(f.data.previousTaskDefArn!==n.resolved.previousTaskDefArn){e.onLog?.(`${n.serviceName} was relaunched while awaiting confirmation \u2014 skipping its restart.`,"info");continue}s=f.data}const c=await E({ecsService:o.ecsService},{clusterArn:m,serviceArn:n.serviceArn,serviceName:n.serviceName,taskDefinition:s.taskDefinition,containerDefinitions:s.containerDefinitions,previousTaskDefArn:s.previousTaskDefArn,startedAtMs:Date.now()},e);if(!c.success){y(n.serviceName,c.error.message);continue}p.push(n.serviceName)}return p.length>0&&(b.debug(w,"Restarted services after database replacement",{appName:t,changedOutputs:h,services:p,alreadyRolled:S}),e.onLog?.(`Restarted ${p.length} service${p.length===1?"":"s"} onto the new database endpoint.`,"info")),D>0?{kind:"failed"}:{kind:"reconciled",services:p}}export{I as captureDatabaseConnectionOutputs,W as captureServiceTaskDefinitions,q as reconcileDatabaseEndpointChange};
|
|
@@ -17,13 +17,21 @@ export interface DeploySessionContext {
|
|
|
17
17
|
* `AdministratorAccess` and the OIDC subject encodes no resource narrowing, so
|
|
18
18
|
* without this the session is an org-wide admin bearer credential.
|
|
19
19
|
*
|
|
20
|
-
*
|
|
20
|
+
* Three accepted v1 limitations (not app-confinement guarantees):
|
|
21
21
|
* - Session policies do not propagate across `sts:AssumeRole`, so mutation via
|
|
22
22
|
* the CDK deploy/cfn-exec roles is out of this policy's reach — the real
|
|
23
23
|
* confinement is the Phase E `FjallDeployBoundary` permission boundary.
|
|
24
24
|
* - The `stack/${appPascal}*` prefix wildcard over-grants a prefix-collision
|
|
25
25
|
* sibling app (`Web` vs `WebApp`) — same low-risk, same-account class as the
|
|
26
26
|
* ECR baseline-prefix note in `buildSecretSession.ts`.
|
|
27
|
+
* - The `-cache` NotResource entry exempts `repository/<appKebab>-cache` from
|
|
28
|
+
* the foreign-ECR Deny. CLI app creation reserves the `-cache` suffix
|
|
29
|
+
* (`hasReservedAppNameSuffix`, `@fjall/util`); the webapp create route does
|
|
30
|
+
* NOT yet enforce it (its pinned @fjall/util predates the helper — wiring
|
|
31
|
+
* lands with the 2.26.x bump), so until then a webapp-created sibling app
|
|
32
|
+
* could take that name. The residual beyond that is a pre-existing
|
|
33
|
+
* customer-created repo of that name in the BYOC account — same accepted
|
|
34
|
+
* class as `cdk-*`.
|
|
27
35
|
*
|
|
28
36
|
* DENY-list, not allow-list (deliberate). `fjall deploy` runs `cdk deploy`: the
|
|
29
37
|
* caller's session performs synth lookups, asset uploads to the CDK asset bucket,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{success as p,failure as s}from"@fjall/generator";import{toKebab as m,toPascalCase as d}from"@fjall/util";import{MAX_INLINE_POLICY_CHARS as
|
|
1
|
+
import{success as p,failure as s}from"@fjall/generator";import{toKebab as m,toPascalCase as d}from"@fjall/util";import{buildCacheRepositoryName as u}from"@fjall/util/docker";import{MAX_INLINE_POLICY_CHARS as i}from"./buildSecretSession.js";const f=["cloudformation:CreateStack","cloudformation:UpdateStack","cloudformation:DeleteStack","cloudformation:CreateChangeSet","cloudformation:ExecuteChangeSet","cloudformation:DeleteChangeSet","cloudformation:CancelUpdateStack","cloudformation:ContinueUpdateRollback","cloudformation:RollbackStack","cloudformation:SetStackPolicy","cloudformation:UpdateTerminationProtection"],y=["ecr:PutImage","ecr:InitiateLayerUpload","ecr:UploadLayerPart","ecr:CompleteLayerUpload","ecr:BatchDeleteImage","ecr:DeleteRepository","ecr:SetRepositoryPolicy","ecr:DeleteRepositoryPolicy"];function k(a){const{region:o,accountId:e,partition:t}=a,n=d(a.appName),c=m(a.appName),l=[{Sid:"FjallDeployInheritAdmin",Effect:"Allow",Action:"*",Resource:"*"},{Sid:"FjallDeployDenyForeignStackMutation",Effect:"Deny",Action:f,NotResource:[`arn:${t}:cloudformation:${o}:${e}:stack/${n}*/*`,`arn:${t}:cloudformation:${o}:${e}:stack/CDKToolkit/*`]},{Sid:"FjallDeployDenyForeignEcrMutation",Effect:"Deny",Action:y,NotResource:[`arn:${t}:ecr:${o}:${e}:repository/${c}`,`arn:${t}:ecr:${o}:${e}:repository/${u(c)}`,`arn:${t}:ecr:${o}:${e}:repository/cdk-*`]}],r=JSON.stringify({Version:"2012-10-17",Statement:l});return r.length>i?s(new Error(`Deploy session policy is ${r.length} chars, exceeding the ${i}-char AWS inline-policy limit.`)):p(r)}export{k as buildDeploySessionPolicy};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{isAbsolute as
|
|
1
|
+
import{isAbsolute as F,join as L,dirname as M,resolve as _}from"node:path";import{existsSync as K}from"node:fs";import{success as N,failure as h}from"@fjall/generator";import{deriveContentHashTag as W,maskSensitiveOutput as b,toKebab as x}from"@fjall/util";import{evaluateBakeGuard as z,evaluateResolvedBuildArgValues as X,acknowledgedBuildArgKeys as Y,buildCacheRepositoryName as H,BAKE_GUARD_EXPOSURE_CLAUSE as q}from"@fjall/util/docker";import{runWithBoundedConcurrency as J,resolveBuildConcurrency as Q}from"@fjall/util/buildConcurrency";import{logger as T}from"@fjall/util/logger";import{parseDockerServicesFromManifest as Z}from"@fjall/util/manifest";import{STEP_IDS as A}from"../../types/stepDefinitions.js";import{isAborted as U}from"../../aws/organisations/types.js";import{resolveBuildSecrets as ee,resolveSecretRefValue as re,resolveBuildSecretSessionProvider as te,sourcedRefsFromBuildArgs as ne}from"./buildSecretResolver.js";import{partitionForRegion as oe}from"./buildSecretSession.js";import{resolveBuildArgs as ie}from"./buildArgResolver.js";import{validateBuildGroup as se}from"./buildGroupValidator.js";import{ensureBuildCacheRepository as ae}from"./ecrCacheRepository.js";const I="dockerBuildHelper",C="Building and pushing Docker image";function de(e,n,t){const r=L(n,e);if(K(r))return{path:r,base:n};if(t!==""&&t!==n){const i=L(t,e);if(K(i))return{path:i,base:t}}return{path:r,base:n}}function ue(e,n,t){const r=e.docker.path,i=e.docker.context;let s,a;if(F(r))s=r,a=n;else{const p=de(r,n,t);s=p.path,a=p.base}return{buildContext:i?F(i)?_(i):_(a,i):_(M(s)),dockerfilePath:s,target:e.docker.target,resolvedViaFallback:a!==n}}function ce(e){return`${e.buildContext}|${e.dockerfilePath}|${e.target??""}`}function le(e,n){return`Dockerfile not found for service '${e}' at ${n}. Checked the synth directory and the working directory. If your Dockerfile lives at the project root with the CDK app in a subdirectory, set an absolute docker.context (the build context must share the Dockerfile's directory).`}function fe(e){return e.startsWith("cn-")?"amazonaws.com.cn":"amazonaws.com"}function G(e,n){return`${e}.dkr.ecr.${n}.${fe(n)}`}function ge(e,n,t){return`${G(e,n)}/${x(t)}`}function me(e,n,t){return`${G(e,n)}/${H(x(t))}`}function pe(e,n,t){const r=new Map;for(const i of e){const s=ue(i,n,t),a=ce(s),m=r.get(a);m?m.members.push(i):r.set(a,{identity:s,members:[i]})}return Array.from(r.values())}async function he(e,n,t,r,i){const s=await e.buildAndPush({appName:n.appName,appPath:n.path,region:t,accountId:r},(a,m,p,S)=>{i.onDockerProgress?.(b(a),m,p,S)});return s.success?N({imageUri:s.data.imageUri,imageTag:s.data.imageTag}):h(s.error)}function j(){return{contentHashTagsByService:{},imageDigestsByService:{}}}async function be(e,n,t,r,i,s,a,m,p,S,w,l,E){const c=e.members[0];if(!K(e.identity.dockerfilePath))return h(new Error(le(c.name,e.identity.dockerfilePath)));e.identity.resolvedViaFallback&&m.onLog?.(`Building service '${c.name}' from ${e.identity.dockerfilePath} (resolved at the project root).`,"info");const y=[...c.docker.buildSecrets??[],...E??[]],v=se({serviceName:c.name,dockerfilePath:e.identity.dockerfilePath,buildContext:e.identity.buildContext,...e.identity.target!==void 0&&{target:e.identity.target},...c.docker.buildArgs!==void 0&&{buildArgs:c.docker.buildArgs},...y.length>0&&{buildSecrets:y},members:e.members.map(o=>({serviceName:o.name,...o.docker.buildArgs!==void 0&&{buildArgs:o.docker.buildArgs},...o.docker.buildSecrets!==void 0&&{buildSecrets:o.docker.buildSecrets}}))});if(!v.success)return h(v.error);const k=z(c.docker.buildArgs,x(t.appName));for(const o of k.warnings)m.onLog?.(b(o.message),"warn");if(k.findings.length>0)return h(new Error(b(k.findings.map(o=>o.message).join(" "))));const d=y,f=ne(c.docker.buildArgs),D={appName:t.appName,region:r,accountId:i,partition:oe(r)},$=await te(p,[...d,...f],D);if(!$.success)return h($.error);const O=$.data,R=await ie({manifestBuildArgs:c.docker.buildArgs,serviceDir:e.identity.buildContext,appDir:t.path,...w!==void 0&&{stage:w},shellEnv:process.env,...l!==void 0&&{flagBuildArgs:l},resolveSourcedValue:(o,P)=>re(O,{id:o,...P},S)});if(!R.success)return h(R.error);const B=R.data.buildArgs;for(const o of R.data.inferredDotenvKeys)m.onLog?.(b(`buildArg "${o}" was auto-declared from a .env file and will be baked into the image \u2014 ${q}.`),"warn");const u=X(B,x(t.appName),Y(c.docker.buildArgs));if(u.length>0)return h(new Error(b(u.map(o=>o.message).join(" "))));let g;if(d.length>0){const o=await ee(O,d,S);if(!o.success)return h(o.error);g=o.data}try{if(U(S))return h(new Error(`Build of service '${c.name}' was aborted before the docker build started`));const o={appName:t.appName,appPath:e.identity.buildContext,region:r,accountId:i,buildContext:e.identity.buildContext,dockerfilePath:e.identity.dockerfilePath,pushByDigest:!0,serviceName:c.name,...a!==void 0&&{cacheRepoUri:a},...e.identity.target!==void 0&&{target:e.identity.target},...Object.keys(B).length>0&&{buildArgs:B},...g!==void 0&&{buildSecrets:g.resolved}};return await ve(e,o,n,s,m)}finally{g!==void 0&&await g.cleanup().catch(()=>m.onLog?.("Build-secret cleanup failed.","warn"))}}async function ve(e,n,t,r,i){const s=await t.buildAndPush(n,(l,E,c,y)=>{i.onDockerProgress?.(b(l),E,c,y)});if(!s.success)return h(s.error);const a=s.data.imageDigest;if(typeof a!="string"||!a.startsWith("sha256:"))return h(new Error(`Buildx push succeeded but returned an invalid digest: ${b(String(a))}`));const m={},p={},S=[];for(const l of e.members){const E=l.docker.target,c=W(a,l.name,E);if(!c.success)return h(c.error);const y=c.data,v=l.name;m[v]=y,p[v]=a,S.push(`${r}:${y}`)}const w=await t.tagByDigest({sourceImage:r,digest:a,tags:S});return w.success?N({contentHashTagsByService:m,imageDigestsByService:p}):h(w.error)}async function Te(e,n,t,r){const i=e.dockerProvider;if(!i)return N(j());const s=n.awsProvider.getAccountId();if(!s)return r.onLog?.("Skipping Docker build \u2014 account ID not available","warn"),N(j());const a=n.awsProvider.getRegion(),m=L(t.path,"cdk.out"),p=Z(m),S=ge(s,a,t.appName);if(T.debug(I,"runDockerBuild starting",{appName:t.appName,appPath:t.path,accountId:s,region:a,cdkOutPath:m,manifestServiceCount:p.length,manifestServices:p.map(d=>({name:d.name,path:d.docker.path,context:d.docker.context,target:d.docker.target})),stepId:A.DOCKER_OPERATIONS,stepName:C}),r.onStepStart?.(A.DOCKER_OPERATIONS,C),p.length===0){r.onLog?.("Initialising ECR repository\u2026","info");const d=await i.initialiseECR({appName:t.appName,region:a,accountId:s});d.success||r.onLog?.(`ECR initialisation failed: ${b(d.error.message)}`,"warn"),r.onLog?.("Building and pushing Docker image\u2026","info");const f=await he(i,t,a,s,r);if(!f.success){T.debug(I,"Docker buildAndPush (legacy) failed",{appName:t.appName,error:b(f.error.message)}),r.onStepComplete?.(A.DOCKER_OPERATIONS,C,"error");const D=new Error(b(f.error.message));return r.onError?.(D),h(D)}return r.onStepComplete?.(A.DOCKER_OPERATIONS,C,"completed"),r.onLog?.(`Docker image pushed: ${f.data.imageUri}`,"info"),N(j())}const w=await i.preflight?.();if(w!==void 0&&!w.success){r.onStepComplete?.(A.DOCKER_OPERATIONS,C,"error");const d=new Error(b(w.error.message));return r.onError?.(d),h(d)}const l=pe(p,t.path,e.workingDirectory);let E=me(s,a,t.appName);const c=await ae(n.awsProvider,H(x(t.appName)),e.abortSignal);c.success||(E=void 0,r.onLog?.(`Building without registry cache \u2014 ${c.error.message}`,"warn")),r.onLog?.(`Building and pushing ${p.length} service image(s) in ${l.length} group(s)\u2026`,"info");let y=!1;if(i.beginEcrSession!==void 0){const d=await i.beginEcrSession({accountId:s,region:a});if(!d.success){r.onStepComplete?.(A.DOCKER_OPERATIONS,C,"error");const f=new Error(b(d.error.message));return r.onError?.(f),h(f)}y=!0}const v=new AbortController,k=()=>v.abort();e.abortSignal!==void 0&&(e.abortSignal.aborted?v.abort():e.abortSignal.addEventListener("abort",k,{once:!0}));try{const d=()=>U(e.abortSignal)?"the deployment was cancelled":"a sibling group failed";let f;const D=Q(l.length),$=l.map((u,g)=>async()=>{const o=u.members.map(V=>V.name).join(", ");if(v.signal.aborted)throw new Error(`Build of group ${g+1}/${l.length} (${o}) was not started \u2014 ${d()}`);r.onLog?.(`Building group ${g+1}/${l.length} (${o})\u2026`,"info");const P=await be(u,i,t,a,s,S,E,r,n.awsProvider,v.signal,e.options?.environment,e.options?.flagBuildArgs,e.options?.flagBuildSecrets);if(!P.success)throw f===void 0&&(f={error:P.error,leader:u.members[0]?.name,members:o}),v.abort(),P.error;return P.data}),O=await J($,D,{abortOnFirstFailure:!0,abortSignal:v.signal});if(f===void 0)for(let u=0;u<l.length;u++){const g=O[u];if(!(g!==void 0&&g.success)){f={error:g!==void 0&&!g.success?g.error:new Error(`Build of group ${u+1}/${l.length} never ran \u2014 ${d()}`),leader:l[u].members[0]?.name,members:l[u].members.map(o=>o.name).join(", ")};break}}if(f!==void 0){T.debug(I,"Docker buildAndPush failed",{appName:t.appName,leader:f.leader,members:f.members,error:b(f.error.message)}),r.onStepComplete?.(A.DOCKER_OPERATIONS,C,"error");const u=new Error(b(f.error.message));return r.onError?.(u),h(u)}const R={},B={};for(const u of O)if(!(u===void 0||!u.success)){for(const[g,o]of Object.entries(u.data.contentHashTagsByService))R[g]=o;for(const[g,o]of Object.entries(u.data.imageDigestsByService))B[g]=o}return T.debug(I,"Docker buildAndPush succeeded",{appName:t.appName,services:p.map(u=>u.name),groupCount:l.length,contentHashTags:R}),r.onStepComplete?.(A.DOCKER_OPERATIONS,C,"completed"),r.onLog?.(`Docker images pushed for ${p.length} service(s)`,"info"),N({contentHashTagsByService:R,imageDigestsByService:B,repoUri:S})}finally{e.abortSignal?.removeEventListener("abort",k),y&&i.endEcrSession!==void 0&&await i.endEcrSession().catch(d=>{T.debug(I,"endEcrSession cleanup failed",{error:b(d instanceof Error?d.message:String(d))})})}}export{C as DOCKER_BUILD_STEP_NAME,j as emptyBuildProducts,Te as runDockerBuild};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type Result } from "@fjall/generator";
|
|
2
|
+
import type { AwsProvider } from "../../aws/AwsProvider.js";
|
|
3
|
+
/**
|
|
4
|
+
* Idempotently provision the buildx registry-cache repository beside the app's
|
|
5
|
+
* image repository: create-if-absent, then (re)apply the shared untagged-image
|
|
6
|
+
* lifecycle policy. Mirrors the `fjall build` provisioner
|
|
7
|
+
* (`@fjall/cli` EcrBuildOrchestrator → EcrRepositoryService) — both address the
|
|
8
|
+
* same physical repo via `buildCacheRepositoryName` and the shared policy text,
|
|
9
|
+
* so neither path can drift the other.
|
|
10
|
+
*
|
|
11
|
+
* Failure messages are pre-masked at construction — callers may surface
|
|
12
|
+
* `error.message` to log/callback sinks without re-masking.
|
|
13
|
+
*/
|
|
14
|
+
export declare function ensureBuildCacheRepository(awsProvider: AwsProvider, cacheRepositoryName: string, abortSignal?: AbortSignal): Promise<Result<void>>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{ECRClient as s,CreateRepositoryCommand as u,PutLifecyclePolicyCommand as m}from"@aws-sdk/client-ecr";import{success as y,failure as i}from"@fjall/generator";import{getErrorMessage as n,maskSensitiveOutput as c}from"@fjall/util";import{untaggedLifecyclePolicyText as d,CACHE_REPO_UNTAGGED_RETENTION_DAYS as p}from"@fjall/util/docker";import{logger as f}from"@fjall/util/logger";import{composeSdkAbortSignal as a,extractErrorName as g}from"../../aws/organisations/types.js";const C="EcrCacheRepository";async function O(l,r,t){const o=l.getClient(s);try{await o.send(new u({repositoryName:r,imageScanningConfiguration:{scanOnPush:!0},imageTagMutability:"MUTABLE"}),{abortSignal:a(t)}),f.debug(C,"Created build-cache repository",{cacheRepositoryName:r})}catch(e){if(g(e)!=="RepositoryAlreadyExistsException")return i(new Error(`Could not create build-cache repository '${r}': ${c(n(e))}`))}try{await o.send(new m({repositoryName:r,lifecyclePolicyText:d(p)}),{abortSignal:a(t)})}catch(e){return i(new Error(`Could not set lifecycle policy on build-cache repository '${r}': ${c(n(e))}`))}return y(void 0)}export{O as ensureBuildCacheRepository};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import{join as
|
|
2
|
-
`);
|
|
1
|
+
import{join as Y}from"node:path";import{OrganizationsClient as v}from"@aws-sdk/client-organizations";import{success as z,failure as B}from"@fjall/generator";import{ORGANISATION_TYPES as K,getOrganisationStackName as q}from"../../types/operations.js";import{synthOrFail as J,bootstrapOrFail as Q,forwardOutput as U,forwardResourceProgress as V}from"../contextHelpers.js";import{partitionAccounts as X,probeCascadeRoles as Z}from"../organisation/cascadeHelpers.js";import{accountTier as tt,maskSensitiveOutput as l}from"@fjall/util";import{findDevelopmentOuId as et}from"../../aws/organisations/organisationalUnits.js";import{buildOrgContext as ot,resolveOrgDetails as at}from"./orgContext.js";import{INFRA_STEPS as x,maskAndFail as C,readStackOutputsBestEffort as H}from"./infraSteps.js";import{resolveCascadeAccounts as rt}from"./resolveCascadeAccounts.js";import{executeCascade as nt}from"./cascadeExecution.js";import{reconcileOrgTrailOutputs as st}from"./trailReconciliation.js";async function yt(n,e,b,M){const{callbacks:t,options:k}=n,{providerAccounts:u,effectiveOrgConfig:j,defaultRegion:G}=await rt(n,e),p=await at(e,n.abortSignal);if(!p.success)return C(t,p.error.message);const m=await et(e.awsProvider.getClient(v),p.data.rootId,n.abortSignal);if(!m.success)return C(t,m.error.message);const W={...p.data,...m.data!==void 0?{devOuId:m.data}:{}},_=u.find(o=>tt(o)==="organisation"),d=ot(n,e,b,"organisation",W,void 0,_?.trailLifecycle),h=k?.cascade!==!1,{platformAccount:f,memberAccounts:I}=X(u),P=h&&f!==void 0?1:0,D=h&&I.length>0?1:0,s=2+P+D,E=h?[...f!==void 0?[f]:[],...I]:[];if(E.length>0){const o=await Z(e,E,t,n.abortSignal),r=f!==void 0?o.find(a=>a.accountId===f.id):void 0;if(r!==void 0)return C(t,`Pre-flight cascade role check failed for the platform account ${r.accountName} (${r.accountId}): ${r.error}`);for(const a of o)t.onProgress?.({type:"warning",message:l(`Pre-flight cascade role check failed for ${a.accountName} (${a.accountId}) \u2014 its cascade deploy is expected to fail: ${a.error}`)})}t.onCascadeAccountsReconciled?.({hasPlatformAccount:P>0,hasMemberAccounts:D>0});const{id:S,name:O}=x.PREPARE;t.onStepStart?.(S,O,0,s),t.onLog?.("Synthesising organisation infrastructure\u2026","info");const N=await J(e,d,t,"CDK synthesis failed");if(!N.success)return t.onStepComplete?.(S,O,"error",0,s),N;const F=await Q(e,d,t);if(!F.success)return t.onStepComplete?.(S,O,"error",0,s),F;t.onStepComplete?.(S,O,"completed",0,s);const{id:A,name:R}=x.ORG_DEPLOY,c=q(K.ORGANISATION);let T=!0;const g=await e.hashService.getTemplateHashes(Y(d.path,"cdk.out"));if(g.success){const o=await e.hashService.compareWithState(g.data,d.path);o.success?T=o.data.stackChanges.get(c)??!0:t.onLog?.(l(`Org root change detection failed \u2014 deploying to be safe: ${o.error.message}`),"warn")}else t.onLog?.(l(`Org root template hashing failed \u2014 deploying to be safe: ${g.error.message}`),"warn");const $=T||k?.force===!0||!await e.cfnService.stackExists(c);t.onOrgChangesDetected?.({hasOrgChanges:$});let w;if($){t.onStepStart?.(A,R,1,s);const o=await e.cdkService.runCdkDeploy(d,c,U(t),V(t),e.awsProvider);if(!o.success)return t.onStepComplete?.(A,R,"error",1,s),C(t,o.error);w=await H(e,t,c,"Failed to read org stack outputs (non-critical)");const r=g.success?g.data.get(c):void 0;if(r!==void 0){const a=await e.hashService.updateStateAfterDeploy(d.path,new Map([[c,r]]));a.success||t.onLog?.(`Warning: failed to update state file \u2014 next deploy may re-deploy the org root: ${l(a.error.message)}`,"warn")}t.onStepComplete?.(A,R,"completed",1,s)}else t.onLog?.("Organisation root: no infrastructure changes \u2014 skipping deploy","info"),w=await H(e,t,c,"Failed to read org stack outputs (non-critical)");const i=[],y=[];let L=$;if(h&&u.length>0){const{anyCascadeDeployHappened:o}=await nt(n,e,b,{providerAccounts:u,effectiveOrgConfig:j,totalSteps:s,cascadeErrors:i,allCascadeOutputs:y});if(o&&(L=!0),await st(n,e,{orgOutputs:w,allCascadeOutputs:y,orgDetails:p.data,providerAccounts:u,defaultRegion:G}),i.length>0){const r=i.map(a=>` ${a.accountId}: ${a.error}`).join(`
|
|
2
|
+
`);t.onLog?.(l(`Cascade failed for ${i.length} target(s):
|
|
3
3
|
${r}`),"warn")}}if(i.length>0){const o=i.map(a=>l(`${a.accountId}: ${a.error}`)).join(`
|
|
4
4
|
`),r=new Error(`Organisation root deployed, but the cascade failed for ${i.length} target(s):
|
|
5
|
-
${o}`);return
|
|
5
|
+
${o}`);return t.onError?.(r),B(r)}return z({target:b.target,deploymentType:"organisation",outputs:w,artefacts:[],...y.length>0?{cascadeOutputs:y}:{},...L?{}:{noChanges:!0},durationMs:Date.now()-M})}export{yt as deployOrgWithCascade};
|
|
@@ -7,6 +7,13 @@ export interface OrgDetailsForSynth {
|
|
|
7
7
|
orgId: string;
|
|
8
8
|
rootId: string;
|
|
9
9
|
managementAccountId: string;
|
|
10
|
+
/**
|
|
11
|
+
* Development OU id, resolved only for organisation-tier synths (the dev
|
|
12
|
+
* isolation SCP is synthesised by the Organisation stack alone). Absent =
|
|
13
|
+
* the org has no development OU, or the deploy path never synthesises the
|
|
14
|
+
* org stack.
|
|
15
|
+
*/
|
|
16
|
+
devOuId?: string;
|
|
10
17
|
}
|
|
11
18
|
export declare function buildOrgContext(params: DeployParams, services: DeployServices, operation: OrganisationOperation, deployType: "organisation" | "platform" | "account", orgDetails: OrgDetailsForSynth | undefined, accountName?: string, trailLifecycle?: ProviderAccount["trailLifecycle"], targetIsOrganisationTier?: boolean): import("../../types/deployment/DeploymentTypes.js").DeploymentContext;
|
|
12
19
|
export declare function resolveOrgDetails(services: DeployServices, abortSignal?: AbortSignal): Promise<Result<OrgDetailsForSynth>>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{success as s,failure as u}from"@fjall/generator";import{OrganizationsClient as
|
|
1
|
+
import{success as s,failure as u}from"@fjall/generator";import{OrganizationsClient as d}from"@aws-sdk/client-organizations";import{CdkContextBuilder as l}from"../../services/supporting/CdkContextBuilder.js";import{stubCallerIdentity as I}from"../../types/deployment/index.js";import{ensureOrganisationExists as m,describeOrganisation as f}from"../../aws/organisations/organisation.js";import{buildParamsContext as C,resolveAccountBootstrapSkipOidc as O}from"../contextHelpers.js";function w(o,n,e,t,r,a,c,g){const i=n.awsProvider.getRegion();return l.buildDeploymentContext({deployType:t,target:e.target,path:e.path,region:i,accountName:a,callerIdentity:I(n.awsProvider.getAccountId()),orgId:r?.orgId,rootId:r?.rootId,managementAccountId:r?.managementAccountId,devOuId:r?.devOuId,...C({orgConfig:o.orgConfig,identity:o.identity,skipOidc:O(t,o.orgConfig,o.options?.skipOidc,g),...t!=="organisation"?{region:i,primaryRegion:o.orgConfig?.primaryRegion}:{},trailLifecycle:c})},{verbose:o.options?.verbose,infraOnly:o.options?.infraOnly},o.orgConfig)}async function P(o,n){const e=o.awsProvider.getClient(d),t=await m(e,n);return t.success?s({orgId:t.data.orgId,rootId:t.data.rootId,managementAccountId:t.data.managementAccountId}):u(t.error)}async function k(o,n){const e=o.awsProvider.getClient(d),t=await f(e,n,{tolerateAccessDenied:!0});if(!(!t.success||t.data===null))return{orgId:t.data.orgId,rootId:t.data.rootId,managementAccountId:t.data.managementAccountId}}export{w as buildOrgContext,P as resolveOrgDetails,k as resolveOrgDetailsForSolo};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{filterDangerousEnvVars as
|
|
1
|
+
import{filterDangerousEnvVars as o}from"@fjall/util";class f{buildContextArgs(r){const e=[];return r?.accountId&&e.push("-c",`accountId=${r.accountId}`),r?.environment&&e.push("-c",`environment=${r.environment}`),r?.managedAccount&&e.push("-c","managedAccount=true"),r?.accountName&&e.push("-c",`accountName=${r.accountName}`),r?.orgId&&e.push("-c",`orgId=${r.orgId}`),r?.rootId&&e.push("-c",`rootId=${r.rootId}`),r?.managementAccountId&&e.push("-c",`managementAccountId=${r.managementAccountId}`),r?.devOuId&&e.push("-c",`fjallDevOuId=${r.devOuId}`),r?.ipamPoolId&&e.push("-c",`ipamPoolId=${r.ipamPoolId}`),r?.fjallOrgId&&e.push("-c",`fjallOrgId=${r.fjallOrgId}`),r?.fjallOidcConfigured&&e.push("-c",`fjallOidcConfigured=${r.fjallOidcConfigured}`),r?.fjallAccountGlobalsConfigured&&e.push("-c",`fjallAccountGlobalsConfigured=${r.fjallAccountGlobalsConfigured}`),r?.fjallAccountTrailState&&e.push("-c",`fjallAccountTrailState=${r.fjallAccountTrailState}`),r?.orgConfig&&e.push("-c",`orgConfig=${r.orgConfig}`),r?.fjallAdoptBackupVault&&e.push("-c","fjallAdoptBackupVault=true"),r?.resolvedSecretArns&&e.push("-c",`fjallResolvedSecretArns=${r.resolvedSecretArns}`),e}buildParameterArgs(r,e){if(r===void 0)return[];const l=Object.entries(r);if(l.length===0)return[];const i=e!==void 0&&e!==""?`${e}:`:"",n=[];for(const[a,u]of l){if(!/^[A-Za-z][A-Za-z0-9]*$/.test(a))throw new Error(`Invalid CloudFormation parameter name "${a}": must match /^[A-Za-z][A-Za-z0-9]*$/ (alphanumeric, leading letter, no separators).`);if(u==="")throw new Error(`CloudFormation parameter "${a}" has an empty value.`);if(/[,\n\r]/.test(u))throw new Error(`CloudFormation parameter "${a}" value contains "," or newline \u2014 cdk's --parameters splits on "," so the deploy would silently fragment.`);n.push("--parameters",`${i}${a}=${u}`)}return n}injectCascadeCredentials(r,e){e&&(r.AWS_ACCESS_KEY_ID=e.accessKeyId,r.AWS_SECRET_ACCESS_KEY=e.secretAccessKey,delete r.AWS_SESSION_TOKEN,e.sessionToken&&(r.AWS_SESSION_TOKEN=e.sessionToken))}buildCdkEnv(r){const e={...o(process.env),CI:"true",FORCE_COLOR:"0",CDK_DISABLE_VERSION_CHECK:"1",NODE_NO_WARNINGS:"1"};return r?.context?.region&&(e.AWS_REGION=r.context.region,e.AWS_DEFAULT_REGION=r.context.region,e.CDK_DEFAULT_REGION=r.context.region),r?.context?.accountId&&(e.CDK_DEFAULT_ACCOUNT=r.context.accountId),this.injectCascadeCredentials(e,r?.credentials),e}}export{f as CdkArgumentBuilder};
|
|
@@ -10,6 +10,13 @@ export interface CdkContext {
|
|
|
10
10
|
orgId?: string;
|
|
11
11
|
rootId?: string;
|
|
12
12
|
managementAccountId?: string;
|
|
13
|
+
/**
|
|
14
|
+
* Development OU id for the dev-isolation SCP (G6). Emitted as
|
|
15
|
+
* `-c fjallDevOuId=<id>`; keep the key literal in sync with the reader,
|
|
16
|
+
* CDK_CONTEXT_KEYS.DEV_OU_ID in @fjall/components-infrastructure
|
|
17
|
+
* (`utils/cdkContext.ts`).
|
|
18
|
+
*/
|
|
19
|
+
devOuId?: string;
|
|
13
20
|
ipamPoolId?: string;
|
|
14
21
|
fjallOrgId?: string;
|
|
15
22
|
fjallOidcConfigured?: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{getApplicationStackName as l,getOrganisationStackName as
|
|
1
|
+
import{getApplicationStackName as l,getOrganisationStackName as u,isApplicationStack as d}from"../../types/operations.js";const t=5e3;function c(a,e){if(a&&!a.includes("*"))return a;if(a){const o=a.match(/\*?(\w+)\*?/);if(o?.[1]){const n=o[1],r=e.target;return d(n)?l(r,n):`${r}${n}`}return a}}function f(a){const e=a.deployType;return e==="organisation"||e==="platform"||e==="account"?u(e):`${a.target}Network`}function p(a,e,o){return{accountId:e,region:o,environment:a.environment,managedAccount:a.isManagedAccount,accountName:a.accountName,orgId:a.orgId,rootId:a.rootId,managementAccountId:a.managementAccountId,devOuId:a.devOuId,ipamPoolId:a.ipamPoolId,fjallOrgId:a.fjallOrgId,fjallOidcConfigured:a.fjallOidcConfigured?"true":void 0,fjallAccountGlobalsConfigured:a.fjallAccountGlobalsConfigured?"true":void 0,fjallAccountTrailState:a.fjallAccountTrailState,orgConfig:a.orgConfig,fjallAdoptBackupVault:a.fjallAdoptBackupVault?"true":void 0,resolvedSecretArns:a.resolvedSecretArns}}export{t as STACK_DETECTION_FALLBACK_MS,p as buildDeploymentCdkContext,f as getFallbackStackName,c as resolveStackName};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{DEFAULT_REGION as l}from"@fjall/generator";class
|
|
1
|
+
import{DEFAULT_REGION as l}from"@fjall/generator";class t{static buildDeploymentContext(e,o,a){return{deployType:e.deployType,target:e.target,path:e.path,...e.assemblyDir!==void 0?{assemblyDir:e.assemblyDir}:{},...e.environment!==void 0?{environment:e.environment}:{},options:o,stackOutputs:e.stackOutputs||{},callerIdentity:e.callerIdentity,region:e.region||a?.primaryRegion||l,isManagedAccount:e.isManagedAccount,accountName:e.accountName,logPath:e.logPath,orgId:e.orgId,rootId:e.rootId,managementAccountId:e.managementAccountId,devOuId:e.devOuId,ipamPoolId:e.ipamPoolId,fjallOrgId:e.fjallOrgId,fjallOidcConfigured:e.fjallOidcConfigured,fjallAccountGlobalsConfigured:e.fjallAccountGlobalsConfigured,fjallAccountTrailState:e.fjallAccountTrailState,orgConfig:e.orgConfig}}static updateContext(e,o){return{...e,...o}}}export{t as CdkContextBuilder};
|
|
@@ -10,7 +10,7 @@ export { parseDeployEvent, parseDeployContractVersion, type ParsedDeployEvent }
|
|
|
10
10
|
export type { ProgressEvent, ProgressEventType, ResourceEvent, AwsAuthResult, CascadeDeploymentResult, CascadePhase, BuildPushStartEvent, BuildPushProgressEvent, BuildPushCompleteEvent, TaskDefRegisteredEvent, ECSCompleteEvent, MigrationsStartEvent, MigrationsCompleteEvent, TrailMigrationPhase, TrailMigrationStatus, TrailMigrationPhaseEvent } from "./events.js";
|
|
11
11
|
export { TRAIL_MIGRATION_PHASES, TRAIL_MIGRATION_STATUSES } from "./events.js";
|
|
12
12
|
export type { ApiClientInterface, EntitlementsData } from "./apiClient.js";
|
|
13
|
-
export type { DeployParams, DeployOptions, DeploymentType, DeployResult, DestroyParams, DestroyOptions, DestroyResult } from "./params.js";
|
|
13
|
+
export type { DeployParams, DeployOptions, DatabaseEndpointRestartRequest, DeploymentType, DeployResult, DestroyParams, DestroyOptions, DestroyResult } from "./params.js";
|
|
14
14
|
export type { ApprovalGate, ApprovalGatePlanDetail, ApprovalRequest, GateResolution } from "./approval.js";
|
|
15
15
|
export type { OrgConfig, ProviderAccount, RootAccessManagementMode, SSOSession } from "./config/orgConfig.js";
|
|
16
16
|
export type { Entitlements } from "./config/entitlements.js";
|
|
@@ -76,6 +76,25 @@ export interface DeployParams {
|
|
|
76
76
|
* the gate even when this is omitted.
|
|
77
77
|
*/
|
|
78
78
|
approvalGate?: ApprovalGate;
|
|
79
|
+
/**
|
|
80
|
+
* Asked before the post-deploy database-endpoint reconcile restarts the
|
|
81
|
+
* application's ECS services (fires only when a deploy replaced the
|
|
82
|
+
* database — e.g. a snapshot restore — without also rolling a fresh image).
|
|
83
|
+
* Omitted ⇒ restart without asking: running containers are actively writing
|
|
84
|
+
* to the doomed old database, so automation surfaces (worker,
|
|
85
|
+
* non-interactive CLI) must not block on consent.
|
|
86
|
+
*/
|
|
87
|
+
confirmDatabaseEndpointRestart?: (request: DatabaseEndpointRestartRequest) => Promise<boolean>;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Context handed to `confirmDatabaseEndpointRestart` so an interactive
|
|
91
|
+
* surface can render what changed and what a "yes" will restart.
|
|
92
|
+
*/
|
|
93
|
+
export interface DatabaseEndpointRestartRequest {
|
|
94
|
+
appName: string;
|
|
95
|
+
/** Database-stack output keys whose value changed (e.g. `AppDbEndpoint`). */
|
|
96
|
+
changedOutputs: string[];
|
|
97
|
+
serviceCount: number;
|
|
79
98
|
}
|
|
80
99
|
export interface DeployOptions {
|
|
81
100
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fjall/deploy-core",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.27.0",
|
|
4
4
|
"description": "Shared deployment engine for Fjall — used by CLI and webapp worker",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/src/index.js",
|
|
@@ -82,8 +82,8 @@
|
|
|
82
82
|
"@aws-sdk/client-ssm": "^3.1038.0",
|
|
83
83
|
"@aws-sdk/client-sso-admin": "^3.1038.0",
|
|
84
84
|
"@aws-sdk/client-sts": "^3.1038.0",
|
|
85
|
-
"@fjall/generator": "^2.
|
|
86
|
-
"@fjall/util": "^2.
|
|
85
|
+
"@fjall/generator": "^2.27.0",
|
|
86
|
+
"@fjall/util": "^2.27.0",
|
|
87
87
|
"@smithy/node-http-handler": "^4.6.1",
|
|
88
88
|
"aws-cdk": "^2.1128.1",
|
|
89
89
|
"tsx": "^4.21.0",
|
|
@@ -94,5 +94,5 @@
|
|
|
94
94
|
"typescript": "^6.0.3",
|
|
95
95
|
"vitest": "^4.1.5"
|
|
96
96
|
},
|
|
97
|
-
"gitHead": "
|
|
97
|
+
"gitHead": "921ecd4f65f52c17037a8a24834d7e4c196b9347"
|
|
98
98
|
}
|