@@ -43,9 +43,15 @@ import { logger } from "../utilities/logger.js";
4343import { createTaskFileImports , gatherTaskFiles } from "../utilities/taskFiles" ;
4444import { login } from "./login" ;
4545
46+ import { Glob } from "glob" ;
4647import type { SetOptional } from "type-fest" ;
4748import { bundleDependenciesPlugin , workerSetupImportConfigPlugin } from "../utilities/build" ;
48- import { Glob } from "glob" ;
49+ import { chalkError , chalkPurple , chalkWarning } from "../utilities/cliOutput" ;
50+ import {
51+ logESMRequireError ,
52+ parseBuildErrorStack ,
53+ parseNpmInstallError ,
54+ } from "../utilities/deployErrors" ;
4955
5056const DeployCommandOptions = CommonCommandOptions . extend ( {
5157 skipTypecheck : z . boolean ( ) . default ( false ) ,
@@ -272,28 +278,44 @@ async function _deployCommand(dir: string, options: DeployCommandOptions) {
272278 ) ;
273279 }
274280
275- return buildAndPushImage ( {
276- registryHost,
277- auth : authorization . auth . accessToken ,
278- imageTag : deploymentResponse . data . imageTag ,
279- buildId : deploymentResponse . data . externalBuildData . buildId ,
280- buildToken : deploymentResponse . data . externalBuildData . buildToken ,
281- buildProjectId : deploymentResponse . data . externalBuildData . projectId ,
282- cwd : compilation . path ,
283- projectId : resolvedConfig . config . project ,
284- deploymentId : deploymentResponse . data . id ,
285- deploymentVersion : deploymentResponse . data . version ,
286- contentHash : deploymentResponse . data . contentHash ,
287- projectRef : resolvedConfig . config . project ,
288- loadImage : options . loadImage ,
289- buildPlatform : options . buildPlatform ,
290- } ) ;
281+ return buildAndPushImage (
282+ {
283+ registryHost,
284+ auth : authorization . auth . accessToken ,
285+ imageTag : deploymentResponse . data . imageTag ,
286+ buildId : deploymentResponse . data . externalBuildData . buildId ,
287+ buildToken : deploymentResponse . data . externalBuildData . buildToken ,
288+ buildProjectId : deploymentResponse . data . externalBuildData . projectId ,
289+ cwd : compilation . path ,
290+ projectId : resolvedConfig . config . project ,
291+ deploymentId : deploymentResponse . data . id ,
292+ deploymentVersion : deploymentResponse . data . version ,
293+ contentHash : deploymentResponse . data . contentHash ,
294+ projectRef : resolvedConfig . config . project ,
295+ loadImage : options . loadImage ,
296+ buildPlatform : options . buildPlatform ,
297+ } ,
298+ deploymentSpinner
299+ ) ;
291300 } ;
292301
293302 const image = await buildImage ( ) ;
294303
295304 if ( ! image . ok ) {
296- deploymentSpinner . stop ( `Failed to build project image: ${ image . error } ` ) ;
305+ deploymentSpinner . stop ( `Failed to build project.` ) ;
306+
307+ // If there are logs, let's write it out to a temporary file and include the path in the error message
308+ if ( image . logs . trim ( ) !== "" ) {
309+ const logPath = join ( await createTempDir ( ) , `build-${ deploymentResponse . data . shortCode } .log` ) ;
310+
311+ await writeFile ( logPath , image . logs ) ;
312+
313+ logger . log (
314+ `${ chalkError ( "X Error:" ) } ${ image . error } . Full build logs have been saved to ${ logPath } )`
315+ ) ;
316+ } else {
317+ logger . log ( `${ chalkError ( "X Error:" ) } ${ image . error } .` ) ;
318+ }
297319
298320 throw new SkipLoggingError ( `Failed to build project image: ${ image . error } ` ) ;
299321 }
@@ -379,10 +401,19 @@ async function _deployCommand(dir: string, options: DeployCommandOptions) {
379401 }
380402 case "FAILED" : {
381403 if ( finishedDeployment . errorData ) {
382- deploymentSpinner . stop (
383- `Deployment encountered an error: ${ finishedDeployment . errorData . name } . ${ deploymentLink } `
384- ) ;
385- logger . error ( finishedDeployment . errorData . stack ) ;
404+ const parsedError = finishedDeployment . errorData . stack
405+ ? parseBuildErrorStack ( finishedDeployment . errorData )
406+ : finishedDeployment . errorData . message ;
407+
408+ if ( typeof parsedError === "string" ) {
409+ deploymentSpinner . stop ( `Deployment encountered an error. ${ deploymentLink } ` ) ;
410+
411+ logger . log ( `${ chalkError ( "X Error:" ) } ${ parsedError } ` ) ;
412+ } else {
413+ deploymentSpinner . stop ( `Deployment encountered an error. ${ deploymentLink } ` ) ;
414+
415+ logESMRequireError ( parsedError , resolvedConfig ) ;
416+ }
386417
387418 throw new SkipLoggingError (
388419 `Deployment encountered an error: ${ finishedDeployment . errorData . name } `
@@ -551,15 +582,18 @@ type BuildAndPushImageResults =
551582 | {
552583 ok : true ;
553584 image : string ;
585+ logs : string ;
554586 digest ?: string ;
555587 }
556588 | {
557589 ok : false ;
558590 error : string ;
591+ logs : string ;
559592 } ;
560593
561594async function buildAndPushImage (
562- options : BuildAndPushImageOptions
595+ options : BuildAndPushImageOptions ,
596+ updater : ReturnType < typeof spinner >
563597) : Promise < BuildAndPushImageResults > {
564598 return tracer . startActiveSpan ( "buildAndPushImage" , async ( span ) => {
565599 span . setAttributes ( {
@@ -626,7 +660,7 @@ async function buildAndPushImage(
626660 const errors : string [ ] = [ ] ;
627661
628662 try {
629- await new Promise < void > ( ( res , rej ) => {
663+ const processCode = await new Promise < number | null > ( ( res , rej ) => {
630664 // For some reason everything is output on stderr, not stdout
631665 childProcess . stderr ?. on ( "data" , ( data : Buffer ) => {
632666 const text = data . toString ( ) ;
@@ -636,9 +670,19 @@ async function buildAndPushImage(
636670 } ) ;
637671
638672 childProcess . on ( "error" , ( e ) => rej ( e ) ) ;
639- childProcess . on ( "close" , ( ) => res ( ) ) ;
673+ childProcess . on ( "close" , ( code ) => res ( code ) ) ;
640674 } ) ;
641675
676+ const logs = extractLogs ( errors ) ;
677+
678+ if ( processCode !== 0 ) {
679+ return {
680+ ok : false as const ,
681+ error : `Error building image` ,
682+ logs,
683+ } ;
684+ }
685+
642686 const digest = extractImageDigest ( errors ) ;
643687
644688 span . setAttributes ( {
@@ -650,6 +694,7 @@ async function buildAndPushImage(
650694 return {
651695 ok : true as const ,
652696 image : options . imageTag ,
697+ logs,
653698 digest,
654699 } ;
655700 } catch ( e ) {
@@ -659,6 +704,7 @@ async function buildAndPushImage(
659704 return {
660705 ok : false as const ,
661706 error : e instanceof Error ? e . message : JSON . stringify ( e ) ,
707+ logs : extractLogs ( errors ) ,
662708 } ;
663709 }
664710 } ) ;
@@ -751,6 +797,7 @@ async function buildAndPushSelfHostedImage(
751797 return {
752798 ok : false as const ,
753799 error : e instanceof Error ? e . message : JSON . stringify ( e ) ,
800+ logs : extractLogs ( errors ) ,
754801 } ;
755802 }
756803
@@ -793,6 +840,7 @@ async function buildAndPushSelfHostedImage(
793840 return {
794841 ok : false as const ,
795842 error : e instanceof Error ? e . message : JSON . stringify ( e ) ,
843+ logs : extractLogs ( errors ) ,
796844 } ;
797845 }
798846 }
@@ -803,6 +851,7 @@ async function buildAndPushSelfHostedImage(
803851 ok : true as const ,
804852 image : options . imageTag ,
805853 digest,
854+ logs : extractLogs ( errors ) ,
806855 } ;
807856 } ) ;
808857}
@@ -820,6 +869,13 @@ function extractImageDigest(outputs: string[]) {
820869 }
821870}
822871
872+ function extractLogs ( outputs : string [ ] ) {
873+ // Remove empty lines
874+ const cleanedOutputs = outputs . map ( ( line ) => line . trim ( ) ) . filter ( ( line ) => line !== "" ) ;
875+
876+ return cleanedOutputs . map ( ( line ) => line . trim ( ) ) . join ( "\n" ) ;
877+ }
878+
823879async function compileProject (
824880 config : ResolvedConfig ,
825881 options : DeployCommandOptions ,
@@ -1057,7 +1113,7 @@ async function compileProject(
10571113 ) ;
10581114
10591115 if ( ! resolvingDependenciesResult ) {
1060- throw new Error ( "Failed to resolve dependencies" ) ;
1116+ throw new SkipLoggingError ( "Failed to resolve dependencies" ) ;
10611117 }
10621118
10631119 // Write the Containerfile to /tmp/dir/Containerfile
@@ -1188,15 +1244,39 @@ async function resolveDependencies(
11881244
11891245 return true ;
11901246 } catch ( installError ) {
1191- logger . debug ( `Failed to resolve dependencies: ${ JSON . stringify ( installError ) } ` ) ;
1192-
11931247 recordSpanException ( span , installError ) ;
1194-
11951248 span . end ( ) ;
11961249
1197- resolvingDepsSpinner . stop (
1198- "Failed to resolve dependencies. Rerun with --log-level=debug for more information"
1199- ) ;
1250+ const parsedError = parseNpmInstallError ( installError ) ;
1251+
1252+ if ( typeof parsedError === "string" ) {
1253+ resolvingDepsSpinner . stop ( `Failed to resolve dependencies: ${ parsedError } ` ) ;
1254+ } else {
1255+ switch ( parsedError . type ) {
1256+ case "package-not-found-error" : {
1257+ resolvingDepsSpinner . stop ( `Failed to resolve dependencies` ) ;
1258+
1259+ logger . log (
1260+ `\n${ chalkError ( "X Error:" ) } The package ${ chalkPurple (
1261+ parsedError . packageName
1262+ ) } could not be found in the npm registry.`
1263+ ) ;
1264+
1265+ break ;
1266+ }
1267+ case "no-matching-version-error" : {
1268+ resolvingDepsSpinner . stop ( `Failed to resolve dependencies` ) ;
1269+
1270+ logger . log (
1271+ `\n${ chalkError ( "X Error:" ) } The package ${ chalkPurple (
1272+ parsedError . packageName
1273+ ) } could not resolve because the version doesn't exist`
1274+ ) ;
1275+
1276+ break ;
1277+ }
1278+ }
1279+ }
12001280
12011281 return false ;
12021282 }
@@ -1312,8 +1392,12 @@ async function gatherRequiredDependencies(
13121392 dependencies [ packageParts . name ] = externalDependencyVersion ;
13131393 continue ;
13141394 } else {
1315- logger . warn (
1316- `Could not find version for package ${ packageName } , add a version specifier to the package name (e.g. ${ packageParts . name } @latest) or add it to your project's package.json`
1395+ logger . log (
1396+ `${ chalkWarning ( "X Warning:" ) } Could not find version for package ${ chalkPurple (
1397+ packageName
1398+ ) } , add a version specifier to the package name (e.g. ${
1399+ packageParts . name
1400+ } @latest) or add it to your project's package.json`
13171401 ) ;
13181402 }
13191403 }
0 commit comments