@fjall/deploy-core 2.16.0 → 2.17.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/orchestration/contextHelpers.d.ts +36 -0
- package/dist/src/orchestration/contextHelpers.js +1 -1
- package/dist/src/orchestration/organisationDeploy/orgContext.d.ts +1 -1
- package/dist/src/orchestration/organisationDeploy/orgContext.js +1 -1
- package/dist/src/orchestration/organisationDeploy/singleComponentDeploy.js +1 -1
- package/package.json +4 -4
package/dist/.minified
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
152 files minified at 2026-06-
|
|
1
|
+
152 files minified at 2026-06-15T10:18:02.165Z
|
|
@@ -40,6 +40,42 @@ export declare function buildParamsContext(params: {
|
|
|
40
40
|
fjallAccountGlobalsConfigured?: boolean;
|
|
41
41
|
fjallAccountTrailState?: string;
|
|
42
42
|
};
|
|
43
|
+
/**
|
|
44
|
+
* Effective `skipOidc` for the single-component (platform/account) deploy path.
|
|
45
|
+
*
|
|
46
|
+
* A standalone single account's OIDC provider + FjallDeploy role are created by
|
|
47
|
+
* the connect-time Quick-Create stack (FjallOIDCConnector), so the Account
|
|
48
|
+
* stack must NOT recreate the account-global `https://fjall.io` provider — a
|
|
49
|
+
* second create throws EntityAlreadyExistsException and rolls the stack back.
|
|
50
|
+
* An org MEMBER is the opposite case: its provider + role are created AND
|
|
51
|
+
* managed by its own Account-stack OidcConnector, so skipping there drops the
|
|
52
|
+
* construct from the template and lets CloudFormation DELETE the provider and
|
|
53
|
+
* deploy role the account depends on. An explicit caller value always wins.
|
|
54
|
+
*
|
|
55
|
+
* Discriminator — two independent reasons to skip:
|
|
56
|
+
* 1. Solo estate — no organisation-tier account anywhere → Quick-Create owns
|
|
57
|
+
* the provider → skip. Absent org config reads as solo (favours the
|
|
58
|
+
* connected-account case that breaks).
|
|
59
|
+
* 2. Standalone management account — the estate HAS an organisation-tier
|
|
60
|
+
* account AND this deploy targets it (`targetIsOrganisationTier`). The only
|
|
61
|
+
* way an `account`-type deploy targets the organisation-tier (management)
|
|
62
|
+
* account is a management account connected standalone via Quick-Create; a
|
|
63
|
+
* real organisation deploys its root through `deployType: "organisation"`,
|
|
64
|
+
* which returns undefined here. Its provider is Quick-Create-owned → skip.
|
|
65
|
+
* Any other `account` target is an org member → do NOT skip. `targetIsOrganisationTier`
|
|
66
|
+
* is the resolved tier of the deploy target, supplied by the orchestrator — the
|
|
67
|
+
* helper never re-derives it from the estate.
|
|
68
|
+
*
|
|
69
|
+
* Maintainer note: Quick-Create is the canonical OWNER of the provider +
|
|
70
|
+
* FjallDeploy role for a standalone account — a deploy can only authenticate by
|
|
71
|
+
* assuming that role, so the provider must already exist before any deploy runs.
|
|
72
|
+
* Do NOT "fix" a removed-provider report by re-enabling Account-stack creation
|
|
73
|
+
* for a standalone account: a genuinely-deleted provider is restored by RECONNECT
|
|
74
|
+
* (re-running Quick-Create), not by redeploy. Re-enabling creation reintroduces
|
|
75
|
+
* the collision. Equally, do NOT widen the skip to org members — that deletes a
|
|
76
|
+
* provider the Account stack legitimately owns.
|
|
77
|
+
*/
|
|
78
|
+
export declare function resolveAccountBootstrapSkipOidc(deployType: "organisation" | "platform" | "account", orgConfig: OrgConfig | undefined, explicitSkipOidc: boolean | undefined, targetIsOrganisationTier?: boolean): boolean | undefined;
|
|
43
79
|
/** Forward onOutput callback — reduces lambda repetition across orchestration files. */
|
|
44
80
|
export declare function forwardOutput(callbacks: DeployCallbacks): (chunk: string) => void;
|
|
45
81
|
/** Forward onResourceProgress callback — reduces lambda repetition across orchestration files. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{success as f,failure as i}from"@fjall/generator";import{getErrorMessage as m,maskSensitiveOutput as d,sleep as y}from"@fjall/util";import{logger as
|
|
1
|
+
import{success as f,failure as i}from"@fjall/generator";import{getErrorMessage as m,maskSensitiveOutput as d,sleep as y}from"@fjall/util";import{logger as C}from"@fjall/util/logger";import{hasOrganisationTierAccount as R}from"../aws/organisations/accountGlobals.js";import{SimpleAwsProvider as S}from"../aws/SimpleAwsProvider.js";const w={account:"active",draining:"draining",org:"removed"};function T(e,r){return e!==void 0&&e!==""&&r!==void 0&&r!==""&&e!==r}function D(e){const r=T(e.region,e.primaryRegion);return{...e.orgConfig!==void 0?{orgConfig:JSON.stringify(e.orgConfig)}:{},...e.identity!==void 0?{fjallOrgId:e.identity.fjallOrgId}:{},...e.skipOidc?{fjallOidcConfigured:!0}:{},...r?{fjallAccountGlobalsConfigured:!0}:{},...e.trailLifecycle!==void 0?{fjallAccountTrailState:w[e.trailLifecycle]}:{}}}function P(e,r,t,n){if(t!==void 0)return t;if(e==="account")return R(r?.providerAccounts)?n===!0:!0}function _(e){return r=>e.onOutput?.(r)}function B(e){return r=>e.onResourceProgress?.(r)}function j(e){if(!e.success||e.data.length===0)return;const r={};for(const t of e.data)t.OutputKey&&t.OutputValue!==void 0&&(r[t.OutputKey]=t.OutputValue);return Object.keys(r).length>0?r:void 0}const E="OrganizationAccountAccessRole",l=5,$=5e3,b=3e4;function h(e){return`arn:aws:iam::${e}:role/${E}`}function K(e,r){return r===void 0?y(e):r.aborted?Promise.resolve():new Promise(t=>{const n=()=>{t()};r.addEventListener("abort",n,{once:!0}),y(e).then(()=>{r.removeEventListener("abort",n),t()})})}async function F(e,r,t,n,o){if(!e.assumeRole)return i(new Error("AwsProvider does not support assumeRole"));const u=h(r),g=e.assumeRole.bind(e);let s;for(let c=0;c<=l;c++)try{s=await g(u,n);break}catch(a){const p=a instanceof Error?a.name:void 0;if(p==="AccessDenied"||p==="AccessDeniedException")return i(new Error(`Access denied assuming ${E} in account ${r}. The role may not exist or may not trust the management account.`));if(c<l){const A=Math.min($*2**c,b);if(C.debug("assumeCascadeRole",`Attempt ${c+1} failed for account ${r}, retrying in ${Math.round(A/1e3)}s`,{error:d(m(a))}),await K(A,o),o?.aborted)return i(new Error(`Aborted while retrying assume-role for account ${r}`));continue}return i(new Error(`Failed to assume role in account ${r} after ${l+1} attempts: ${d(m(a))}`))}if(!s)return i(new Error(`Failed to assume role in account ${r}`));const O=new S({accessKeyId:s.accessKeyId,secretAccessKey:s.secretAccessKey,sessionToken:s.sessionToken,region:t,accountId:r});return f({provider:O,credentials:{accessKeyId:s.accessKeyId,secretAccessKey:s.secretAccessKey,sessionToken:s.sessionToken}})}async function Y(e,r,t,n){const o=await e.cdkService.runCdkSynth(r,u=>t.onCdkOutput?.(u,"synth"));if(!o.success){const u=new Error(d(`${n}: ${o.error}`));return t.onError?.(u),i(u)}return f(void 0)}async function I(e,r,t){t.onCDKBootstrap?.("bootstrapping");const n=await e.cdkService.runCdkBootstrap(r,_(t));if(!n.success){t.onCDKBootstrap?.("failed");const o=new Error(d(`Bootstrap failed: ${n.error}`));return t.onError?.(o),i(o)}return t.onCDKBootstrap?.("complete"),f(void 0)}export{F as assumeCascadeRole,I as bootstrapOrFail,h as buildCascadeRoleArn,D as buildParamsContext,j as collectStackOutputs,_ as forwardOutput,B as forwardResourceProgress,P as resolveAccountBootstrapSkipOidc,Y as synthOrFail,T as targetsNonPrimaryRegion};
|
|
@@ -8,5 +8,5 @@ export interface OrgDetailsForSynth {
|
|
|
8
8
|
rootId: string;
|
|
9
9
|
managementAccountId: string;
|
|
10
10
|
}
|
|
11
|
-
export declare function buildOrgContext(params: DeployParams, services: DeployServices, operation: OrganisationOperation, deployType: "organisation" | "platform" | "account", orgDetails: OrgDetailsForSynth, accountName?: string, trailLifecycle?: ProviderAccount["trailLifecycle"]): import("../../types/deployment/DeploymentTypes.js").DeploymentContext;
|
|
11
|
+
export declare function buildOrgContext(params: DeployParams, services: DeployServices, operation: OrganisationOperation, deployType: "organisation" | "platform" | "account", orgDetails: OrgDetailsForSynth, accountName?: string, trailLifecycle?: ProviderAccount["trailLifecycle"], targetIsOrganisationTier?: boolean): import("../../types/deployment/DeploymentTypes.js").DeploymentContext;
|
|
12
12
|
export declare function resolveOrgDetails(services: DeployServices, abortSignal?: AbortSignal): Promise<Result<OrgDetailsForSynth>>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{success as a,failure as
|
|
1
|
+
import{success as a,failure as s}from"@fjall/generator";import{OrganizationsClient as u}from"@aws-sdk/client-organizations";import{CdkContextBuilder as f}from"../../services/supporting/CdkContextBuilder.js";import{stubCallerIdentity as m}from"../../types/deployment/index.js";import{ensureOrganisationExists as l}from"../../aws/organisations/organisation.js";import{buildParamsContext as I,resolveAccountBootstrapSkipOidc as C}from"../contextHelpers.js";function A(t,n,r,o,i,g,d,c){const e=n.awsProvider.getRegion();return f.buildDeploymentContext({deployType:o,target:r.target,path:r.path,region:e,accountName:g,callerIdentity:m(n.awsProvider.getAccountId()),orgId:i.orgId,rootId:i.rootId,managementAccountId:i.managementAccountId,...I({orgConfig:t.orgConfig,identity:t.identity,skipOidc:C(o,t.orgConfig,t.options?.skipOidc,c),...o!=="organisation"?{region:e,primaryRegion:t.orgConfig?.primaryRegion}:{},trailLifecycle:d})},{verbose:t.options?.verbose,infraOnly:t.options?.infraOnly},t.orgConfig)}async function k(t,n){const r=t.awsProvider.getClient(u),o=await l(r,n);return o.success?a({orgId:o.data.orgId,rootId:o.data.rootId,managementAccountId:o.data.managementAccountId}):s(o.error)}export{A as buildOrgContext,k as resolveOrgDetails};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{success as
|
|
1
|
+
import{success as N}from"@fjall/generator";import{BackupClient as w}from"@aws-sdk/client-backup";import{IAMClient as D}from"@aws-sdk/client-iam";import{getOrganisationStackName as I}from"../../types/operations.js";import{accountHasDisasterRecovery as k,describeBackupVaultExists as y}from"../../aws/organisations/backup.js";import{describeAccountGlobalsExist as L,buildMissingAccountGlobalsAdvisory as T}from"../../aws/organisations/accountGlobals.js";import{targetsNonPrimaryRegion as h,synthOrFail as M,bootstrapOrFail as v,forwardOutput as F,forwardResourceProgress as G}from"../contextHelpers.js";import{accountTier as S}from"@fjall/util";import{buildOrgContext as Y,resolveOrgDetails as x}from"./orgContext.js";import{INFRA_STEPS as o,INFRA_STEP_TOTAL as i,failPrepareStep as c,maskAndFail as d,readStackOutputsBestEffort as B}from"./infraSteps.js";async function X(e,r,a,s,E){const{callbacks:t}=e;t.onStepComplete?.(o.CONNECT.id,o.CONNECT.name,"completed",0,i),t.onStepStart?.(o.PREPARE.id,o.PREPARE.name,1,i);const R=r.awsProvider.getRegion(),l=e.orgConfig?.primaryRegion;if(l!==void 0&&h(R,l)){const n=await L(r.awsProvider.getClient(D),e.abortSignal);if(!n.success)return c(t),d(t,n.error.message);if(!n.data)return c(t),d(t,T({target:a.target,deployRegion:R,primaryRegion:l,...e.orgConfig!==void 0?{providerAccounts:e.orgConfig.providerAccounts}:{}}))}const f=await x(r,e.abortSignal);if(!f.success)return c(t),d(t,f.error.message);const u=s==="account"?e.orgConfig?.providerAccounts.find(n=>n.name===a.target):e.orgConfig?.providerAccounts.find(n=>S(n)==="platform"),A=s==="account"&&u!==void 0&&S(u)==="organisation",g=Y(e,r,a,s,f.data,s==="account"?a.target:void 0,u?.trailLifecycle,A),m=e.orgConfig?.disasterRecoveryRegion;if(s==="account"&&(m!==void 0&&m!=="")&&(u===void 0||k(u.environment,m))){const n=await y(r.awsProvider.getClient(w),e.abortSignal);if(!n.success)return c(t),d(t,n.error.message);g.fjallAdoptBackupVault=n.data}t.onLog?.(`Synthesising ${s} infrastructure\u2026`,"info");const p=await M(r,g,t,"CDK synthesis failed");if(!p.success)return c(t),p;const P=await v(r,g,t);if(!P.success)return c(t),P;t.onStepComplete?.(o.PREPARE.id,o.PREPARE.name,"completed",1,i);const C=I(a.type);t.onStepStart?.(o.DEPLOY.id,o.DEPLOY.name,2,i);const O=await r.cdkService.runCdkDeploy(g,C,F(t),G(t),r.awsProvider);if(!O.success)return t.onStepComplete?.(o.DEPLOY.id,o.DEPLOY.name,"error",2,i),d(t,O.error);t.onStepComplete?.(o.DEPLOY.id,o.DEPLOY.name,"completed",2,i);const b=await B(r,t,C,"Failed to read stack outputs (non-critical)");return t.onStepStart?.(o.MONITORING.id,o.MONITORING.name,3,i),t.onStepComplete?.(o.MONITORING.id,o.MONITORING.name,"completed",3,i),N({target:a.target,deploymentType:"organisation",outputs:b,durationMs:Date.now()-E})}export{X as deploySingleComponent};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fjall/deploy-core",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.17.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",
|
|
@@ -78,8 +78,8 @@
|
|
|
78
78
|
"@aws-sdk/client-sqs": "^3.1067.0",
|
|
79
79
|
"@aws-sdk/client-sso-admin": "^3.1038.0",
|
|
80
80
|
"@aws-sdk/client-sts": "^3.1038.0",
|
|
81
|
-
"@fjall/generator": "^2.
|
|
82
|
-
"@fjall/util": "^2.
|
|
81
|
+
"@fjall/generator": "^2.17.0",
|
|
82
|
+
"@fjall/util": "^2.17.0",
|
|
83
83
|
"@smithy/node-http-handler": "^4.6.1",
|
|
84
84
|
"tsx": "^4.21.0",
|
|
85
85
|
"zod": "^4.4.3"
|
|
@@ -88,5 +88,5 @@
|
|
|
88
88
|
"@types/node": "^25.6.0",
|
|
89
89
|
"vitest": "^4.1.5"
|
|
90
90
|
},
|
|
91
|
-
"gitHead": "
|
|
91
|
+
"gitHead": "21cfe1aae339e12183af2813ec81f581b9b77d49"
|
|
92
92
|
}
|