@fjall/deploy-core 2.19.2 → 2.19.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/.minified CHANGED
@@ -1 +1 @@
1
- 152 files minified at 2026-06-20T09:23:13.824Z
1
+ 152 files minified at 2026-06-20T10:10:32.930Z
@@ -1 +1 @@
1
- import{isAbsolute as O,join as E,dirname as T,resolve as R}from"node:path";import{existsSync as $}from"node:fs";import{success as D,failure as h}from"@fjall/generator";import{deriveContentHashTag as w,maskSensitiveOutput as k}from"@fjall/util";import{logger as S}from"@fjall/util/logger";import{parseDockerServicesFromManifest as A}from"@fjall/util/manifest";import{STEP_IDS as v}from"../types/stepDefinitions.js";const b="dockerBuildHelper",P="Building and pushing Docker image";function x(e,n,r){const t=E(n,e);if($(t))return{path:t,base:n};if(r!==""&&r!==n){const i=E(r,e);if($(i))return{path:i,base:r}}return{path:t,base:n}}function I(e,n,r){const t=e.docker.path,i=e.docker.context;let o,a;if(O(t))o=t,a=n;else{const c=x(t,n,r);o=c.path,a=c.base}return{buildContext:i?O(i)?R(i):R(a,i):R(T(o)),dockerfilePath:o,target:e.docker.target,resolvedViaFallback:a!==n}}function L(e){return`${e.buildContext}|${e.dockerfilePath}|${e.target??""}`}function B(e,n){return`Dockerfile not found for service '${e}' at ${n}. Checked the synth directory and the working directory. If your Dockerfile lives at the project root with the CDK app in a subdirectory, set an absolute docker.context (the build context must share the Dockerfile's directory).`}function _(e){return e.startsWith("cn-")?"amazonaws.com.cn":"amazonaws.com"}function K(e,n,r){return`${e}.dkr.ecr.${n}.${_(n)}/${r.toLowerCase()}`}function H(e,n){const r=n.name.toLowerCase(),t=n.docker.target,i=t?`-${t.toLowerCase()}`:"";return[`${e}:${r}${i}-latest`]}function j(e,n,r,t){const i=new Map;for(const o of e){const a=I(o,n,t),u=L(a),c=H(r,o),d=i.get(u);d?(d.members.push(o),d.latestTags.push(...c)):i.set(u,{identity:a,members:[o],latestTags:[...c]})}return Array.from(i.values())}async function F(e,n,r,t,i){const o=await e.buildAndPush({appName:n.appName,appPath:n.path,region:r,accountId:t},(a,u,c,d)=>{i.onDockerProgress?.(k(a),u,c,d)});return o.success?D({imageUri:o.data.imageUri,imageTag:o.data.imageTag}):h(o.error)}async function U(e,n,r,t,i,o,a){const u=e.members[0];if(!$(e.identity.dockerfilePath))return h(new Error(B(u.name,e.identity.dockerfilePath)));e.identity.resolvedViaFallback&&a.onLog?.(`Building service '${u.name}' from ${e.identity.dockerfilePath} (resolved at the project root).`,"info");const c={appName:r.appName,appPath:e.identity.buildContext,region:t,accountId:i,buildContext:e.identity.buildContext,dockerfilePath:e.identity.dockerfilePath,imageTags:e.latestTags,serviceName:u.name,...e.identity.target!==void 0&&{target:e.identity.target},...u.docker.buildArgs!==void 0&&{buildArgs:u.docker.buildArgs}},d=await n.buildAndPush(c,(g,C,f,m)=>{a.onDockerProgress?.(k(g),C,f,m)});if(!d.success)return h(d.error);const p=d.data.imageDigest;if(typeof p!="string"||!p.startsWith("sha256:"))return h(new Error(`Buildx push succeeded but returned an invalid digest: ${k(String(p))}`));const l={},y=[];for(const g of e.members){const C=g.docker.target,f=w(p,g.name,C);if(!f.success)return h(f.error);const m=f.data,N=g.name;l[N]=m,y.push(`${o}:${m}`)}const s=await n.tagByDigest({sourceImage:o,digest:p,tags:y});return s.success?D({contentHashTagsByService:l}):h(s.error)}async function J(e,n,r,t){const i=e.dockerProvider;if(!i)return D({});const o=n.awsProvider.getAccountId();if(!o)return t.onLog?.("Skipping Docker build \u2014 account ID not available","warn"),D({});const a=n.awsProvider.getRegion(),u=E(r.path,"cdk.out"),c=A(u),d=K(o,a,r.appName);S.debug(b,"runDockerBuild starting",{appName:r.appName,appPath:r.path,accountId:o,region:a,cdkOutPath:u,manifestServiceCount:c.length,manifestServices:c.map(s=>({name:s.name,path:s.docker.path,context:s.docker.context,target:s.docker.target})),stepId:v.DOCKER_OPERATIONS,stepName:P}),t.onStepStart?.(v.DOCKER_OPERATIONS,P),t.onLog?.("Initialising ECR repository\u2026","info");const p=await i.initialiseECR({appName:r.appName,region:a,accountId:o});if(p.success||t.onLog?.(`ECR initialisation failed: ${k(p.error.message)}`,"warn"),c.length===0){t.onLog?.("Building and pushing Docker image\u2026","info");const s=await F(i,r,a,o,t);if(!s.success){S.debug(b,"Docker buildAndPush (legacy) failed",{appName:r.appName,error:k(s.error.message)}),t.onStepComplete?.(v.DOCKER_OPERATIONS,P,"error");const g=new Error(k(s.error.message));return t.onError?.(g),h(g)}return t.onStepComplete?.(v.DOCKER_OPERATIONS,P,"completed"),t.onLog?.(`Docker image pushed: ${s.data.imageUri}`,"info"),D({})}const l=j(c,r.path,d,e.workingDirectory);t.onLog?.(`Building and pushing ${c.length} service image(s) in ${l.length} group(s)\u2026`,"info");const y={};for(let s=0;s<l.length;s++){const g=l[s],C=g.members.map(m=>m.name).join(", ");t.onLog?.(`Building group ${s+1}/${l.length} (${C})\u2026`,"info");const f=await U(g,i,r,a,o,d,t);if(!f.success){S.debug(b,"Docker buildAndPush failed",{appName:r.appName,leader:g.members[0]?.name,members:C,error:k(f.error.message)}),t.onStepComplete?.(v.DOCKER_OPERATIONS,P,"error");const m=new Error(k(f.error.message));return t.onError?.(m),h(m)}for(const[m,N]of Object.entries(f.data.contentHashTagsByService))y[m]=N}return S.debug(b,"Docker buildAndPush succeeded",{appName:r.appName,services:c.map(s=>s.name),groupCount:l.length,contentHashTags:y}),t.onStepComplete?.(v.DOCKER_OPERATIONS,P,"completed"),t.onLog?.(`Docker images pushed for ${c.length} service(s)`,"info"),D(y)}export{P as DOCKER_BUILD_STEP_NAME,J as runDockerBuild};
1
+ import{isAbsolute as $,join as N,dirname as T,resolve as E}from"node:path";import{existsSync as R}from"node:fs";import{success as b,failure as h}from"@fjall/generator";import{deriveContentHashTag as w,maskSensitiveOutput as k,toKebab as A}from"@fjall/util";import{logger as P}from"@fjall/util/logger";import{parseDockerServicesFromManifest as x}from"@fjall/util/manifest";import{STEP_IDS as C}from"../types/stepDefinitions.js";const S="dockerBuildHelper",v="Building and pushing Docker image";function I(e,n,r){const t=N(n,e);if(R(t))return{path:t,base:n};if(r!==""&&r!==n){const i=N(r,e);if(R(i))return{path:i,base:r}}return{path:t,base:n}}function L(e,n,r){const t=e.docker.path,i=e.docker.context;let o,s;if($(t))o=t,s=n;else{const a=I(t,n,r);o=a.path,s=a.base}return{buildContext:i?$(i)?E(i):E(s,i):E(T(o)),dockerfilePath:o,target:e.docker.target,resolvedViaFallback:s!==n}}function B(e){return`${e.buildContext}|${e.dockerfilePath}|${e.target??""}`}function _(e,n){return`Dockerfile not found for service '${e}' at ${n}. Checked the synth directory and the working directory. If your Dockerfile lives at the project root with the CDK app in a subdirectory, set an absolute docker.context (the build context must share the Dockerfile's directory).`}function K(e){return e.startsWith("cn-")?"amazonaws.com.cn":"amazonaws.com"}function H(e,n,r){return`${e}.dkr.ecr.${n}.${K(n)}/${A(r)}`}function j(e,n){const r=n.name.toLowerCase(),t=n.docker.target,i=t?`-${t.toLowerCase()}`:"";return[`${e}:${r}${i}-latest`]}function F(e,n,r,t){const i=new Map;for(const o of e){const s=L(o,n,t),u=B(s),a=j(r,o),d=i.get(u);d?(d.members.push(o),d.latestTags.push(...a)):i.set(u,{identity:s,members:[o],latestTags:[...a]})}return Array.from(i.values())}async function U(e,n,r,t,i){const o=await e.buildAndPush({appName:n.appName,appPath:n.path,region:r,accountId:t},(s,u,a,d)=>{i.onDockerProgress?.(k(s),u,a,d)});return o.success?b({imageUri:o.data.imageUri,imageTag:o.data.imageTag}):h(o.error)}async function M(e,n,r,t,i,o,s){const u=e.members[0];if(!R(e.identity.dockerfilePath))return h(new Error(_(u.name,e.identity.dockerfilePath)));e.identity.resolvedViaFallback&&s.onLog?.(`Building service '${u.name}' from ${e.identity.dockerfilePath} (resolved at the project root).`,"info");const a={appName:r.appName,appPath:e.identity.buildContext,region:t,accountId:i,buildContext:e.identity.buildContext,dockerfilePath:e.identity.dockerfilePath,imageTags:e.latestTags,serviceName:u.name,...e.identity.target!==void 0&&{target:e.identity.target},...u.docker.buildArgs!==void 0&&{buildArgs:u.docker.buildArgs}},d=await n.buildAndPush(a,(p,l,g,D)=>{s.onDockerProgress?.(k(p),l,g,D)});if(!d.success)return h(d.error);const m=d.data.imageDigest;if(typeof m!="string"||!m.startsWith("sha256:"))return h(new Error(`Buildx push succeeded but returned an invalid digest: ${k(String(m))}`));const y={},c=[];for(const p of e.members){const l=p.docker.target,g=w(m,p.name,l);if(!g.success)return h(g.error);const D=g.data,O=p.name;y[O]=D,c.push(`${o}:${D}`)}const f=await n.tagByDigest({sourceImage:o,digest:m,tags:c});return f.success?b({contentHashTagsByService:y}):h(f.error)}async function Q(e,n,r,t){const i=e.dockerProvider;if(!i)return b({});const o=n.awsProvider.getAccountId();if(!o)return t.onLog?.("Skipping Docker build \u2014 account ID not available","warn"),b({});const s=n.awsProvider.getRegion(),u=N(r.path,"cdk.out"),a=x(u),d=H(o,s,r.appName);if(P.debug(S,"runDockerBuild starting",{appName:r.appName,appPath:r.path,accountId:o,region:s,cdkOutPath:u,manifestServiceCount:a.length,manifestServices:a.map(c=>({name:c.name,path:c.docker.path,context:c.docker.context,target:c.docker.target})),stepId:C.DOCKER_OPERATIONS,stepName:v}),t.onStepStart?.(C.DOCKER_OPERATIONS,v),a.length===0){t.onLog?.("Initialising ECR repository\u2026","info");const c=await i.initialiseECR({appName:r.appName,region:s,accountId:o});c.success||t.onLog?.(`ECR initialisation failed: ${k(c.error.message)}`,"warn"),t.onLog?.("Building and pushing Docker image\u2026","info");const f=await U(i,r,s,o,t);if(!f.success){P.debug(S,"Docker buildAndPush (legacy) failed",{appName:r.appName,error:k(f.error.message)}),t.onStepComplete?.(C.DOCKER_OPERATIONS,v,"error");const p=new Error(k(f.error.message));return t.onError?.(p),h(p)}return t.onStepComplete?.(C.DOCKER_OPERATIONS,v,"completed"),t.onLog?.(`Docker image pushed: ${f.data.imageUri}`,"info"),b({})}const m=F(a,r.path,d,e.workingDirectory);t.onLog?.(`Building and pushing ${a.length} service image(s) in ${m.length} group(s)\u2026`,"info");const y={};for(let c=0;c<m.length;c++){const f=m[c],p=f.members.map(g=>g.name).join(", ");t.onLog?.(`Building group ${c+1}/${m.length} (${p})\u2026`,"info");const l=await M(f,i,r,s,o,d,t);if(!l.success){P.debug(S,"Docker buildAndPush failed",{appName:r.appName,leader:f.members[0]?.name,members:p,error:k(l.error.message)}),t.onStepComplete?.(C.DOCKER_OPERATIONS,v,"error");const g=new Error(k(l.error.message));return t.onError?.(g),h(g)}for(const[g,D]of Object.entries(l.data.contentHashTagsByService))y[g]=D}return P.debug(S,"Docker buildAndPush succeeded",{appName:r.appName,services:a.map(c=>c.name),groupCount:m.length,contentHashTags:y}),t.onStepComplete?.(C.DOCKER_OPERATIONS,v,"completed"),t.onLog?.(`Docker images pushed for ${a.length} service(s)`,"info"),b(y)}export{v as DOCKER_BUILD_STEP_NAME,Q as runDockerBuild};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fjall/deploy-core",
3
- "version": "2.19.2",
3
+ "version": "2.19.3",
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.19.2",
82
- "@fjall/util": "^2.19.2",
81
+ "@fjall/generator": "^2.19.3",
82
+ "@fjall/util": "^2.19.3",
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": "9c77ec058ff9a6bbd09d318747ff9cf3152ee58b"
91
+ "gitHead": "622bf104895ff3f51147de46d5b70c5928aaaa86"
92
92
  }