@fjall/deploy-core 2.20.1 → 2.22.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/AwsProvider.d.ts +9 -0
- package/dist/src/aws/index.d.ts +3 -1
- package/dist/src/aws/index.js +1 -1
- package/dist/src/aws/regions/regionOptStatus.d.ts +26 -0
- package/dist/src/aws/regions/regionOptStatus.js +1 -0
- package/dist/src/aws/targetReadiness.d.ts +9 -0
- package/dist/src/aws/targetReadiness.js +1 -1
- package/dist/src/aws/utils/cloudformationEventHelpers.js +1 -1
- package/dist/src/aws/utils/cloudformationEventTypes.d.ts +6 -0
- package/dist/src/aws/utils/cloudformationEvents.d.ts +1 -0
- package/dist/src/aws/utils/cloudformationEvents.js +1 -1
- package/dist/src/index.d.ts +11 -3
- package/dist/src/index.js +1 -1
- package/dist/src/orchestration/application/applicationDeploy.js +1 -1
- package/dist/src/orchestration/application/applicationDeployHelpers.js +2 -2
- package/dist/src/orchestration/application/approvalGate.d.ts +44 -0
- package/dist/src/orchestration/application/approvalGate.js +1 -0
- package/dist/src/orchestration/application/buildArgResolver.d.ts +72 -0
- package/dist/src/orchestration/application/buildArgResolver.js +1 -0
- package/dist/src/orchestration/application/buildGroupValidator.d.ts +64 -0
- package/dist/src/orchestration/application/buildGroupValidator.js +1 -0
- package/dist/src/orchestration/application/buildSecretResolver.d.ts +57 -0
- package/dist/src/orchestration/application/buildSecretResolver.js +1 -0
- package/dist/src/orchestration/application/buildSecretSession.d.ts +40 -0
- package/dist/src/orchestration/application/buildSecretSession.js +1 -0
- package/dist/src/orchestration/application/codeOnlyDeploy.js +1 -1
- package/dist/src/orchestration/application/dockerBuildHelper.js +1 -1
- package/dist/src/orchestration/application/plan/approvalToken.d.ts +40 -0
- package/dist/src/orchestration/application/plan/approvalToken.js +1 -0
- package/dist/src/orchestration/application/plan/assemblyDigest.d.ts +21 -0
- package/dist/src/orchestration/application/plan/assemblyDigest.js +2 -0
- package/dist/src/orchestration/application/plan/buildDeployPlan.d.ts +14 -0
- package/dist/src/orchestration/application/plan/buildDeployPlan.js +1 -0
- package/dist/src/orchestration/application/plan/classify.d.ts +37 -0
- package/dist/src/orchestration/application/plan/classify.js +1 -0
- package/dist/src/orchestration/application/plan/computeDeployPlan.d.ts +24 -0
- package/dist/src/orchestration/application/plan/computeDeployPlan.js +1 -0
- package/dist/src/orchestration/application/plan/index.d.ts +8 -0
- package/dist/src/orchestration/application/plan/index.js +1 -0
- package/dist/src/orchestration/application/plan/renderDeployPlan.d.ts +18 -0
- package/dist/src/orchestration/application/plan/renderDeployPlan.js +1 -0
- package/dist/src/orchestration/application/plan/types.d.ts +54 -0
- package/dist/src/orchestration/application/plan/types.js +0 -0
- package/dist/src/orchestration/deploy.js +1 -1
- package/dist/src/orchestration/destroy.js +1 -1
- package/dist/src/orchestration/dockerBuildParamsKeys.d.ts +2 -0
- package/dist/src/orchestration/dockerBuildParamsKeys.js +1 -0
- package/dist/src/orchestration/dockerInterface.d.ts +34 -0
- package/dist/src/orchestration/index.d.ts +8 -1
- package/dist/src/orchestration/index.js +1 -1
- package/dist/src/orchestration/serviceFactory.d.ts +7 -0
- package/dist/src/orchestration/serviceFactory.js +1 -1
- package/dist/src/orchestration/stepLifecycle.d.ts +7 -1
- package/dist/src/orchestration/stepLifecycle.js +1 -1
- package/dist/src/services/infrastructure/CdkEventMonitoring.d.ts +3 -0
- package/dist/src/services/infrastructure/CdkEventMonitoring.js +1 -1
- package/dist/src/services/infrastructure/CdkProcessManager.d.ts +10 -1
- package/dist/src/services/infrastructure/CdkProcessManager.js +3 -3
- package/dist/src/services/infrastructure/CdkService.d.ts +1 -0
- package/dist/src/services/infrastructure/CdkService.js +2 -2
- package/dist/src/services/infrastructure/CdkServiceTypes.d.ts +15 -0
- package/dist/src/services/infrastructure/CloudFormationService.d.ts +11 -0
- package/dist/src/services/infrastructure/CloudFormationService.js +1 -1
- package/dist/src/services/infrastructure/cancelInFlightStack.d.ts +22 -0
- package/dist/src/services/infrastructure/cancelInFlightStack.js +3 -0
- package/dist/src/types/approval.d.ts +53 -0
- package/dist/src/types/approval.js +0 -0
- package/dist/src/types/callbackKeys.d.ts +1 -1
- package/dist/src/types/callbackKeys.js +1 -1
- package/dist/src/types/callbacks.d.ts +18 -4
- package/dist/src/types/config/entitlements.d.ts +7 -0
- package/dist/src/types/deployEmitter.d.ts +85 -0
- package/dist/src/types/deployEmitter.js +1 -0
- package/dist/src/types/deployEvent.d.ts +907 -0
- package/dist/src/types/deployEvent.js +1 -0
- package/dist/src/types/deployEventParse.d.ts +52 -0
- package/dist/src/types/deployEventParse.js +1 -0
- package/dist/src/types/deployEventTypeGuards.d.ts +10 -0
- package/dist/src/types/deployEventTypeGuards.js +1 -0
- package/dist/src/types/index.d.ts +6 -1
- package/dist/src/types/index.js +1 -1
- package/dist/src/types/params.d.ts +103 -0
- package/package.json +8 -4
package/dist/.minified
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
174 files minified at 2026-06-30T11:38:21.686Z
|
|
@@ -23,6 +23,15 @@ export interface AwsProvider {
|
|
|
23
23
|
* Used by organisation cascade to assume roles in member accounts.
|
|
24
24
|
*/
|
|
25
25
|
assumeRole?(roleArn: string, sessionName: string): Promise<AwsProviderCredentials>;
|
|
26
|
+
/**
|
|
27
|
+
* Assume the SAME identity but with an inline session policy that scopes it
|
|
28
|
+
* down to exactly the SSM / Secrets Manager parameters a build needs to read
|
|
29
|
+
* (least privilege). Returns a provider bound to the scoped session. When a
|
|
30
|
+
* provider cannot narrow its own session (the worker's pre-assumed OIDC
|
|
31
|
+
* credentials), this is absent and the caller falls back to the unscoped
|
|
32
|
+
* provider — see `resolveBuildSecrets`.
|
|
33
|
+
*/
|
|
34
|
+
assumeScopedBuildSecretSession?(inlinePolicy: string, sessionName: string): Promise<AwsProvider>;
|
|
26
35
|
}
|
|
27
36
|
export interface AwsProviderCredentials {
|
|
28
37
|
accessKeyId: string;
|
package/dist/src/aws/index.d.ts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
export type { AwsProvider, AwsProviderCredentials, AwsSdkClientConstructor } from "./AwsProvider.js";
|
|
2
2
|
export { SimpleAwsProvider } from "./SimpleAwsProvider.js";
|
|
3
|
-
export { checkTargetReadiness, describeTargetReadinessReason, buildTargetUnreadyAdvisory } from "./targetReadiness.js";
|
|
3
|
+
export { checkTargetReadiness, describeTargetReadinessReason, buildTargetUnreadyAdvisory, probeAccountStackExists } from "./targetReadiness.js";
|
|
4
4
|
export type { TargetReadinessAccount, TargetReadinessClients, TargetReadinessReason, TargetReadinessVerdict } from "./targetReadiness.js";
|
|
5
5
|
export { buildTargetSetUnreadyAdvisory } from "./targetSetAdvisory.js";
|
|
6
6
|
export type { TargetSetUnreadyAdvisoryParams } from "./targetSetAdvisory.js";
|
|
7
|
+
export { checkRegionEnabled, regionDisabledMessage } from "./regions/regionOptStatus.js";
|
|
8
|
+
export type { RegionAvailability } from "./regions/regionOptStatus.js";
|
|
7
9
|
export { ensureOrganisationExists, describeOrganisation, enablePolicyTypes, enableServiceAccess, SERVICE_PRINCIPALS, enableRamSharing, activateTrustedAccess, enableIpamDelegatedAdmin, updateBackupGlobalSettings, listAccounts, findAccount, createAccount, ensureOrganisationalUnitsExist, placeAccountsInOUs, buildAccountToOUMap, activateCostAllocationTags, checkIdentityCentreStatus, extractErrorName, isOULeaf, registerSecurityDelegates, SECURITY_SERVICE_PRINCIPALS, enableCentralisedRootAccess, ROOT_ACCESS_FEATURES, RootAccessEnablementError } from "./organisations/index.js";
|
|
8
10
|
export type { OrgDetails, OUMap, OUTree, AccountPlacementResult, AccountInfo, IdentityCentreStatus, BackupGlobalSettings, CostAllocationTag, CreateAccountResult, RootAccessFeature, RootAccessEnablementSummary } from "./organisations/index.js";
|
|
9
11
|
export { CloudFormationEventMonitor } from "./utils/cloudformationEvents.js";
|
package/dist/src/aws/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{SimpleAwsProvider as a}from"./SimpleAwsProvider.js";import{checkTargetReadiness as s,describeTargetReadinessReason as o,buildTargetUnreadyAdvisory as n}from"./targetReadiness.js";import{buildTargetSetUnreadyAdvisory as
|
|
1
|
+
import{SimpleAwsProvider as a}from"./SimpleAwsProvider.js";import{checkTargetReadiness as s,describeTargetReadinessReason as o,buildTargetUnreadyAdvisory as n,probeAccountStackExists as i}from"./targetReadiness.js";import{buildTargetSetUnreadyAdvisory as l}from"./targetSetAdvisory.js";import{checkRegionEnabled as A,regionDisabledMessage as u}from"./regions/regionOptStatus.js";import{ensureOrganisationExists as E,describeOrganisation as S,enablePolicyTypes as b,enableServiceAccess as p,SERVICE_PRINCIPALS as R,enableRamSharing as m,activateTrustedAccess as C,enableIpamDelegatedAdmin as T,updateBackupGlobalSettings as x,listAccounts as I,findAccount as f,createAccount as y,ensureOrganisationalUnitsExist as O,placeAccountsInOUs as U,buildAccountToOUMap as v,activateCostAllocationTags as P,checkIdentityCentreStatus as k,extractErrorName as _,isOULeaf as h,registerSecurityDelegates as D,SECURITY_SERVICE_PRINCIPALS as L,enableCentralisedRootAccess as M,ROOT_ACCESS_FEATURES as N,RootAccessEnablementError as F}from"./organisations/index.js";import{CloudFormationEventMonitor as w}from"./utils/cloudformationEvents.js";export{w as CloudFormationEventMonitor,N as ROOT_ACCESS_FEATURES,F as RootAccessEnablementError,L as SECURITY_SERVICE_PRINCIPALS,R as SERVICE_PRINCIPALS,a as SimpleAwsProvider,P as activateCostAllocationTags,C as activateTrustedAccess,v as buildAccountToOUMap,l as buildTargetSetUnreadyAdvisory,n as buildTargetUnreadyAdvisory,k as checkIdentityCentreStatus,A as checkRegionEnabled,s as checkTargetReadiness,y as createAccount,S as describeOrganisation,o as describeTargetReadinessReason,M as enableCentralisedRootAccess,T as enableIpamDelegatedAdmin,b as enablePolicyTypes,m as enableRamSharing,p as enableServiceAccess,E as ensureOrganisationExists,O as ensureOrganisationalUnitsExist,_ as extractErrorName,f as findAccount,h as isOULeaf,I as listAccounts,U as placeAccountsInOUs,i as probeAccountStackExists,u as regionDisabledMessage,D as registerSecurityDelegates,x as updateBackupGlobalSettings};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { AwsProviderCredentials } from "../AwsProvider.js";
|
|
2
|
+
/**
|
|
3
|
+
* Verdict for a deploy region's opt-in status.
|
|
4
|
+
*
|
|
5
|
+
* `unknown` is the fail-open outcome: the Account API was unreachable, denied,
|
|
6
|
+
* throttled, or timed out, so the caller proceeds — a pre-flight must never
|
|
7
|
+
* block a deploy on its own probe. The real error then surfaces at
|
|
8
|
+
* synth/bootstrap if the region truly is unusable.
|
|
9
|
+
*/
|
|
10
|
+
export type RegionAvailability = "enabled" | "disabled" | "enabling" | "unknown";
|
|
11
|
+
/**
|
|
12
|
+
* Resolve whether `region` is usable for a deploy in this account.
|
|
13
|
+
*
|
|
14
|
+
* Non-opt-in regions are enabled by default and short-circuit to "enabled"
|
|
15
|
+
* with NO API call. Opt-in regions are probed via the Account API
|
|
16
|
+
* (`GetRegionOptStatus`) against an always-on endpoint, with the per-call
|
|
17
|
+
* timeout composed against any caller shutdown signal. Any failure maps to
|
|
18
|
+
* "unknown" (fail-open).
|
|
19
|
+
*/
|
|
20
|
+
export declare function checkRegionEnabled(region: string, credentials: AwsProviderCredentials | undefined, abortSignal?: AbortSignal): Promise<RegionAvailability>;
|
|
21
|
+
/**
|
|
22
|
+
* Canonical operator-facing message for a confirmed-disabled opt-in region.
|
|
23
|
+
* Shared by the deploy-entry hard block and the CLI connect pre-flight so the
|
|
24
|
+
* wording cannot drift (reuses `optInRegionWarning` from `@fjall/util`).
|
|
25
|
+
*/
|
|
26
|
+
export declare function regionDisabledMessage(region: string): string;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{AccountClient as a,GetRegionOptStatusCommand as o}from"@aws-sdk/client-account";import{OPT_IN_REGION_CODES as r,optInRegionWarning as i}from"@fjall/util";import{composeSdkAbortSignal as s}from"../organisations/types.js";const c=1e4,l="us-east-1";async function A(n,e,t){if(!r.has(n))return"enabled";try{switch((await new a({region:l,credentials:e}).send(new o({RegionName:n}),{abortSignal:s(t,c)})).RegionOptStatus){case"ENABLED":case"ENABLED_BY_DEFAULT":return"enabled";case"DISABLED":case"DISABLING":return"disabled";case"ENABLING":return"enabling";default:return"unknown"}}catch{return"unknown"}}function S(n){return`Region ${n} is not enabled in this AWS account. ${i(n)} Enable it and wait for activation, then retry \u2014 or deploy to a region that is already enabled.`}export{A as checkRegionEnabled,S as regionDisabledMessage};
|
|
@@ -52,6 +52,15 @@ export declare function buildTargetUnreadyAdvisory(params: {
|
|
|
52
52
|
reasons: TargetReadinessReason[];
|
|
53
53
|
providerAccounts?: OrgConfig["providerAccounts"];
|
|
54
54
|
}): string;
|
|
55
|
+
/**
|
|
56
|
+
* Probe whether the `Account` CloudFormation stack exists and is provisioned
|
|
57
|
+
* in `region`. `success(true)` = a usable stack; `success(false)` = absent or
|
|
58
|
+
* an unprovisioned shell (failed/rolled-back create, unexecuted change-set,
|
|
59
|
+
* in-flight/failed delete); `failure` = the describe itself errored. Exported
|
|
60
|
+
* so connect-time deploy-state probes reuse the same unprovisioned-status
|
|
61
|
+
* interpretation as the deploy readiness path rather than re-deriving it.
|
|
62
|
+
*/
|
|
63
|
+
export declare function probeAccountStackExists(client: CloudFormationClient, region: string, abortSignal?: AbortSignal): Promise<Result<boolean>>;
|
|
55
64
|
/**
|
|
56
65
|
* Probe whether a deploy target is provisioned before any AWS mutation:
|
|
57
66
|
* the fixed-name Account stack must exist in the target region (both shapes),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{DescribeStacksCommand as N}from"@aws-sdk/client-cloudformation";import{DescribeIpamPoolsCommand as S}from"@aws-sdk/client-ec2";import{success as i,failure as c}from"@fjall/generator";import{accountTier as A,generateTargetName as g,getErrorMessage as E}from"@fjall/util";import{formatIpamPairTagValue as y,IPAM_OPERATIONS_POOL_TAG_KEY as O,STACK_NOT_FOUND_PATTERN as k}from"@fjall/util/aws";import{getOrganisationStackName as _,ORGANISATION_TYPES as R}from"../types/operations.js";import{hasOrganisationTierAccount as T}from"./organisations/accountGlobals.js";import{composeSdkAbortSignal as I,isAborted as P}from"./organisations/types.js";const m=_(R.ACCOUNT),w=new Set(["CREATE_FAILED","ROLLBACK_IN_PROGRESS","ROLLBACK_COMPLETE","ROLLBACK_FAILED","REVIEW_IN_PROGRESS","DELETE_IN_PROGRESS","DELETE_FAILED"]);function $(e){switch(e.kind){case"missing-account-stack":return`the ${e.stackName} stack was not found in ${e.region}`;case"missing-ipam-pool":return`no IPAM pool exists for account ${e.accountId} in ${e.region}`}}function L(e){const t=e.accountName??e.accountId,n=e.reasons.map($).join(" and "),o=e.accountName!==void 0?`run \`fjall target set ${g(e.accountName,e.region)}\` then \`fjall deploy account\`, then retry this deploy`:"run `fjall target list` to find the target name, `fjall target set <target>`, then `fjall deploy account`, then retry this deploy",r=T(e.providerAccounts)?"run `fjall deploy organisation` (the cascade provisions every account and creates the IPAM pool), then retry this deploy":o;return`Target "${t}" is not ready in ${e.region}: ${n}. Nothing has been deployed. Provision the target first: ${r}.`}function b(e){return e instanceof Error&&e.name==="ValidationError"&&e.message.includes(k)}async function C(e,t,n){try{const r=(await e.send(new N({StackName:m}),{abortSignal:I(n)})).Stacks?.[0]?.StackStatus;return i(r!==void 0&&!w.has(r))}catch(o){return b(o)?i(!1):c(new Error(`Failed to probe the ${m} stack in ${t}: ${E(o)}`))}}function
|
|
1
|
+
import{DescribeStacksCommand as N}from"@aws-sdk/client-cloudformation";import{DescribeIpamPoolsCommand as S}from"@aws-sdk/client-ec2";import{success as i,failure as c}from"@fjall/generator";import{accountTier as A,generateTargetName as g,getErrorMessage as E}from"@fjall/util";import{formatIpamPairTagValue as y,IPAM_OPERATIONS_POOL_TAG_KEY as O,STACK_NOT_FOUND_PATTERN as k}from"@fjall/util/aws";import{getOrganisationStackName as _,ORGANISATION_TYPES as R}from"../types/operations.js";import{hasOrganisationTierAccount as T}from"./organisations/accountGlobals.js";import{composeSdkAbortSignal as I,isAborted as P}from"./organisations/types.js";const m=_(R.ACCOUNT),w=new Set(["CREATE_FAILED","ROLLBACK_IN_PROGRESS","ROLLBACK_COMPLETE","ROLLBACK_FAILED","REVIEW_IN_PROGRESS","DELETE_IN_PROGRESS","DELETE_FAILED"]);function $(e){switch(e.kind){case"missing-account-stack":return`the ${e.stackName} stack was not found in ${e.region}`;case"missing-ipam-pool":return`no IPAM pool exists for account ${e.accountId} in ${e.region}`}}function L(e){const t=e.accountName??e.accountId,n=e.reasons.map($).join(" and "),o=e.accountName!==void 0?`run \`fjall target set ${g(e.accountName,e.region)}\` then \`fjall deploy account\`, then retry this deploy`:"run `fjall target list` to find the target name, `fjall target set <target>`, then `fjall deploy account`, then retry this deploy",r=T(e.providerAccounts)?"run `fjall deploy organisation` (the cascade provisions every account and creates the IPAM pool), then retry this deploy":o;return`Target "${t}" is not ready in ${e.region}: ${n}. Nothing has been deployed. Provision the target first: ${r}.`}function b(e){return e instanceof Error&&e.name==="ValidationError"&&e.message.includes(k)}async function C(e,t,n){try{const r=(await e.send(new N({StackName:m}),{abortSignal:I(n)})).Stacks?.[0]?.StackStatus;return i(r!==void 0&&!w.has(r))}catch(o){return b(o)?i(!1):c(new Error(`Failed to probe the ${m} stack in ${t}: ${E(o)}`))}}function x(e,t,n,o){return e.Locale!==n?!1:o.side==="owner"?(e.Tags??[]).some(r=>r.Key===O&&r.Value===o.pairTagValue):e.OwnerId===t?!1:o.platformAccountId===void 0||e.OwnerId===o.platformAccountId}async function v(e,t,n,o,r){try{let a;do{if(P(r))return c(new Error(`Aborted: IPAM pool probe for account ${t} in ${n} cancelled by shutdown signal`));const s=await e.send(new S({...a!==void 0&&{NextToken:a}}),{abortSignal:I(r)});if((s.IpamPools??[]).some(u=>x(u,t,n,o)))return i(!0);a=s.NextToken}while(a!==void 0);return i(!1)}catch(a){return c(new Error(`Failed to probe IPAM pools for account ${t} in ${n}: ${E(a)}`))}}async function B(e,t,n,o,r){const a=[],s=await C(e.cloudFormation,n,r);if(!s.success)return c(s.error);s.data||a.push({kind:"missing-account-stack",stackName:m,region:n});const d=o?.providerAccounts;if(T(d)){const u=(d??[]).find(l=>l.id===t.id),p=(d??[]).find(l=>A(l)==="platform")?.id,h=u!==void 0&&A(u)==="platform"?{side:"owner",pairTagValue:y(t.id,n)}:{side:"consumer",...p!==void 0&&{platformAccountId:p}},f=await v(e.ec2,t.id,n,h,r);if(!f.success)return c(f.error);f.data||a.push({kind:"missing-ipam-pool",accountId:t.id,region:n})}return a.length===0?i({ready:!0}):i({ready:!1,reasons:a,advisory:L({accountId:t.id,...t.name!==void 0&&{accountName:t.name},region:n,reasons:a,...o!==void 0&&{providerAccounts:o.providerAccounts}})})}export{m as ACCOUNT_STACK_NAME,L as buildTargetUnreadyAdvisory,B as checkTargetReadiness,$ as describeTargetReadinessReason,C as probeAccountStackExists};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{CloudFormationClient as g,DescribeStackEventsCommand as y,DescribeStacksCommand as A}from"@aws-sdk/client-cloudformation";import{logger as m}from"@fjall/util/logger";import{getErrorMessage as S,maskSensitiveOutput as C}from"@fjall/util";import{STACK_NOT_FOUND_PATTERN as f}from"@fjall/util/aws";import{CF_STACK_RESOURCE_TYPE as I}from"./cloudformationEventTypes.js";import{extractErrorName as P}from"../organisations/types.js";const
|
|
1
|
+
import{CloudFormationClient as g,DescribeStackEventsCommand as y,DescribeStacksCommand as A}from"@aws-sdk/client-cloudformation";import{logger as m}from"@fjall/util/logger";import{getErrorMessage as S,maskSensitiveOutput as C}from"@fjall/util";import{STACK_NOT_FOUND_PATTERN as f,maskFailureAnalysis as L}from"@fjall/util/aws";import{CF_STACK_RESOURCE_TYPE as I}from"./cloudformationEventTypes.js";import{extractErrorName as P}from"../organisations/types.js";const h=["CREATE_COMPLETE","CREATE_FAILED","DELETE_COMPLETE","DELETE_FAILED","UPDATE_COMPLETE","UPDATE_FAILED","UPDATE_ROLLBACK_COMPLETE","UPDATE_ROLLBACK_FAILED","ROLLBACK_COMPLETE","ROLLBACK_FAILED","IMPORT_COMPLETE","IMPORT_ROLLBACK_COMPLETE","IMPORT_ROLLBACK_FAILED"],k=["CREATE_COMPLETE","UPDATE_COMPLETE","DELETE_COMPLETE","IMPORT_COMPLETE"];function O(s){return h.includes(s)}function b(s){return k.includes(s)}const M=new Set(["AWS::EC2::IPAMPool","AWS::EC2::IPAMPoolCidr","AWS::EC2::IPAM"]);function _(s){return M.has(s)}class v{inProgress=new Map;get size(){return this.inProgress.size}clear(){this.inProgress.clear()}trackEvent(e,i){if(e.status.includes("IN_PROGRESS")){this.inProgress.set(e.logicalId,{resourceType:e.resourceType,startTime:e.timestamp});const o=Array.from(this.inProgress.entries()).map(([a,r])=>`${a} (${r.resourceType.split("::").pop()}, started ${r.startTime.toISOString()})`);m.debug("CloudFormation","IPAM resource started",{logicalId:e.logicalId,resourceType:e.resourceType,concurrentIpamCount:this.inProgress.size,concurrentIpamResources:o}),i&&i.writeEvent(e,{phase:"ipam_concurrency",isMainStack:!0,parentStack:`concurrent=${this.inProgress.size}: ${o.join(", ")}`})}else if(e.status.includes("COMPLETE")||e.status.includes("FAILED")){const o=this.inProgress.has(e.logicalId),a=this.inProgress.get(e.logicalId);this.inProgress.delete(e.logicalId);const r=a?e.timestamp.getTime()-a.startTime.getTime():void 0;if(m.debug("CloudFormation",`IPAM resource ${e.status}`,{logicalId:e.logicalId,resourceType:e.resourceType,status:e.status,statusReason:e.statusReason,durationMs:r,wasTracked:o,remainingIpamCount:this.inProgress.size,remainingIpamResources:Array.from(this.inProgress.keys())}),e.status.includes("FAILED")&&i){const l=Array.from(this.inProgress.entries()).map(([c,u])=>({logicalId:c,resourceType:u.resourceType,startTime:u.startTime.toISOString(),inFlightDurationMs:e.timestamp.getTime()-u.startTime.getTime()}));i.writeEvent(e,{phase:"ipam_failure_snapshot",isMainStack:!0,parentStack:JSON.stringify({failedResource:e.logicalId,reason:e.statusReason,durationMs:r,concurrentIpamAtFailure:l})})}}}}async function $(s,e,i){try{const o=s.aws.getClient(g);let a,r=!1,l=!1;do{const c=new y({StackName:e,...a?{NextToken:a}:{}}),u=await o.send(c);if(!u.StackEvents)break;for(const t of u.StackEvents){const d=`${t.EventId}`;if(s.seenEventIds.has(d)){l=!0;continue}if(s.seenEventIds.add(d),t.ResourceType===I&&t.LogicalResourceId!==e){const E=t.ResourceStatus||"",T=t.LogicalResourceId||"";E.includes("IN_PROGRESS")?s.activeNestedStacks.set(T,E):(E.includes("COMPLETE")||E.includes("FAILED"))&&s.activeNestedStacks.delete(T)}if(t.LogicalResourceId===e&&t.ResourceType===I){const E=t.ResourceStatus||"";O(E)&&(r=!0)}t.ResourceStatus&&t.ResourceStatus.includes("FAILED")&&t.ResourceStatusReason&&(s.failureReasons.set(t.LogicalResourceId||"unknown",t.ResourceStatusReason),m.debug("CloudFormation",`Captured failure: ${t.LogicalResourceId} - ${t.ResourceStatusReason}`));const n={logicalId:t.LogicalResourceId||"",physicalId:t.PhysicalResourceId,resourceType:t.ResourceType||"",status:t.ResourceStatus||"",statusReason:t.ResourceStatusReason,timestamp:t.Timestamp||new Date},p=n.logicalId,R=s.eventHistory.get(p)??[];if(R.push(n),s.eventHistory.set(p,R),s.eventHistory.size>s.maxHistorySize){const E=[...s.eventHistory.keys()].slice(0,s.eventHistory.size-s.maxHistorySize);for(const T of E)s.eventHistory.delete(T)}_(n.resourceType)&&s.ipamTracker.trackEvent(n,s.eventLogger),s.eventLogger&&s.eventLogger.writeEvent(n),m.debug("CloudFormation","pollEvents delivering event",{stackName:e,logicalId:n.logicalId,status:n.status,resourceType:n.resourceType}),i(n)}a=l?void 0:u.NextToken}while(a);return r&&s.activeNestedStacks.size>0?!1:r}catch(o){const a=P(o),r=S(o);return a==="ValidationError"&&r.includes(f)?!1:a==="Throttling"||a==="TooManyRequestsException"||a==="ThrottlingException"?"throttled":(r.includes(f)||m.error("CloudFormation","Unexpected polling error",{error:C(r)}),!1)}}async function H(s,e,i,o,a,r){const l=await F(s,e),c=l?.status||"UNKNOWN",u=c.endsWith("_COMPLETE")&&!c.includes("ROLLBACK")&&!c.includes("FAILED");let t=l?.statusReason;!u&&i.size>0&&(t=(Array.from(i.values())[0]??t)||t);let d=null;if(!u&&a){const n=a.analyseFailure(r);d=n===null?null:L(n)}return o&&(o.writeDeploymentEnd(u,c,t),!u&&i.size>0&&o.writeFailureSummary(Array.from(i.entries()).map(([n,p])=>({logicalId:n,reason:p}))),d&&o.writeFailureAnalysis({rootCause:d.rootCause.reason,failedResources:d.affectedResources.map(n=>({logicalId:n.logicalId,resourceType:n.resourceType,reason:n.statusReason||"Unknown reason"})),dependencyChain:d.dependencyChain,remediation:d.remediation})),{success:u,failureMessage:t,analysis:d}}async function F(s,e){try{const i=new A({StackName:e}),r=(await s.getClient(g).send(i)).Stacks?.[0];return r?{status:r.StackStatus||"UNKNOWN",statusReason:r.StackStatusReason}:null}catch(i){return m.debug("CloudFormation","getStackStatus failed",{error:S(i)}),null}}async function B(s,e){const i=[];try{const o=new y({StackName:e}),r=await s.getClient(g).send(o);if(!r.StackEvents)return i;const l=new Map;for(const c of r.StackEvents){const u=c.LogicalResourceId||"";if(u===e&&c.ResourceType===I)continue;const t=l.get(u);(!t||c.Timestamp&&t.timestamp<c.Timestamp)&&l.set(u,{logicalId:u,physicalId:c.PhysicalResourceId,resourceType:c.ResourceType||"",status:c.ResourceStatus||"",statusReason:c.ResourceStatusReason,timestamp:c.Timestamp||new Date})}return Array.from(l.values())}catch(o){const a=P(o),r=S(o);return a==="ValidationError"&&r.includes(f)||r.includes(f)||m.error("CloudFormation","Error getting current resources",{error:C(S(o))}),i}}export{v as IpamConcurrencyTracker,B as fetchCurrentResources,F as fetchStackStatus,H as handleStackCompletion,_ as isIpamResource,b as isSuccessState,O as isTerminalState,$ as pollStackEvents};
|
|
@@ -40,6 +40,12 @@ export type EventLogWriterFactory = (deploymentId: string, stackName: string, re
|
|
|
40
40
|
export interface EventMonitorDeps {
|
|
41
41
|
failureAnalyser?: EventFailureAnalyser;
|
|
42
42
|
eventLogWriterFactory?: EventLogWriterFactory;
|
|
43
|
+
/**
|
|
44
|
+
* Fired once when a stack reaches a failed terminal state, carrying the
|
|
45
|
+
* computed analysis. Baked into the monitor at construction so it emits from
|
|
46
|
+
* `handleStackComplete` regardless of whether event-logging is enabled.
|
|
47
|
+
*/
|
|
48
|
+
onFailureAnalysis?: (analysis: FailureAnalysis) => void;
|
|
43
49
|
}
|
|
44
50
|
/** CloudFormation resource type for nested stacks */
|
|
45
51
|
export declare const CF_STACK_RESOURCE_TYPE = "AWS::CloudFormation::Stack";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{logger as u}from"@fjall/util/logger";import{getErrorMessage as
|
|
1
|
+
import{logger as u}from"@fjall/util/logger";import{getErrorMessage as m,sleep as p}from"@fjall/util";import{STACK_NOT_FOUND_PATTERN as d}from"@fjall/util/aws";import{isResourceEvent as x,STACK_NOT_FOUND_PATTERN as b,CDK_NO_STACKS_MATCH as K}from"@fjall/util/aws";import{CF_STACK_RESOURCE_TYPE as S}from"./cloudformationEventTypes.js";import{isTerminalState as v,isSuccessState as w,IpamConcurrencyTracker as C,pollStackEvents as E,handleStackCompletion as M,fetchStackStatus as F,fetchCurrentResources as T}from"./cloudformationEventHelpers.js";class D{aws;seenEventIds=new Set;isMonitoring=!1;pollInterval=null;activeNestedStacks=new Map;failureReasons=new Map;eventHistory=new Map;eventLogger=null;failureAnalyser;eventLogWriterFactory;onFailureAnalysis;lastAnalysis=null;deploymentStartTime=null;maxHistorySize=1e3;maxSeenEventIds=1e4;ipamTracker=new C;constructor(t,e){this.aws=t,this.failureAnalyser=e?.failureAnalyser??null,this.eventLogWriterFactory=e?.eventLogWriterFactory,this.onFailureAnalysis=e?.onFailureAnalysis}enableLogging(t,e,a,s){this.eventLogWriterFactory&&(this.eventLogger=this.eventLogWriterFactory(t,e,a,s))}async startMonitoring(t,e,a){if(this.isMonitoring){u.debug("CloudFormation","startMonitoring SKIPPED - already monitoring",{stackName:t});return}u.debug("CloudFormation","startMonitoring STARTED",{stackName:t}),this.isMonitoring=!0,this.seenEventIds.clear(),this.eventHistory.clear(),this.deploymentStartTime=new Date;try{await this.pollEvents(t,()=>{})}catch(r){const h=m(r);h.includes(d)||u.debug("CloudFormation","Initial poll failed",{error:h})}let s=5e3;const o=1e4;let n=0,g=0;const l=async()=>{if(this.isMonitoring)try{const r=await this.pollEvents(t,e);n++,r==="throttled"?(g++,s=Math.min(3e4,5e3*Math.pow(2,g-1))):(g=0,n>20&&s<o?s=Math.min(o,s+1e3):n<=20&&(s=5e3)),r===!0?await this.handleStackComplete(t,a):this.pollInterval=setTimeout(l,s)}catch(r){u.debug("CloudFormation","Polling iteration error (continuing)",{error:m(r)}),this.pollInterval=setTimeout(l,s)}};this.pollInterval=setTimeout(l,s)}stopMonitoring(){u.debug("CloudFormation","stopMonitoring called",{wasMonitoring:this.isMonitoring,seenEventCount:this.seenEventIds.size}),this.isMonitoring=!1,this.pollInterval&&(clearTimeout(this.pollInterval),this.pollInterval=null),this.cleanup()}cleanup(){if(this.activeNestedStacks.clear(),this.failureReasons.clear(),this.eventHistory.clear(),this.ipamTracker.clear(),this.seenEventIds.size>this.maxSeenEventIds){const t=Array.from(this.seenEventIds),e=Math.floor(this.maxSeenEventIds/2);this.seenEventIds=new Set(t.slice(-e))}this.eventLogger&&(this.eventLogger=null)}async handleStackComplete(t,e){const a=new Map(this.failureReasons),s=new Map(this.eventHistory),o=this.eventLogger;this.stopMonitoring();const n=await M(this.aws,t,a,o,this.failureAnalyser,s);this.lastAnalysis=n.analysis,n.analysis&&this.onFailureAnalysis&&this.onFailureAnalysis(n.analysis),e&&e(n.success,n.failureMessage)}getResourceHistory(t){return this.eventHistory.get(t)||[]}getEventHistory(){return new Map(this.eventHistory)}getFailureAnalysis(){return this.lastAnalysis}getFirstFailureReason(){return this.failureReasons.size>0?Array.from(this.failureReasons.values())[0]??null:null}getEventLogger(){return this.eventLogger}getEventLogPath(){return this.eventLogger?.getLogPath()||null}getLogSummary(){return this.eventLogger?.getLogSummary()||null}async waitForStackComplete(t,e={}){const{timeout:a=1800*1e3,pollInterval:s=2e3,onResourceUpdate:o,onStackComplete:n}=e,g=Date.now();let l,r=!1,h=!1,c;u.debug("CloudFormation","waitForStackComplete called",{stackName:t,timeout:a,pollInterval:s,hasOnResourceUpdate:!!o}),await this.startMonitoring(t,i=>{o&&o(i),i.resourceType===S&&i.logicalId===t&&(l=i.status,v(i.status)&&(r=!0,h=w(i.status),h||(c=this.getFirstFailureReason()||i.statusReason||"Stack operation failed")))},(i,f)=>{n&&n(i,f)});try{let i=!1,f=!1;for(;!r&&this.isMonitoring&&Date.now()-g<a;)await p(s),!i&&Date.now()-g>3e4&&!f&&(f=!0,await this.getStackStatus(t)||u.debug("CloudFormation","Stack not found after 30s, continuing to wait (CDK may be uploading assets)",{stackName:t})),l&&(i=!0);if(this.stopMonitoring(),!r){if(c)return{success:!1,status:"FAILED",failureReason:c,logPath:this.getLogSummary()||void 0};const y=await this.getStackStatus(t);return{success:!1,status:y?.status||"UNKNOWN",failureReason:`Deployment timed out after ${a/1e3} seconds. Stack status: ${y?.status||"UNKNOWN"}`,logPath:this.getLogSummary()||void 0}}return{success:h,status:l,failureReason:c,logPath:this.getLogSummary()||void 0}}catch(i){return this.stopMonitoring(),{success:!1,failureReason:`Monitoring error: ${m(i)}`,logPath:this.getLogSummary()||void 0}}}pollContext(){return{aws:this.aws,seenEventIds:this.seenEventIds,activeNestedStacks:this.activeNestedStacks,failureReasons:this.failureReasons,eventHistory:this.eventHistory,maxHistorySize:this.maxHistorySize,ipamTracker:this.ipamTracker,eventLogger:this.eventLogger}}async pollEvents(t,e){return E(this.pollContext(),t,e)}async getStackStatus(t){return F(this.aws,t)}async getCurrentResources(t){return T(this.aws,t)}}export{K as CDK_NO_STACKS_MATCH,D as CloudFormationEventMonitor,b as STACK_NOT_FOUND_PATTERN,x as isResourceEvent};
|
package/dist/src/index.d.ts
CHANGED
|
@@ -13,11 +13,15 @@
|
|
|
13
13
|
*/
|
|
14
14
|
export { DeploymentEventSchema, DEPLOYMENT_EVENT_TYPES, DEPLOYMENT_EVENT_RESOURCE_CATEGORIES, DEPLOYMENT_EVENT_STATUS_REASON_MAX, DEPLOYMENT_EVENT_ERROR_MESSAGE_MAX, DEPLOYMENT_EVENT_TRAIL_DETAIL_MAX, CASCADE_PHASES, CASCADE_ACCOUNT_STATUSES } from "./types/index.js";
|
|
15
15
|
export type { DeploymentEvent, DeploymentEventType, DeploymentEventResourceCategory, DeploymentEventCascadePhase, DeploymentEventCascadeAccountStatus } from "./types/index.js";
|
|
16
|
-
export
|
|
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
|
+
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, 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, ApprovalRequest, GateResolution, OrgConfig, ProviderAccount, RootAccessManagementMode, SSOSession, Entitlements } from "./types/index.js";
|
|
17
19
|
export type { AwsProvider, AwsProviderCredentials, AwsSdkClientConstructor } from "./aws/index.js";
|
|
18
20
|
export { SimpleAwsProvider } from "./aws/index.js";
|
|
19
|
-
export { checkTargetReadiness, describeTargetReadinessReason, buildTargetUnreadyAdvisory, buildTargetSetUnreadyAdvisory } from "./aws/index.js";
|
|
21
|
+
export { checkTargetReadiness, describeTargetReadinessReason, buildTargetUnreadyAdvisory, buildTargetSetUnreadyAdvisory, probeAccountStackExists } from "./aws/index.js";
|
|
20
22
|
export type { TargetReadinessAccount, TargetReadinessClients, TargetReadinessReason, TargetReadinessVerdict, TargetSetUnreadyAdvisoryParams } from "./aws/index.js";
|
|
23
|
+
export { checkRegionEnabled, regionDisabledMessage } from "./aws/index.js";
|
|
24
|
+
export type { RegionAvailability } from "./aws/index.js";
|
|
21
25
|
export { ensureOrganisationExists, describeOrganisation, enablePolicyTypes, enableServiceAccess, SERVICE_PRINCIPALS, enableRamSharing, activateTrustedAccess, enableIpamDelegatedAdmin, updateBackupGlobalSettings, listAccounts, findAccount, createAccount, ensureOrganisationalUnitsExist, placeAccountsInOUs, buildAccountToOUMap, activateCostAllocationTags, checkIdentityCentreStatus, extractErrorName, isOULeaf, registerSecurityDelegates, SECURITY_SERVICE_PRINCIPALS, enableCentralisedRootAccess, ROOT_ACCESS_FEATURES, RootAccessEnablementError } from "./aws/index.js";
|
|
22
26
|
export type { OrgDetails, OUMap, OUTree, AccountPlacementResult, AccountInfo, IdentityCentreStatus, BackupGlobalSettings, CostAllocationTag, CreateAccountResult, RootAccessFeature, RootAccessEnablementSummary } from "./aws/index.js";
|
|
23
27
|
export { STEP_IDS, STEP_NAMES, INFRASTRUCTURE_STEP_NAMES, INFRA_STEP_NAME } from "./types/index.js";
|
|
@@ -59,8 +63,12 @@ export { parseAccountsConfiguration, flattenAccountsToEnvironments, extractAllAc
|
|
|
59
63
|
export type { AccountsConfig } from "./orchestration/index.js";
|
|
60
64
|
export { runOpenNextBuild } from "./orchestration/index.js";
|
|
61
65
|
export { runOrganisationSetup, ORG_SETUP_PHASES } from "./orchestration/index.js";
|
|
62
|
-
export type { OrgSetupPhase, OrgSetupCallbacks, OrgSetupConfig, OrgSetupResult, DockerProvider, DockerProgressCallback, DockerBuildParams, DockerBuildResult, ECRInitParams, ECRInitResult, TagImagesParams, TagImagesResult, TagByDigestParams, DomainDeployProvider, DomainConfig, DomainDeployResult, DeployServices } from "./orchestration/index.js";
|
|
66
|
+
export type { OrgSetupPhase, OrgSetupCallbacks, OrgSetupConfig, OrgSetupResult, DockerProvider, DockerProgressCallback, DockerBuildParams, DockerBuildResult, ResolvedBuildSecret, ResolvedBuildSecrets, ECRInitParams, ECRInitResult, TagImagesParams, TagImagesResult, TagByDigestParams, DomainDeployProvider, DomainConfig, DomainDeployResult, DeployServices } from "./orchestration/index.js";
|
|
67
|
+
export { resolveBuildSecrets, resolveSecretRefValue, resolveBuildSecretSessionProvider, sourcedRefsFromBuildArgs, resolveBuildArgs, buildBuildSecretSessionPolicy, partitionForRegion, validateBuildGroup } from "./orchestration/index.js";
|
|
68
|
+
export type { ResolveBuildArgsContext, ResolveBuildArgsResult, SourcedBuildArgResolver, BuildSecretRef, BuildSecretSessionContext, ValidateBuildGroupInput, BuildGroupMemberInputs } from "./orchestration/index.js";
|
|
63
69
|
export type { FrameworkBuilder, FrameworkDetection, BuildPlan, BuildCommand, BuildCallbacks, DetectionContext, BuildOptions } from "./types/index.js";
|
|
64
70
|
export { FrameworkRegistry, type ResolvedBuilder } from "./orchestration/index.js";
|
|
65
71
|
export { openNextBuilder, dockerBuilder } from "./orchestration/index.js";
|
|
66
72
|
export { StepRegistry, getDestroyStepId } from "./steps/index.js";
|
|
73
|
+
export { buildDeployPlan, computeDeployPlan, computeAssemblyDigest, digestHead, classifyImpact, derivePropertyChanges, isDataLoss, isStatefulResourceType, renderPlanSummary, renderPlanLines, toWirePlanChanges, signApprovalToken, verifyApprovalToken, DEFAULT_APPROVAL_TTL_MS, APPROVAL_TOKEN_PATTERN, runApprovalGate } from "./orchestration/index.js";
|
|
74
|
+
export type { DeployPlan, DeployPlanResourceChange, StackTemplatePair, CfnTemplateReader, ComputeDeployPlanParams, ComputeDeployPlanStack, ImpactClassification, SignApprovalTokenOptions, SignedApprovalToken, VerifyApprovalTokenOptions, VerifyApprovalTokenResult, RunApprovalGateParams, ApprovalGateOutcome } from "./orchestration/index.js";
|
package/dist/src/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{DeploymentEventSchema as t,DEPLOYMENT_EVENT_TYPES as o,DEPLOYMENT_EVENT_RESOURCE_CATEGORIES as i,DEPLOYMENT_EVENT_STATUS_REASON_MAX as a,DEPLOYMENT_EVENT_ERROR_MESSAGE_MAX as s,DEPLOYMENT_EVENT_TRAIL_DETAIL_MAX as n,CASCADE_PHASES as c,CASCADE_ACCOUNT_STATUSES as E}from"./types/index.js";import{SimpleAwsProvider as l}from"./aws/index.js";import{checkTargetReadiness as T,describeTargetReadinessReason as p,buildTargetUnreadyAdvisory as u,buildTargetSetUnreadyAdvisory as _}from"./aws/index.js";import{ensureOrganisationExists as R,describeOrganisation as m,enablePolicyTypes as d,enableServiceAccess as P,SERVICE_PRINCIPALS as C,enableRamSharing as N,activateTrustedAccess as f,enableIpamDelegatedAdmin as g,updateBackupGlobalSettings as x,listAccounts as D,findAccount as I,createAccount as L,ensureOrganisationalUnitsExist as U,placeAccountsInOUs as k,buildAccountToOUMap as y,activateCostAllocationTags as v,checkIdentityCentreStatus as M,extractErrorName as F,isOULeaf as Y,registerSecurityDelegates as b,SECURITY_SERVICE_PRINCIPALS as B,enableCentralisedRootAccess as h,ROOT_ACCESS_FEATURES as K,RootAccessEnablementError as G}from"./aws/index.js";import{STEP_IDS as H,STEP_NAMES as X,INFRASTRUCTURE_STEP_NAMES as w,INFRA_STEP_NAME as j}from"./types/index.js";import{ProgressReporter as q,APPLICATION_STACKS as z,ORGANISATION_TYPES as J,APPLICATION_DEPLOY_ORDER as W,APPLICATION_DESTROY_ORDER as Z,OPENNEXT_DEPLOY_ORDER as $,OPENNEXT_DESTROY_ORDER as ee,PARALLEL_DEPLOY_GROUPS as re,PARALLEL_DESTROY_GROUPS as te,OPENNEXT_PARALLEL_GROUPS as oe,PARALLEL_OPERATION_TYPES as ie,isApplicationOperation as ae,isOrganisationOperation as se,getParallelDeployGroups as ne,getParallelDestroyGroups as ce,getApplicationDeployOrder as Ee,getApplicationDestroyOrder as Se,getApplicationStackName as le,getOrganisationStackName as Ae,isApplicationStack as Te,isQuarantineDetail as pe,isRetainedBucketsDetail as ue,getApplicationStepName as _e,getApplicationStepId as Oe,toPascalCase as Re,isOpenNextPattern as me,OPENNEXT_PATTERNS as de,deriveResourcesFromManifestStacks as Pe,STACK_NOT_FOUND_PATTERN as Ce,STACK_FAILED_STATE_PATTERN as Ne,CDK_NO_STACKS_MATCH as fe,INFRASTRUCTURE_FILENAME as ge,ApplicationError as xe,wrapApplicationError as De,FjallStateFileSchema as Ie,readStateFile as Le,writeStateFile as Ue,createEmptyState as ke,deleteStateFile as ye,updateTemplateHash as ve,getStateFilePath as Me,stubCallerIdentity as Fe}from"./types/index.js";import{CloudFormationEventMonitor as be}from"./aws/index.js";import{CdkService as he,CdkArgumentBuilder as Ke,CdkProcessManager as Ge,CdkEventMonitor as Ve,startStackMonitoring as He,DEFAULT_DEPLOY_TIMEOUT_MS as Xe,isCdkError as we,formatInfrastructureError as je,getStructuralHint as Qe,getSourceContext as qe,hasCdkDifferences as ze,parseDiffOutput as Je,CloudFormationService as We,CloudFormationError as Ze,EcsService as $e,EcsError as er,EcsServiceResolver as rr,TemplateHashService as tr,TemplateHashError as or,CdkContextBuilder as ir,emitProgress as ar,PROGRESS_MESSAGES as sr,parseBuildPhase as nr,buildStepContextBuildConfig as cr,convertCloudFormationOutputsToRecord as Er,ApplicationStackService as Sr}from"./services/index.js";import{CdkError as Ar}from"./types/errors/index.js";import{BaseServiceError as pr,ValidationError as ur,AuthError as _r,AwsError as Or,DeploymentError as Rr,NetworkError as mr,FileSystemError as dr,ConfigError as Pr,toServiceError as Cr}from"./types/errors/index.js";import{filterDangerousEnvVars as fr,maskSensitiveOutput as gr,parseShellArgs as xr,sleep as Dr}from"@fjall/util";import{hasDockerfile as Lr}from"./util/dockerfileDetection.js";import{createSequencedCallbacks as kr}from"./util/sequencedCallbacks.js";import{fileExists as vr}from"@fjall/util/fsHelpers";import{success as Fr,failure as Yr,isSuccess as br,isFailure as Br}from"@fjall/generator";import{deploy as Kr}from"./orchestration/index.js";import{destroy as Vr}from"./orchestration/index.js";import{partitionAccounts as Xr}from"./orchestration/index.js";import{buildRegionList as jr,buildAccountRegionPairs as Qr,cascadeHomeRegion as qr,cascadeOperationKey as zr}from"./orchestration/index.js";import{projectScalarSummary as Wr,projectAccountRows as Zr}from"./orchestration/index.js";import{reconcileProviderAccounts as et,mergeReconciledProviderAccounts as rt}from"./orchestration/index.js";import{decideNextTransition as ot,reconcileTrailMigration as it,decommissionMemberTrailStorage as at,ORG_TRAIL_BUCKET_OUTPUT_KEY as st,TRAIL_BUCKET_OUTPUT_KEY as nt,TRAIL_KEY_ARN_OUTPUT_KEY as ct}from"./orchestration/index.js";import{unlockBucket as St,unlockQueue as lt}from"./orchestration/index.js";import{triageBucketPolicy as Tt,isEnforceSslStatement as pt,toResourcePolicyStatements as ut,restoreBucketPolicy as _t,synthesiseEnforceSslDocument as Ot,ensureEnforceSsl as Rt,restoreAndReconcileQuarantinedBucket as mt}from"./orchestration/index.js";import{verifyOrgTrailDelivery as Pt}from"./aws/cloudtrail/orgTrailDelivery.js";import{assumeRootForTask as Nt,isRootTaskPolicyArn as ft,ROOT_TASK_POLICY_ARNS as gt,MAX_ROOT_SESSION_SECONDS as xt}from"./aws/sts/assumeRoot.js";import{parseAccountsConfiguration as It,flattenAccountsToEnvironments as Lt,extractAllAccountNames as Ut,accountsConfigToOUTree as kt,isStringArray as yt,isAccountsConfig as vt,isOuOnlyAccountBucket as Mt,OU_ONLY_ACCOUNT_BUCKETS as Ft}from"./orchestration/index.js";import{runOpenNextBuild as bt}from"./orchestration/index.js";import{runOrganisationSetup as ht,ORG_SETUP_PHASES as Kt}from"./orchestration/index.js";import{FrameworkRegistry as Vt}from"./orchestration/index.js";import{openNextBuilder as Xt,dockerBuilder as wt}from"./orchestration/index.js";import{StepRegistry as Qt,getDestroyStepId as qt}from"./steps/index.js";export{W as APPLICATION_DEPLOY_ORDER,Z as APPLICATION_DESTROY_ORDER,z as APPLICATION_STACKS,xe as ApplicationError,Sr as ApplicationStackService,_r as AuthError,Or as AwsError,pr as BaseServiceError,E as CASCADE_ACCOUNT_STATUSES,c as CASCADE_PHASES,fe as CDK_NO_STACKS_MATCH,Ke as CdkArgumentBuilder,ir as CdkContextBuilder,Ar as CdkError,Ve as CdkEventMonitor,Ge as CdkProcessManager,he as CdkService,Ze as CloudFormationError,be as CloudFormationEventMonitor,We as CloudFormationService,Pr as ConfigError,Xe as DEFAULT_DEPLOY_TIMEOUT_MS,s as DEPLOYMENT_EVENT_ERROR_MESSAGE_MAX,i as DEPLOYMENT_EVENT_RESOURCE_CATEGORIES,a as DEPLOYMENT_EVENT_STATUS_REASON_MAX,n as DEPLOYMENT_EVENT_TRAIL_DETAIL_MAX,o as DEPLOYMENT_EVENT_TYPES,Rr as DeploymentError,t as DeploymentEventSchema,er as EcsError,$e as EcsService,rr as EcsServiceResolver,dr as FileSystemError,Ie as FjallStateFileSchema,Vt as FrameworkRegistry,ge as INFRASTRUCTURE_FILENAME,w as INFRASTRUCTURE_STEP_NAMES,j as INFRA_STEP_NAME,xt as MAX_ROOT_SESSION_SECONDS,mr as NetworkError,$ as OPENNEXT_DEPLOY_ORDER,ee as OPENNEXT_DESTROY_ORDER,oe as OPENNEXT_PARALLEL_GROUPS,de as OPENNEXT_PATTERNS,J as ORGANISATION_TYPES,Kt as ORG_SETUP_PHASES,st as ORG_TRAIL_BUCKET_OUTPUT_KEY,Ft as OU_ONLY_ACCOUNT_BUCKETS,re as PARALLEL_DEPLOY_GROUPS,te as PARALLEL_DESTROY_GROUPS,ie as PARALLEL_OPERATION_TYPES,sr as PROGRESS_MESSAGES,q as ProgressReporter,K as ROOT_ACCESS_FEATURES,gt as ROOT_TASK_POLICY_ARNS,G as RootAccessEnablementError,B as SECURITY_SERVICE_PRINCIPALS,C as SERVICE_PRINCIPALS,Ne as STACK_FAILED_STATE_PATTERN,Ce as STACK_NOT_FOUND_PATTERN,H as STEP_IDS,X as STEP_NAMES,l as SimpleAwsProvider,Qt as StepRegistry,nt as TRAIL_BUCKET_OUTPUT_KEY,ct as TRAIL_KEY_ARN_OUTPUT_KEY,or as TemplateHashError,tr as TemplateHashService,ur as ValidationError,kt as accountsConfigToOUTree,v as activateCostAllocationTags,f as activateTrustedAccess,Nt as assumeRootForTask,Qr as buildAccountRegionPairs,y as buildAccountToOUMap,jr as buildRegionList,cr as buildStepContextBuildConfig,_ as buildTargetSetUnreadyAdvisory,u as buildTargetUnreadyAdvisory,qr as cascadeHomeRegion,zr as cascadeOperationKey,M as checkIdentityCentreStatus,T as checkTargetReadiness,Er as convertCloudFormationOutputsToRecord,L as createAccount,ke as createEmptyState,kr as createSequencedCallbacks,ot as decideNextTransition,at as decommissionMemberTrailStorage,ye as deleteStateFile,Kr as deploy,Pe as deriveResourcesFromManifestStacks,m as describeOrganisation,p as describeTargetReadinessReason,Vr as destroy,wt as dockerBuilder,ar as emitProgress,h as enableCentralisedRootAccess,g as enableIpamDelegatedAdmin,d as enablePolicyTypes,N as enableRamSharing,P as enableServiceAccess,Rt as ensureEnforceSsl,R as ensureOrganisationExists,U as ensureOrganisationalUnitsExist,Ut as extractAllAccountNames,F as extractErrorName,Yr as failure,vr as fileExists,fr as filterDangerousEnvVars,I as findAccount,Lt as flattenAccountsToEnvironments,je as formatInfrastructureError,Ee as getApplicationDeployOrder,Se as getApplicationDestroyOrder,le as getApplicationStackName,Oe as getApplicationStepId,_e as getApplicationStepName,qt as getDestroyStepId,Ae as getOrganisationStackName,ne as getParallelDeployGroups,ce as getParallelDestroyGroups,qe as getSourceContext,Me as getStateFilePath,Qe as getStructuralHint,ze as hasCdkDifferences,Lr as hasDockerfile,vt as isAccountsConfig,ae as isApplicationOperation,Te as isApplicationStack,we as isCdkError,pt as isEnforceSslStatement,Br as isFailure,Y as isOULeaf,me as isOpenNextPattern,se as isOrganisationOperation,Mt as isOuOnlyAccountBucket,pe as isQuarantineDetail,ue as isRetainedBucketsDetail,ft as isRootTaskPolicyArn,yt as isStringArray,br as isSuccess,D as listAccounts,gr as maskSensitiveOutput,rt as mergeReconciledProviderAccounts,Xt as openNextBuilder,It as parseAccountsConfiguration,nr as parseBuildPhase,Je as parseDiffOutput,xr as parseShellArgs,Xr as partitionAccounts,k as placeAccountsInOUs,Zr as projectAccountRows,Wr as projectScalarSummary,Le as readStateFile,et as reconcileProviderAccounts,it as reconcileTrailMigration,b as registerSecurityDelegates,mt as restoreAndReconcileQuarantinedBucket,_t as restoreBucketPolicy,bt as runOpenNextBuild,ht as runOrganisationSetup,Dr as sleep,He as startStackMonitoring,Fe as stubCallerIdentity,Fr as success,Ot as synthesiseEnforceSslDocument,Re as toPascalCase,ut as toResourcePolicyStatements,Cr as toServiceError,Tt as triageBucketPolicy,St as unlockBucket,lt as unlockQueue,x as updateBackupGlobalSettings,ve as updateTemplateHash,Pt as verifyOrgTrailDelivery,De as wrapApplicationError,Ue as writeStateFile};
|
|
1
|
+
import{DeploymentEventSchema as t,DEPLOYMENT_EVENT_TYPES as o,DEPLOYMENT_EVENT_RESOURCE_CATEGORIES as E,DEPLOYMENT_EVENT_STATUS_REASON_MAX as a,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 C,DEPLOY_EVENT_NAME_MAX as N,DEPLOY_EVENT_MESSAGE_MAX as d,DEPLOY_EVENT_STATUS_REASON_MAX as D,DEPLOY_EVENT_ERROR_MESSAGE_MAX as L,DEPLOY_EVENT_TRAIL_DETAIL_MAX as g,DEPLOY_EVENT_OUTPUT_MAX as f,DEPLOY_EVENT_ARN_MAX as I,DEPLOY_EVENT_URL_MAX as x,STEP_COMPLETE_STATUSES as U,LOG_LEVELS as y,STACK_CLEANUP_PHASES as M,CASCADE_ACCOUNT_PHASES as v,DEPLOY_RESULTS as k,DETECTION_PHASES as Y,PHASE_STATUSES as V,BUILDERS as h,CASCADE_OUTCOME_RESULTS as F,PLAN_ACTIONS as B,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 ae,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 Ce,extractErrorName as Ne,isOULeaf as de,registerSecurityDelegates as De,SECURITY_SERVICE_PRINCIPALS as Le,enableCentralisedRootAccess as ge,ROOT_ACCESS_FEATURES as fe,RootAccessEnablementError as Ie}from"./aws/index.js";import{STEP_IDS as Ue,STEP_NAMES as ye,INFRASTRUCTURE_STEP_NAMES as Me,INFRA_STEP_NAME as ve}from"./types/index.js";import{ProgressReporter as Ye,APPLICATION_STACKS as Ve,ORGANISATION_TYPES as he,APPLICATION_DEPLOY_ORDER as Fe,APPLICATION_DESTROY_ORDER as Be,OPENNEXT_DEPLOY_ORDER as be,OPENNEXT_DESTROY_ORDER as Ge,PARALLEL_DEPLOY_GROUPS as Ke,PARALLEL_DESTROY_GROUPS as Xe,OPENNEXT_PARALLEL_GROUPS as He,PARALLEL_OPERATION_TYPES as we,isApplicationOperation as je,isOrganisationOperation as Qe,getParallelDeployGroups as qe,getParallelDestroyGroups as Je,getApplicationDeployOrder as We,getApplicationDestroyOrder as ze,getApplicationStackName as Ze,getOrganisationStackName as $e,isApplicationStack as er,isQuarantineDetail as rr,isRetainedBucketsDetail as tr,getApplicationStepName as or,getApplicationStepId as Er,toPascalCase as ar,isOpenNextPattern as ir,OPENNEXT_PATTERNS as sr,deriveResourcesFromManifestStacks as Sr,STACK_NOT_FOUND_PATTERN as nr,STACK_FAILED_STATE_PATTERN as cr,CDK_NO_STACKS_MATCH as Ar,INFRASTRUCTURE_FILENAME as Tr,ApplicationError as _r,wrapApplicationError as lr,FjallStateFileSchema as pr,readStateFile as Or,writeStateFile as Pr,createEmptyState as Rr,deleteStateFile as ur,updateTemplateHash as mr,getStateFilePath as Cr,stubCallerIdentity as Nr}from"./types/index.js";import{CloudFormationEventMonitor as Dr}from"./aws/index.js";import{CdkService as gr,CdkArgumentBuilder as fr,CdkProcessManager as Ir,CdkEventMonitor as xr,startStackMonitoring as Ur,DEFAULT_DEPLOY_TIMEOUT_MS as yr,isCdkError as Mr,formatInfrastructureError as vr,getStructuralHint as kr,getSourceContext as Yr,hasCdkDifferences as Vr,parseDiffOutput as hr,CloudFormationService as Fr,CloudFormationError as Br,EcsService as br,EcsError as Gr,EcsServiceResolver as Kr,TemplateHashService as Xr,TemplateHashError as Hr,CdkContextBuilder as wr,emitProgress as jr,PROGRESS_MESSAGES as Qr,parseBuildPhase as qr,buildStepContextBuildConfig as Jr,convertCloudFormationOutputsToRecord as Wr,ApplicationStackService as zr}from"./services/index.js";import{CdkError as $r}from"./types/errors/index.js";import{BaseServiceError as rt,ValidationError as tt,AuthError as ot,AwsError as Et,DeploymentError as at,NetworkError as it,FileSystemError as st,ConfigError as St,toServiceError as nt}from"./types/errors/index.js";import{filterDangerousEnvVars as At,maskSensitiveOutput as Tt,parseShellArgs as _t,sleep as lt}from"@fjall/util";import{hasDockerfile as Ot}from"./util/dockerfileDetection.js";import{createSequencedCallbacks as Rt}from"./util/sequencedCallbacks.js";import{fileExists as mt}from"@fjall/util/fsHelpers";import{success as Nt,failure as dt,isSuccess as Dt,isFailure as Lt}from"@fjall/generator";import{deploy as ft}from"./orchestration/index.js";import{destroy as xt}from"./orchestration/index.js";import{partitionAccounts as yt}from"./orchestration/index.js";import{buildRegionList as vt,buildAccountRegionPairs as kt,cascadeHomeRegion as Yt,cascadeOperationKey as Vt}from"./orchestration/index.js";import{projectScalarSummary as Ft,projectAccountRows as Bt}from"./orchestration/index.js";import{reconcileProviderAccounts as Gt,mergeReconciledProviderAccounts as Kt}from"./orchestration/index.js";import{decideNextTransition as Ht,reconcileTrailMigration as wt,decommissionMemberTrailStorage as jt,ORG_TRAIL_BUCKET_OUTPUT_KEY as Qt,TRAIL_BUCKET_OUTPUT_KEY as qt,TRAIL_KEY_ARN_OUTPUT_KEY as Jt}from"./orchestration/index.js";import{unlockBucket as zt,unlockQueue as Zt}from"./orchestration/index.js";import{triageBucketPolicy as eo,isEnforceSslStatement as ro,toResourcePolicyStatements as to,restoreBucketPolicy as oo,synthesiseEnforceSslDocument as Eo,ensureEnforceSsl as ao,restoreAndReconcileQuarantinedBucket as io}from"./orchestration/index.js";import{verifyOrgTrailDelivery as So}from"./aws/cloudtrail/orgTrailDelivery.js";import{assumeRootForTask as co,isRootTaskPolicyArn as Ao,ROOT_TASK_POLICY_ARNS as To,MAX_ROOT_SESSION_SECONDS as _o}from"./aws/sts/assumeRoot.js";import{parseAccountsConfiguration as po,flattenAccountsToEnvironments as Oo,extractAllAccountNames as Po,accountsConfigToOUTree as Ro,isStringArray as uo,isAccountsConfig as mo,isOuOnlyAccountBucket as Co,OU_ONLY_ACCOUNT_BUCKETS as No}from"./orchestration/index.js";import{runOpenNextBuild as Lo}from"./orchestration/index.js";import{runOrganisationSetup as fo,ORG_SETUP_PHASES as Io}from"./orchestration/index.js";import{resolveBuildSecrets as Uo,resolveSecretRefValue as yo,resolveBuildSecretSessionProvider as Mo,sourcedRefsFromBuildArgs as vo,resolveBuildArgs as ko,buildBuildSecretSessionPolicy as Yo,partitionForRegion as Vo,validateBuildGroup as ho}from"./orchestration/index.js";import{FrameworkRegistry as Bo}from"./orchestration/index.js";import{openNextBuilder as Go,dockerBuilder as Ko}from"./orchestration/index.js";import{StepRegistry as Ho,getDestroyStepId as wo}from"./steps/index.js";import{buildDeployPlan as Qo,computeDeployPlan as qo,computeAssemblyDigest as Jo,digestHead as Wo,classifyImpact as zo,derivePropertyChanges as Zo,isDataLoss as $o,isStatefulResourceType as eE,renderPlanSummary as rE,renderPlanLines as tE,toWirePlanChanges as oE,signApprovalToken as EE,verifyApprovalToken as aE,DEFAULT_APPROVAL_TTL_MS as iE,APPROVAL_TOKEN_PATTERN as sE,runApprovalGate as SE}from"./orchestration/index.js";export{X as ALL_PROGRESS_KINDS,Fe as APPLICATION_DEPLOY_ORDER,Be as APPLICATION_DESTROY_ORDER,Ve as APPLICATION_STACKS,K as APPROVAL_DECISIONS,G as APPROVAL_KINDS,sE as APPROVAL_TOKEN_PATTERN,_r as ApplicationError,zr as ApplicationStackService,ot as AuthError,Et as AwsError,h as BUILDERS,rt as BaseServiceError,v as CASCADE_ACCOUNT_PHASES,n as CASCADE_ACCOUNT_STATUSES,F as CASCADE_OUTCOME_RESULTS,S as CASCADE_PHASES,Ar as CDK_NO_STACKS_MATCH,fr as CdkArgumentBuilder,wr as CdkContextBuilder,$r as CdkError,xr as CdkEventMonitor,Ir as CdkProcessManager,gr as CdkService,O as ChangeCountsSchema,Br as CloudFormationError,Dr as CloudFormationEventMonitor,Fr as CloudFormationService,St as ConfigError,iE as DEFAULT_APPROVAL_TTL_MS,yr as DEFAULT_DEPLOY_TIMEOUT_MS,i as DEPLOYMENT_EVENT_ERROR_MESSAGE_MAX,E as DEPLOYMENT_EVENT_RESOURCE_CATEGORIES,a 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,C as DEPLOY_EVENT_ID_MAX,d as DEPLOY_EVENT_MESSAGE_MAX,N as DEPLOY_EVENT_NAME_MAX,f as DEPLOY_EVENT_OUTPUT_MAX,D as DEPLOY_EVENT_STATUS_REASON_MAX,g as DEPLOY_EVENT_TRAIL_DETAIL_MAX,x as DEPLOY_EVENT_URL_MAX,k as DEPLOY_RESULTS,Y as DETECTION_PHASES,A as DeployEventSchema,_ as DeployResourceSchema,at as DeploymentError,t as DeploymentEventSchema,Gr as EcsError,br as EcsService,Kr as EcsServiceResolver,st as FileSystemError,pr as FjallStateFileSchema,Bo as FrameworkRegistry,Tr as INFRASTRUCTURE_FILENAME,Me as INFRASTRUCTURE_STEP_NAMES,ve as INFRA_STEP_NAME,y as LOG_LEVELS,_o as MAX_ROOT_SESSION_SECONDS,it as NetworkError,be as OPENNEXT_DEPLOY_ORDER,Ge as OPENNEXT_DESTROY_ORDER,He as OPENNEXT_PARALLEL_GROUPS,sr as OPENNEXT_PATTERNS,he as ORGANISATION_TYPES,Io as ORG_SETUP_PHASES,Qt as ORG_TRAIL_BUCKET_OUTPUT_KEY,No as OU_ONLY_ACCOUNT_BUCKETS,Ke as PARALLEL_DEPLOY_GROUPS,Xe as PARALLEL_DESTROY_GROUPS,we as PARALLEL_OPERATION_TYPES,V as PHASE_STATUSES,B as PLAN_ACTIONS,Qr as PROGRESS_MESSAGES,l as PlanChangeSchema,p as PlanPropertyChangeSchema,Ye as ProgressReporter,b as REPLACEMENT_MODES,fe as ROOT_ACCESS_FEATURES,To as ROOT_TASK_POLICY_ARNS,Ie as RootAccessEnablementError,Le as SECURITY_SERVICE_PRINCIPALS,ne as SERVICE_PRINCIPALS,M as STACK_CLEANUP_PHASES,cr as STACK_FAILED_STATE_PATTERN,nr as STACK_NOT_FOUND_PATTERN,U as STEP_COMPLETE_STATUSES,Ue as STEP_IDS,ye as STEP_NAMES,m as SUPPORTED_CONTRACT_MARKERS,q as SimpleAwsProvider,T as StepProgressSchema,Ho as StepRegistry,qt as TRAIL_BUCKET_OUTPUT_KEY,Jt as TRAIL_KEY_ARN_OUTPUT_KEY,Hr as TemplateHashError,Xr as TemplateHashService,tt as ValidationError,Ro as accountsConfigToOUTree,me as activateCostAllocationTags,Ae as activateTrustedAccess,co as assumeRootForTask,kt as buildAccountRegionPairs,ue as buildAccountToOUMap,Yo as buildBuildSecretSessionPolicy,Qo as buildDeployPlan,vt as buildRegionList,Jr as buildStepContextBuildConfig,$ as buildTargetSetUnreadyAdvisory,Z as buildTargetUnreadyAdvisory,Yt as cascadeHomeRegion,Vt as cascadeOperationKey,Ce as checkIdentityCentreStatus,te as checkRegionEnabled,W as checkTargetReadiness,zo as classifyImpact,Jo as computeAssemblyDigest,qo as computeDeployPlan,Wr as convertCloudFormationOutputsToRecord,Oe as createAccount,H as createDeployEmitter,Rr as createEmptyState,Rt as createSequencedCallbacks,Ht as decideNextTransition,jt as decommissionMemberTrailStorage,ur as deleteStateFile,ft as deploy,Zo as derivePropertyChanges,Sr as deriveResourcesFromManifestStacks,ie as describeOrganisation,z as describeTargetReadinessReason,xt as destroy,Wo as digestHead,Ko as dockerBuilder,jr as emitProgress,ge as enableCentralisedRootAccess,Te as enableIpamDelegatedAdmin,se as enablePolicyTypes,ce as enableRamSharing,Se as enableServiceAccess,ao as ensureEnforceSsl,ae as ensureOrganisationExists,Pe as ensureOrganisationalUnitsExist,Po as extractAllAccountNames,Ne as extractErrorName,dt as failure,mt as fileExists,At as filterDangerousEnvVars,pe as findAccount,Oo as flattenAccountsToEnvironments,vr as formatInfrastructureError,We as getApplicationDeployOrder,ze as getApplicationDestroyOrder,Ze as getApplicationStackName,Er as getApplicationStepId,or as getApplicationStepName,wo as getDestroyStepId,$e as getOrganisationStackName,qe as getParallelDeployGroups,Je as getParallelDestroyGroups,Yr as getSourceContext,Cr as getStateFilePath,kr as getStructuralHint,Vr as hasCdkDifferences,Ot as hasDockerfile,mo as isAccountsConfig,je as isApplicationOperation,er as isApplicationStack,Mr as isCdkError,$o as isDataLoss,ro as isEnforceSslStatement,Lt as isFailure,de as isOULeaf,ir as isOpenNextPattern,Qe as isOrganisationOperation,Co as isOuOnlyAccountBucket,rr as isQuarantineDetail,tr as isRetainedBucketsDetail,Ao as isRootTaskPolicyArn,eE as isStatefulResourceType,uo as isStringArray,Dt as isSuccess,le as listAccounts,Tt as maskSensitiveOutput,Kt as mergeReconciledProviderAccounts,Go as openNextBuilder,po as parseAccountsConfiguration,qr as parseBuildPhase,j as parseDeployContractVersion,w as parseDeployEvent,hr as parseDiffOutput,_t as parseShellArgs,yt as partitionAccounts,Vo as partitionForRegion,Re as placeAccountsInOUs,ee as probeAccountStackExists,Bt as projectAccountRows,Ft as projectScalarSummary,Or as readStateFile,Gt as reconcileProviderAccounts,wt as reconcileTrailMigration,oe as regionDisabledMessage,De as registerSecurityDelegates,tE as renderPlanLines,rE as renderPlanSummary,ko as resolveBuildArgs,Mo as resolveBuildSecretSessionProvider,Uo as resolveBuildSecrets,yo as resolveSecretRefValue,io as restoreAndReconcileQuarantinedBucket,oo as restoreBucketPolicy,SE as runApprovalGate,Lo as runOpenNextBuild,fo as runOrganisationSetup,EE as signApprovalToken,lt as sleep,vo as sourcedRefsFromBuildArgs,Ur as startStackMonitoring,Nr as stubCallerIdentity,Nt as success,Eo as synthesiseEnforceSslDocument,ar as toPascalCase,to as toResourcePolicyStatements,nt as toServiceError,oE as toWirePlanChanges,eo as triageBucketPolicy,zt as unlockBucket,Zt as unlockQueue,_e as updateBackupGlobalSettings,mr as updateTemplateHash,ho as validateBuildGroup,aE as verifyApprovalToken,So as verifyOrgTrailDelivery,lr as wrapApplicationError,Pr as writeStateFile};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{join as
|
|
1
|
+
import{join as F}from"path";import{CloudFormationClient as Y}from"@aws-sdk/client-cloudformation";import{EC2Client as J}from"@aws-sdk/client-ec2";import{success as E,failure as m}from"@fjall/generator";import{logger as O}from"@fjall/util/logger";import{imageTagParameterName as Q,maskSensitiveOutput as k}from"@fjall/util";import{parseDockerServicesFromManifest as V}from"@fjall/util/manifest";import{stubCallerIdentity as X}from"../../types/deployment/index.js";import{getApplicationDeployOrder as Z,getApplicationStackName as ee,getApplicationStepName as K,getApplicationStepId as W,APPLICATION_STACKS as T}from"../../types/operations.js";import{CdkContextBuilder as te}from"../../services/supporting/CdkContextBuilder.js";import{buildParamsContext as oe,bootstrapOrFail as ne}from"../contextHelpers.js";import{runDetectionPipeline as re}from"./detectionPipeline.js";import{STEP_IDS as N,STEP_NAMES as ae}from"../../types/stepDefinitions.js";import{StepRegistry as ie}from"../../steps/stepRegistry.js";import{checkTargetReadiness as se}from"../../aws/targetReadiness.js";import{cascadeHomeRegion as ce}from"../organisation/cascadeHelpers.js";import{withStepLifecycle as le}from"../stepLifecycle.js";import{DOCKER_BUILD_STEP_NAME as $,runDockerBuild as de}from"./dockerBuildHelper.js";import{getParallelPhase2Stacks as pe,deployParallelPhase as ue,deployStackSequential as fe,runDockerPreCompute as ge,createBuildCallbacks as me}from"./applicationDeployHelpers.js";import{deployCodeOnly as ke}from"./codeOnlyDeploy.js";import{runApprovalGate as ye}from"./approvalGate.js";const R="applicationDeploy";function he(a){const i=Object.entries(a);if(i.length===0)return;const r={};for(const[t,s]of i){const y=Q(t);r[y]=s}return r}async function je(a,i,r){const{callbacks:t,options:s}=a,y=Date.now(),C=te.buildDeploymentContext({deployType:"application",target:r.appName,path:r.path,region:i.awsProvider.getRegion(),callerIdentity:X(i.awsProvider.getAccountId()),...oe({orgConfig:a.orgConfig,identity:a.identity,skipOidc:a.options?.skipOidc})},{verbose:s?.verbose,infraOnly:s?.infraOnly},a.orgConfig),p=i.frameworkRegistry.resolve({appPath:r.path});let D;const h=!!s?.imageTag;if(p&&!h){D=p.builder.plan({appPath:r.path},p.detection);const e=me(t),o=await p.builder.build(r.path,D,e,{skipBuild:s?.skipBuild,infraOnly:s?.infraOnly});if(!o.success){const n=new Error(k(o.error.message));return t.onError?.(n),m(n)}}if(s?.deployOnly||h){t.onLog?.(h?`Rollback mode \u2014 rolling to image tag ${s?.imageTag}`:"Deploy-only mode \u2014 skipping infrastructure pipeline","info");const e=F(r.path,"cdk.out"),o=V(e),n=o.length>0,d=p?.detection.hasDockerfile===!0,l=n||d;O.debug(R,"Deploy-only branch entered",{isRollback:h,imageTag:s?.imageTag,appName:r.appName,appPath:r.path,cdkOutPath:e,dockerProviderAvailable:a.dockerProvider!==void 0,builderName:p?.builder.name,hasDockerfileFromManifest:n,hasDockerfileFromDisk:d,hasDockerfile:l,manifestDockerServiceCount:o.length,manifestDockerPaths:o.map(g=>g.docker.path)}),!n&&!d&&t.onLog?.("No Dockerfile detected via manifest or appPath \u2014 skipping Docker build. If this app uses a cross-repo Dockerfile, ensure a full deploy has run first to populate cdk.out/fjall-manifest.json.","warn");let u={};if(!h&&a.dockerProvider!==void 0&&l){O.debug(R,"Running Docker build before code-only deploy",{source:n?"manifest":"disk"});const g=await de(a,i,r,t);if(!g.success)return m(g.error);u=g.data}else O.debug(R,"Skipping Docker build",{reason:h?"rollback":a.dockerProvider===void 0?"no dockerProvider":"no Dockerfile detected"});return ke(a,i,r,u)}t.onLog?.("Analysing infrastructure\u2026","info");const A=await re(r,i,C,t);if(!A.success){const e=new Error(k(A.error.message));return t.onError?.(e),m(e)}const c=A.data;try{await t.onDetectionComplete?.({...c,builderName:p?.builder.name??"unknown"})}catch(e){const o=e instanceof Error?e.message:String(e),n=new Error(k(o));return t.onError?.(n),m(n)}const q={deploymentType:"application",operation:"deploy",deployOnly:!1,infraOnly:s?.infraOnly??!1,hasDockerfile:c.hasDockerfile,pattern:c.pattern,resources:c.resources,...p&&{builderName:p.builder.name}},I=ie.getSteps(q),M=I.findIndex(e=>e.id===N.TARGET_READINESS),L=await le(t,{stepId:N.TARGET_READINESS,stepName:ae.TARGET_READINESS,...M>=0&&{stepIndex:M,totalSteps:I.length}},async()=>{if(s?.skipReadinessCheck)return t.onLog?.("Skipping target readiness check (--skip-readiness-check)","warn"),{kind:"skipped",data:void 0};const e=i.awsProvider.getAccountId(),o=a.orgConfig?.providerAccounts.find(u=>u.id===e)?.name,n=i.awsProvider.getCredentials(),d=ce(a.orgConfig),l=await se({cloudFormation:i.awsProvider.getClient(Y),ec2:new J({region:d,...n!==void 0&&{credentials:n}})},{id:e,...o!==void 0&&{name:o}},i.awsProvider.getRegion(),a.orgConfig,a.abortSignal);return l.success?l.data.ready?{kind:"completed",data:void 0}:{kind:"error",error:new Error(k(l.data.advisory))}:{kind:"error",error:new Error(k(l.error.message))}},a.abortSignal);if(!L.success)return L;const f=D?D.deployOrder:Z({pattern:c.pattern,resources:c.resources}),S=f.length,_=c.hasDockerfile&&a.dockerProvider!==void 0&&f.includes(T.COMPUTE)&&s?.infraOnly!==!0;if(!c.hasDifferences&&!s?.force&&!_){t.onLog?.("No infrastructure changes detected","info"),t.onLog?.(s?.infraOnly===!0?"Infrastructure-only mode \u2014 application code was not rebuilt. Run with --deploy-only to deploy code changes.":"Nothing to deploy. Run with --deploy-only to redeploy application code.","info");const e=c.hasDockerfile&&a.dockerProvider!==void 0&&f.includes(T.COMPUTE);for(let n=0;n<f.length;n++){const d=f[n];e&&d===T.COMPUTE&&(t.onStepStart?.(N.DOCKER_OPERATIONS,$),t.onStepComplete?.(N.DOCKER_OPERATIONS,$,"skipped"));const l=W(d,"deploy"),u=K(d,"deploy");t.onStepStart?.(l,u,n,S),t.onStepComplete?.(l,u,"skipped",n,S)}const o=await i.stackService.resolveWebsiteUrl(r.appName);return E({target:r.appName,deploymentType:"application",outputs:o?{websiteUrl:o}:void 0,noChanges:!0,durationMs:Date.now()-y})}if(a.approvalGate!==void 0||s?.approvalToken!==void 0&&s.approvalToken!==""||s?.planOnly===!0){const e=F(r.path,"cdk.out"),o=await ye({approvalGate:a.approvalGate,options:s??{},target:r.appName,cdkOutPath:e,detection:{stackChanges:c.stackChanges,currentHashes:c.currentHashes},cfnService:i.cfnService,callbacks:t});if(!o.success)return o;const n=o.data;if(n.kind==="rejected")return t.onLog?.(`Deploy rejected: ${k(n.reason)}`,"warn"),E({target:r.appName,deploymentType:"application",rejected:!0,durationMs:Date.now()-y});if(n.kind==="awaiting")return E({target:r.appName,deploymentType:"application",awaitingApproval:!0,approvalToken:n.approvalToken,assemblyDigest:n.assemblyDigest,durationMs:Date.now()-y})}const x=await ne(i,C,t);if(!x.success)return x;const w={},P=new Map;for(let e=0;e<f.length;e++){const o=f[e],n=ee(r.appName,o),d=W(o,"deploy"),l=K(o,"deploy"),u=c.stackChanges.get(n)??!0,g=o===T.COMPUTE&&_;if(!u&&!s?.force&&!g){t.onStepStart?.(d,l,e,S),t.onLog?.(`Skipping ${o} \u2014 no changes detected`,"info"),t.onStepComplete?.(d,l,"skipped",e,S);continue}const v=pe(f,e,c.stackChanges,r.appName,s?.force);if(v.length>=2){const j=await ue(v,r,i,C,t,e,S,c,w,P);if(!j.success)return m(j.error);e+=v.length-1;continue}const b=await ge(o,a,i,r,t,c.hasDockerfile);if(b!==null&&!b.success)return m(b.error);const z=b!==null&&b.success?b.data.contentHashTagsByService:{},U=he(z),G=await fe(o,i,C,t,e,S,w,U!==void 0?{parameters:U}:void 0);if(!G.success)return m(G.error);const H=c.currentHashes.get(n);H&&P.set(n,H)}if(P.size>0){const e=await i.hashService.updateStateAfterDeploy(r.path,P);if(!e.success){const o=k(e.error.message);O.debug(R,"Failed to update state file",{error:o}),t.onLog?.(`Warning: failed to update state file \u2014 next deploy may re-deploy unchanged stacks: ${o}`,"warn")}}const B=await i.stackService.resolveWebsiteUrl(r.appName);return B&&(w.websiteUrl=B),E({target:r.appName,deploymentType:"application",outputs:Object.keys(w).length>0?w:void 0,durationMs:Date.now()-y})}export{je as deployApplication};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{success as h,failure as S}from"@fjall/generator";import{maskSensitiveOutput as
|
|
1
|
+
import{success as h,failure as S}from"@fjall/generator";import{maskSensitiveOutput as C}from"@fjall/util";import{isAborted as R}from"../../aws/organisations/types.js";import{APPLICATION_STACKS as E,getApplicationStackName as O,getApplicationStepName as y,getApplicationStepId as N,PARALLEL_DEPLOY_GROUPS as L}from"../../types/operations.js";import{emitProgress as I}from"../../services/supporting/helpers.js";import{forwardOutput as B,forwardResourceProgress as _}from"../contextHelpers.js";import{runDockerBuild as k}from"./dockerBuildHelper.js";import{runWelcomeImageSetup as $}from"./welcomeImageHelper.js";import{withStepLifecycle as j}from"../stepLifecycle.js";function M(t,o,s,p,r){const c=new Set(L.PHASE_2.stacks),i=[];for(let n=o;n<t.length&&c.has(t[n]);n++)i.push(t[n]);if(i.length<2)return[];if(!r){const n=i.filter(f=>{const m=O(p,f);return s.get(m)!==!1});return n.length>=2?n:[]}return i}async function W(t,o,s,p,r,c,i,n,f,m){for(let e=0;e<t.length;e++){const a=t[e],l=N(a,"deploy"),d=y(a,"deploy");r.onStepStart?.(l,d,c+e,i)}I(r,"Deploying infrastructure in parallel\u2026"),r.onParallelPhaseStart?.(t,"Storage and database resources (parallel)");const u=await s.stackService.deployStacksInParallel(t,p,{onOutput:B(r),onResourceProgress:(e,a)=>{r.onResourceProgress?.(e),a&&r.onParallelStackResourceProgress?.(a,e)},onStackComplete:(e,a,l,d)=>{const P=N(e,"deploy"),x=y(e,"deploy"),A=c+t.indexOf(e),w=R(s.abortSignal);r.onStepComplete?.(P,x,a?"completed":w?"cancelled":"error",A,i),!a&&d&&!w&&r.onError?.(new Error(C(d.message)))}});if(!u.success)return r.onParallelPhaseComplete?.([]),S(u.error);const g=u.data.filter(e=>!e.success);if(r.onParallelPhaseComplete?.(u.data.map(e=>({stack:e.stack,success:e.success,error:e.error}))),g.length>0){const e=g.map(l=>l.stack).join(", "),a=g.map(l=>`${l.stack}: ${C(l.error?.message??"Unknown error")}`).join(`
|
|
2
2
|
`);return S(new Error(`Failed to deploy stacks: ${e}
|
|
3
3
|
|
|
4
|
-
${
|
|
4
|
+
${a}`))}for(const e of u.data){if(e.success&&e.outputs)for(const[d,P]of Object.entries(e.outputs))f[d]=String(P);const a=O(o.appName,e.stack),l=n.currentHashes.get(a);l&&m.set(a,l)}return h(void 0)}async function Y(t,o,s,p,r,c,i,n){const f=N(t,"deploy"),m=y(t,"deploy");return j(p,{stepId:f,stepName:m,stepIndex:r,totalSteps:c},async()=>{const u=await o.stackService.deployStack(t,s,{onOutput:B(p),onResourceProgress:_(p)},n?.parameters!==void 0?{parameters:n.parameters}:void 0);if(!u.success)return{kind:"error",error:u.error};if(u.data.outputs)for(const[g,e]of Object.entries(u.data.outputs))i[g]=String(e);return{kind:"completed",data:void 0}},o.abortSignal)}async function z(t,o,s,p,r,c){if(t!==E.COMPUTE||!o.dockerProvider)return null;if(c){const n=await k(o,s,p,r);return n.success?h({contentHashTagsByService:n.data}):S(n.error)}const i=await $(o,s,p,r);return i.success?h({contentHashTagsByService:{}}):S(i.error)}function J(t){return{onBuildStart:o=>{t.onOpenNextBuildStart?.(),t.onLog?.(`${o} build started`,"info")},onBuildProgress:(o,s)=>{t.onOpenNextProgress?.(s)},onBuildComplete:o=>{t.onOpenNextBuildComplete?.(),t.onLog?.(`${o} build complete`,"info")},onBuildError:(o,s)=>{t.onOpenNextBuildError?.(s)}}}export{J as createBuildCallbacks,W as deployParallelPhase,Y as deployStackSequential,M as getParallelPhase2Stacks,z as runDockerPreCompute};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The engine-level approval gate, run at the read→mutate boundary of an
|
|
3
|
+
* application deploy (after detection + the no-changes short-circuit, before
|
|
4
|
+
* bootstrap). It computes the typed plan, emits it, and resolves an
|
|
5
|
+
* approve / reject / await decision through the injected surface resolver.
|
|
6
|
+
*
|
|
7
|
+
* Fully backward-compatible: when no gate is engaged the boundary is skipped and
|
|
8
|
+
* the deploy proceeds exactly as before.
|
|
9
|
+
*
|
|
10
|
+
* Minimal-viable emission: the plan is surfaced over the LEGACY DeployCallbacks
|
|
11
|
+
* (`onLog`). Rich contract-event emission (`plan.available` / `approval.awaiting`
|
|
12
|
+
* / `approval.resolved`) is deferred to the emitter migration (Brief 2 Phases
|
|
13
|
+
* 2–3) — the resolver already receives the full typed plan.
|
|
14
|
+
*/
|
|
15
|
+
import { type Result } from "@fjall/generator";
|
|
16
|
+
import type { DeployCallbacks } from "../../types/callbacks.js";
|
|
17
|
+
import type { DeployOptions } from "../../types/params.js";
|
|
18
|
+
import type { ApprovalGate } from "../../types/approval.js";
|
|
19
|
+
import { type CfnTemplateReader } from "./plan/computeDeployPlan.js";
|
|
20
|
+
export interface RunApprovalGateParams {
|
|
21
|
+
/** The surface resolver. Optional — a token-resume or plan-only run needs none. */
|
|
22
|
+
approvalGate?: ApprovalGate;
|
|
23
|
+
options: DeployOptions;
|
|
24
|
+
target: string;
|
|
25
|
+
cdkOutPath: string;
|
|
26
|
+
detection: {
|
|
27
|
+
stackChanges: ReadonlyMap<string, boolean>;
|
|
28
|
+
currentHashes: ReadonlyMap<string, string>;
|
|
29
|
+
};
|
|
30
|
+
cfnService: CfnTemplateReader;
|
|
31
|
+
callbacks: DeployCallbacks;
|
|
32
|
+
}
|
|
33
|
+
export type ApprovalGateOutcome = {
|
|
34
|
+
kind: "proceed";
|
|
35
|
+
} | {
|
|
36
|
+
kind: "rejected";
|
|
37
|
+
reason: string;
|
|
38
|
+
} | {
|
|
39
|
+
kind: "awaiting";
|
|
40
|
+
approvalToken: string;
|
|
41
|
+
assemblyDigest: string;
|
|
42
|
+
expiresAt: string;
|
|
43
|
+
};
|
|
44
|
+
export declare function runApprovalGate(params: RunApprovalGateParams): Promise<Result<ApprovalGateOutcome, Error>>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{join as f}from"node:path";import{readFile as k}from"node:fs/promises";import{success as o,failure as d}from"@fjall/generator";import{maskSensitiveOutput as v}from"@fjall/util";import{computeAssemblyDigest as g}from"./plan/assemblyDigest.js";import{computeDeployPlan as m}from"./plan/computeDeployPlan.js";import{renderPlanLines as h}from"./plan/renderDeployPlan.js";import{signApprovalToken as y,verifyApprovalToken as w}from"./plan/approvalToken.js";function A(e){switch(e){case"superseded":return"the plan changed since it was approved (re-plan and approve again)";case"expired":return"the approval window expired";case"tampered":return"the approval signature did not verify";case"malformed":return"the approval token was malformed"}}async function x(e){const t=[];for(const n of e.changedStacks){const c=f(e.cdkOutPath,`${n}.template.json`);let i;try{i=JSON.parse(await k(c,"utf8"))}catch(r){return d(new Error(v(`Failed to read synthesised template for ${n}: ${r instanceof Error?r.message:String(r)}`)))}t.push({stackName:n,newTemplate:i})}return m({cfnService:e.cfnService,stacks:t,assemblyDigest:e.assemblyDigest})}function S(e,t){e.onLog?.(`Deploy plan: ${t.summary}`,"info");for(const n of h(t))e.onLog?.(n,"info")}async function C(e){const t=g(e.detection.currentHashes),n=e.options.approvalToken;if(n!==void 0&&n!==""){const s=w({token:n,assemblyDigest:t});if(s.ok)return e.callbacks.onLog?.("Approval token verified against the current plan \u2014 proceeding","info"),o({kind:"proceed"});const l=A(s.reason);return e.callbacks.onLog?.(`Approval refused: ${l}`,"warn"),o({kind:"rejected",reason:l})}const c=[...e.detection.stackChanges.entries()].filter(([,s])=>s).map(([s])=>s),i=await x({cfnService:e.cfnService,cdkOutPath:e.cdkOutPath,changedStacks:c,assemblyDigest:t});if(!i.success)return d(i.error);const r=i.data;S(e.callbacks,r);const a=y({assemblyDigest:t});if(e.options.autoApprove===!0)return e.callbacks.onLog?.("Plan auto-approved (--auto-approve)","info"),o({kind:"proceed"});if(e.options.planOnly===!0)return o({kind:"awaiting",approvalToken:a.token,assemblyDigest:t,expiresAt:a.expiresAt});if(e.approvalGate===void 0)return o({kind:"awaiting",approvalToken:a.token,assemblyDigest:t,expiresAt:a.expiresAt});const u={target:e.target,plan:r,approvalToken:a.token,expiresAt:a.expiresAt,hasDestructiveChanges:r.hasDestructiveChanges},p=await e.approvalGate.resolve(u);switch(p.kind){case"approved":return o({kind:"proceed"});case"rejected":return o({kind:"rejected",reason:p.reason});case"suspended":return o({kind:"awaiting",approvalToken:p.approvalToken,assemblyDigest:t,expiresAt:a.expiresAt})}}export{C as runApprovalGate};
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { type Result } from "@fjall/generator";
|
|
2
|
+
import type { DockerBuildArgValue } from "@fjall/util/manifest/schemas";
|
|
3
|
+
/**
|
|
4
|
+
* Resolve a sourced (`{ ssm | secretsManager | env }`) build-arg value to its
|
|
5
|
+
* string value. Threaded in from the caller so this resolver stays free of an
|
|
6
|
+
* AWS dependency; the CLI/worker pass the scoped-session reader (sub-deliverable
|
|
7
|
+
* C) and the resolver only calls it when no higher precedence tier shadows the
|
|
8
|
+
* key.
|
|
9
|
+
*/
|
|
10
|
+
export type SourcedBuildArgResolver = (key: string, source: Exclude<DockerBuildArgValue, string>) => Promise<Result<string>>;
|
|
11
|
+
export interface ResolveBuildArgsContext {
|
|
12
|
+
/**
|
|
13
|
+
* The `docker.buildArgs` declared in the manifest (lowest precedence). Only
|
|
14
|
+
* keys present here (∪ flag keys) are ever emitted — the resolver never
|
|
15
|
+
* injects arbitrary host env.
|
|
16
|
+
*/
|
|
17
|
+
manifestBuildArgs?: Record<string, DockerBuildArgValue>;
|
|
18
|
+
/** Build-context directory holding `.env*` files (`identity.buildContext`). */
|
|
19
|
+
serviceDir: string;
|
|
20
|
+
/** App-dir fallback when a `.env*` file is absent from `serviceDir`. */
|
|
21
|
+
appDir?: string;
|
|
22
|
+
/** Deployment stage, used for the `.env.<stage>` tier (e.g. "production"). */
|
|
23
|
+
stage?: string;
|
|
24
|
+
/** The build-host shell environment (usually `process.env`). */
|
|
25
|
+
shellEnv: Record<string, string | undefined>;
|
|
26
|
+
/**
|
|
27
|
+
* Phase-4 CLI `--build-arg` override seam. Today the callers pass undefined /
|
|
28
|
+
* {}. Highest precedence; its keys widen the emitted set beyond the manifest.
|
|
29
|
+
*/
|
|
30
|
+
flagBuildArgs?: Record<string, string>;
|
|
31
|
+
/**
|
|
32
|
+
* Resolves a sourced manifest value to a string. Required only when the
|
|
33
|
+
* manifest declares a sourced value that no higher tier shadows; absent it
|
|
34
|
+
* (or a sourced value reaching it being unresolvable) fails closed.
|
|
35
|
+
*/
|
|
36
|
+
resolveSourcedValue?: SourcedBuildArgResolver;
|
|
37
|
+
}
|
|
38
|
+
export interface ResolveBuildArgsResult {
|
|
39
|
+
/** The resolved `KEY=VALUE` map for `DockerBuildParams.buildArgs`. */
|
|
40
|
+
buildArgs: Record<string, string>;
|
|
41
|
+
/**
|
|
42
|
+
* Keys auto-declared because a public-prefixed key was found in a parsed
|
|
43
|
+
* `.env`/`.env.<stage>` FILE (not explicit in the manifest, not a flag). The
|
|
44
|
+
* caller surfaces an R3-style inverse-footgun warning for each (masked at the
|
|
45
|
+
* sink) — auto-baking a `.env` key freezes it into the image.
|
|
46
|
+
*/
|
|
47
|
+
inferredDotenvKeys: string[];
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Resolve `docker.buildArgs` to a flat `Record<string, string>` for
|
|
51
|
+
* `DockerBuildParams`, applying the source-precedence ladder (highest → lowest):
|
|
52
|
+
*
|
|
53
|
+
* flagBuildArgs > shell env `KEY` > `.env.<stage>` > `.env`/`.env.production`
|
|
54
|
+
* > manifest literal/sourced value
|
|
55
|
+
*
|
|
56
|
+
* Keys declared in `manifestBuildArgs` (∪ `flagBuildArgs`) are always emitted.
|
|
57
|
+
* Phase 3 ADDITIONALLY auto-declares a key that is BOTH public-prefixed
|
|
58
|
+
* (`isPublicBuildVarName`) AND present in a parsed `.env`/`.env.<stage>` FILE —
|
|
59
|
+
* never from raw shell env. Shell remains a VALUE-override tier for already
|
|
60
|
+
* declared/inferred keys; it is never a KEY source, so arbitrary host env can
|
|
61
|
+
* never be injected as a build-arg.
|
|
62
|
+
*
|
|
63
|
+
* An empty string at ANY tier is treated as ABSENT and falls through to the
|
|
64
|
+
* next tier (the 2026-06-10 Turnstile empty-string shadow shape); deploy-core
|
|
65
|
+
* has no `getEnv`, so the check is inline `raw !== undefined && raw !== ""`.
|
|
66
|
+
*
|
|
67
|
+
* A manifest value that is a sourced reference (`{ ssm | secretsManager | env }`)
|
|
68
|
+
* is resolved via `resolveSourcedValue` ONLY when no higher tier supplied a
|
|
69
|
+
* value for that key. A plain-string manifest value is used verbatim. An
|
|
70
|
+
* auto-declared `.env` key carries no manifest value — the ladder supplies it.
|
|
71
|
+
*/
|
|
72
|
+
export declare function resolveBuildArgs(ctx: ResolveBuildArgsContext): Promise<Result<ResolveBuildArgsResult>>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{readFile as k}from"node:fs/promises";import{join as A}from"node:path";import{success as h,failure as g}from"@fjall/generator";import{getErrorMessage as V,maskSensitiveOutput as B}from"@fjall/util";import{isPublicBuildVarName as D}from"@fjall/util/docker";async function $(e){const o=e.manifestBuildArgs??{},a=e.flagBuildArgs??{};let r,i;try{r=await w(e,[".env",".env.production"]),i=e.stage!==void 0&&e.stage!==""?await w(e,[`.env.${e.stage}`]):{}}catch(n){return g(new Error(`Failed to read .env build-arg sources: ${B(V(n))}`))}const s=new Set([...Object.keys(o),...Object.keys(a)]),f=[];for(const n of new Set([...Object.keys(i),...Object.keys(r)]))s.has(n)||D(n)&&f.push(n);const t=new Set([...s,...f]);if(t.size===0)return h({buildArgs:{},inferredDotenvKeys:[]});const u={};for(const n of t){const p=c(a[n]);if(p!==void 0){u[n]=p;continue}const m=c(e.shellEnv[n]);if(m!==void 0){u[n]=m;continue}const v=c(i[n]);if(v!==void 0){u[n]=v;continue}const y=c(r[n]);if(y!==void 0){u[n]=y;continue}const d=o[n];if(d===void 0)continue;if(typeof d=="string"){const b=c(d);b!==void 0&&(u[n]=b);continue}if(e.resolveSourcedValue===void 0)return g(new Error(`buildArg "${n}" is sourced from a secret store but no resolver was provided to read it.`));const l=await e.resolveSourcedValue(n,d);if(!l.success)return g(l.error);u[n]=l.data}return h({buildArgs:u,inferredDotenvKeys:f})}function c(e){return e!==void 0&&e!==""?e:void 0}async function w(e,o){const a=[e.serviceDir,...e.appDir?[e.appDir]:[]];for(const r of a)for(const i of o){const s=await S(A(r,i));if(s!==void 0)return O(s)}return{}}async function S(e){try{return await k(e,"utf8")}catch{return}}function O(e){const o={};for(const a of e.split(/\r?\n/)){const r=a.trim();if(r===""||r.startsWith("#"))continue;const i=r.replace(/^export\s+/,""),s=i.indexOf("=");if(s<=0)continue;const f=i.slice(0,s).trim();if(f==="")continue;let t=i.slice(s+1).trim();t.length>=2&&(t.startsWith('"')&&t.endsWith('"')||t.startsWith("'")&&t.endsWith("'"))&&(t=t.slice(1,-1)),o[f]=t}return o}export{$ as resolveBuildArgs};
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fail-closed structural validation for a single build group, run BEFORE the
|
|
3
|
+
* bake-guard and before any docker input is resolved. It is the anti-silent
|
|
4
|
+
* -secret-drop gate: both build entry points (`dockerBuildHelper.buildGroup`
|
|
5
|
+
* and `EcrBuildOrchestrator.buildGroupViaBuildx`) resolve only the LEADER's
|
|
6
|
+
* `(buildArgs, buildSecrets)`, so a member of the same build group that
|
|
7
|
+
* declares DIFFERENT docker inputs would ship an artefact that disagrees with
|
|
8
|
+
* what it declared — invisibly. This validator rejects that, plus the
|
|
9
|
+
* collision classes that would otherwise corrupt the buildx argv or the
|
|
10
|
+
* temp-file mount.
|
|
11
|
+
*
|
|
12
|
+
* Every rejection message names the offending IDENTIFIER (a key, a secret id,
|
|
13
|
+
* a service name, a path) — never a secret VALUE (`security-standards.md §
|
|
14
|
+
* "Values, not identifiers"`). The inputs reaching this validator are manifest
|
|
15
|
+
* references, not resolved secrets, so nothing here is value-bearing.
|
|
16
|
+
*
|
|
17
|
+
* Rules (each maps to a brief-named class):
|
|
18
|
+
* - C-f: a `buildArgs` KEY equal to a `buildSecrets[].id` — the public ARG
|
|
19
|
+
* would shadow the secret mount (ambiguous which channel the Dockerfile
|
|
20
|
+
* reads).
|
|
21
|
+
* - C-g: a duplicate `buildSecrets[].id` within the group — the resolver
|
|
22
|
+
* writes each value to `join(tempDir, ref.id)`, so the second collides and
|
|
23
|
+
* silently wins.
|
|
24
|
+
* - C-h: a `buildSecrets[].id` not matching `BUILDKIT_SECRET_ID_PATTERN`
|
|
25
|
+
* (defence-in-depth — parse catches manifest-sourced ids, but a direct
|
|
26
|
+
* deploy-core caller can construct refs without going through the schema).
|
|
27
|
+
* - C-i: `buildContext` is not an existing DIRECTORY.
|
|
28
|
+
* - C-k: a grouped non-leader member whose normalised `(buildArgs,
|
|
29
|
+
* buildSecrets)` differs from the leader's — the silent secret-drop.
|
|
30
|
+
*
|
|
31
|
+
* No `target`-validity check: buildx validates the target stage itself, and a
|
|
32
|
+
* pre-check here would be both redundant and a second source of truth.
|
|
33
|
+
*/
|
|
34
|
+
import { type Result } from "@fjall/generator";
|
|
35
|
+
import { type DockerBuildArgValue, type DockerBuildSecretRef } from "@fjall/util/manifest/schemas";
|
|
36
|
+
/**
|
|
37
|
+
* One member's resolvable docker inputs, used by the C-k leader-parity check.
|
|
38
|
+
* Carries only the two channels that are resolved per-group from the leader —
|
|
39
|
+
* the fields whose divergence ships a different artefact than declared.
|
|
40
|
+
*/
|
|
41
|
+
export interface BuildGroupMemberInputs {
|
|
42
|
+
serviceName: string;
|
|
43
|
+
buildArgs?: Record<string, DockerBuildArgValue>;
|
|
44
|
+
buildSecrets?: readonly DockerBuildSecretRef[];
|
|
45
|
+
}
|
|
46
|
+
export interface ValidateBuildGroupInput {
|
|
47
|
+
/** The group leader's service name — used only for messaging. */
|
|
48
|
+
serviceName: string;
|
|
49
|
+
dockerfilePath: string;
|
|
50
|
+
buildContext: string;
|
|
51
|
+
target?: string;
|
|
52
|
+
/** The leader's declared buildArgs (manifest references, not resolved). */
|
|
53
|
+
buildArgs?: Record<string, DockerBuildArgValue>;
|
|
54
|
+
/** The leader's declared buildSecrets (manifest references, not resolved). */
|
|
55
|
+
buildSecrets?: readonly DockerBuildSecretRef[];
|
|
56
|
+
/**
|
|
57
|
+
* Every member of the group (including the leader). Each member's
|
|
58
|
+
* `(buildArgs, buildSecrets)` is compared to the leader's top-level inputs;
|
|
59
|
+
* any divergence is the C-k silent secret-drop. When omitted, the C-k check
|
|
60
|
+
* is skipped (single-member groups never drift).
|
|
61
|
+
*/
|
|
62
|
+
members?: readonly BuildGroupMemberInputs[];
|
|
63
|
+
}
|
|
64
|
+
export declare function validateBuildGroup(input: ValidateBuildGroupInput): Result<void, Error>;
|