@@ -43,9 +43,15 @@ import { logger } from "../utilities/logger.js";
43
43
import { createTaskFileImports , gatherTaskFiles } from "../utilities/taskFiles" ;
44
44
import { login } from "./login" ;
45
45
46
+ import { Glob } from "glob" ;
46
47
import type { SetOptional } from "type-fest" ;
47
48
import { 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" ;
49
55
50
56
const DeployCommandOptions = CommonCommandOptions . extend ( {
51
57
skipTypecheck : z . boolean ( ) . default ( false ) ,
@@ -272,28 +278,44 @@ async function _deployCommand(dir: string, options: DeployCommandOptions) {
272
278
) ;
273
279
}
274
280
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
+ ) ;
291
300
} ;
292
301
293
302
const image = await buildImage ( ) ;
294
303
295
304
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
+ }
297
319
298
320
throw new SkipLoggingError ( `Failed to build project image: ${ image . error } ` ) ;
299
321
}
@@ -379,10 +401,19 @@ async function _deployCommand(dir: string, options: DeployCommandOptions) {
379
401
}
380
402
case "FAILED" : {
381
403
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
+ }
386
417
387
418
throw new SkipLoggingError (
388
419
`Deployment encountered an error: ${ finishedDeployment . errorData . name } `
@@ -551,15 +582,18 @@ type BuildAndPushImageResults =
551
582
| {
552
583
ok : true ;
553
584
image : string ;
585
+ logs : string ;
554
586
digest ?: string ;
555
587
}
556
588
| {
557
589
ok : false ;
558
590
error : string ;
591
+ logs : string ;
559
592
} ;
560
593
561
594
async function buildAndPushImage (
562
- options : BuildAndPushImageOptions
595
+ options : BuildAndPushImageOptions ,
596
+ updater : ReturnType < typeof spinner >
563
597
) : Promise < BuildAndPushImageResults > {
564
598
return tracer . startActiveSpan ( "buildAndPushImage" , async ( span ) => {
565
599
span . setAttributes ( {
@@ -626,7 +660,7 @@ async function buildAndPushImage(
626
660
const errors : string [ ] = [ ] ;
627
661
628
662
try {
629
- await new Promise < void > ( ( res , rej ) => {
663
+ const processCode = await new Promise < number | null > ( ( res , rej ) => {
630
664
// For some reason everything is output on stderr, not stdout
631
665
childProcess . stderr ?. on ( "data" , ( data : Buffer ) => {
632
666
const text = data . toString ( ) ;
@@ -636,9 +670,19 @@ async function buildAndPushImage(
636
670
} ) ;
637
671
638
672
childProcess . on ( "error" , ( e ) => rej ( e ) ) ;
639
- childProcess . on ( "close" , ( ) => res ( ) ) ;
673
+ childProcess . on ( "close" , ( code ) => res ( code ) ) ;
640
674
} ) ;
641
675
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
+
642
686
const digest = extractImageDigest ( errors ) ;
643
687
644
688
span . setAttributes ( {
@@ -650,6 +694,7 @@ async function buildAndPushImage(
650
694
return {
651
695
ok : true as const ,
652
696
image : options . imageTag ,
697
+ logs,
653
698
digest,
654
699
} ;
655
700
} catch ( e ) {
@@ -659,6 +704,7 @@ async function buildAndPushImage(
659
704
return {
660
705
ok : false as const ,
661
706
error : e instanceof Error ? e . message : JSON . stringify ( e ) ,
707
+ logs : extractLogs ( errors ) ,
662
708
} ;
663
709
}
664
710
} ) ;
@@ -751,6 +797,7 @@ async function buildAndPushSelfHostedImage(
751
797
return {
752
798
ok : false as const ,
753
799
error : e instanceof Error ? e . message : JSON . stringify ( e ) ,
800
+ logs : extractLogs ( errors ) ,
754
801
} ;
755
802
}
756
803
@@ -793,6 +840,7 @@ async function buildAndPushSelfHostedImage(
793
840
return {
794
841
ok : false as const ,
795
842
error : e instanceof Error ? e . message : JSON . stringify ( e ) ,
843
+ logs : extractLogs ( errors ) ,
796
844
} ;
797
845
}
798
846
}
@@ -803,6 +851,7 @@ async function buildAndPushSelfHostedImage(
803
851
ok : true as const ,
804
852
image : options . imageTag ,
805
853
digest,
854
+ logs : extractLogs ( errors ) ,
806
855
} ;
807
856
} ) ;
808
857
}
@@ -820,6 +869,13 @@ function extractImageDigest(outputs: string[]) {
820
869
}
821
870
}
822
871
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
+
823
879
async function compileProject (
824
880
config : ResolvedConfig ,
825
881
options : DeployCommandOptions ,
@@ -1057,7 +1113,7 @@ async function compileProject(
1057
1113
) ;
1058
1114
1059
1115
if ( ! resolvingDependenciesResult ) {
1060
- throw new Error ( "Failed to resolve dependencies" ) ;
1116
+ throw new SkipLoggingError ( "Failed to resolve dependencies" ) ;
1061
1117
}
1062
1118
1063
1119
// Write the Containerfile to /tmp/dir/Containerfile
@@ -1188,15 +1244,39 @@ async function resolveDependencies(
1188
1244
1189
1245
return true ;
1190
1246
} catch ( installError ) {
1191
- logger . debug ( `Failed to resolve dependencies: ${ JSON . stringify ( installError ) } ` ) ;
1192
-
1193
1247
recordSpanException ( span , installError ) ;
1194
-
1195
1248
span . end ( ) ;
1196
1249
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
+ }
1200
1280
1201
1281
return false ;
1202
1282
}
@@ -1312,8 +1392,12 @@ async function gatherRequiredDependencies(
1312
1392
dependencies [ packageParts . name ] = externalDependencyVersion ;
1313
1393
continue ;
1314
1394
} 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`
1317
1401
) ;
1318
1402
}
1319
1403
}
0 commit comments