@@ -14,37 +14,27 @@ use crate::python_version::{
14
14
} ;
15
15
use crate :: python_version_file:: ParsePythonVersionFileError ;
16
16
use crate :: utils:: {
17
- CapturedCommandError , DownloadUnpackArchiveError , FileExistsError , FindBundledPipError ,
18
- ReadOptionalFileError , StreamedCommandError ,
17
+ CapturedCommandError , CommandIoError , DownloadUnpackArchiveError , FileExistsError ,
18
+ FindBundledPipError , ReadOptionalFileError , StreamedCommandError ,
19
19
} ;
20
20
use indoc:: { formatdoc, indoc} ;
21
21
use libherokubuildpack:: log:: log_error;
22
- use std:: io;
23
22
24
23
/// Handle any non-recoverable buildpack or libcnb errors that occur.
25
24
///
26
25
/// The buildpack will exit non-zero after this handler has run, so all that needs to be
27
26
/// performed here is the logging of an error message - and in the future, emitting metrics.
28
- ///
29
- /// We're intentionally not using `libherokubuildpack::error::on_error` since:
30
- /// - It doesn't currently do anything other than logging an internal error for the libcnb
31
- /// error case, and by inlining that here it's easier to keep the output consistent with
32
- /// the messages emitted for buildpack-specific errors.
33
- /// - Using it causes trait mismatch errors when Dependabot PRs incrementally update crates.
34
- /// - When we want to add metrics to our buildpacks, it's going to need a rewrite of
35
- /// `Buildpack::on_error` anyway (we'll need to write out metrics not log them, so will need
36
- /// access to the `BuildContext`), at which point we can re-evaluate.
37
27
pub ( crate ) fn on_error ( error : libcnb:: Error < BuildpackError > ) {
38
28
match error {
39
29
libcnb:: Error :: BuildpackError ( buildpack_error) => on_buildpack_error ( buildpack_error) ,
40
30
libcnb_error => log_error (
41
31
"Internal buildpack error" ,
42
32
formatdoc ! { "
43
- An unexpected internal error was reported by the framework used by this buildpack.
44
-
45
- Please open a support ticket and include the full log output of this build.
46
-
33
+ An error was reported by the framework used by this buildpack.
34
+
47
35
Details: {libcnb_error}
36
+
37
+ {INTERNAL_ERROR_MESSAGE}
48
38
" } ,
49
39
) ,
50
40
}
@@ -303,10 +293,16 @@ fn on_python_layer_error(error: PythonLayerError) {
303
293
Details: {ureq_error}
304
294
" } ,
305
295
) ,
306
- DownloadUnpackArchiveError :: Unpack ( io_error) => log_io_error (
296
+ DownloadUnpackArchiveError :: Unpack ( io_error) => log_error (
307
297
"Unable to unpack the Python archive" ,
308
- "unpacking the downloaded Python runtime archive and writing it to disk" ,
309
- & io_error,
298
+ // TODO: Investigate under what circumstances this error can occur, and so whether
299
+ // we should label this as an internal error or else list suggested actions.
300
+ formatdoc ! { "
301
+ An I/O error occurred while unpacking the downloaded Python
302
+ runtime archive and writing it to disk.
303
+
304
+ Details: I/O Error: {io_error}
305
+ " } ,
310
306
) ,
311
307
} ,
312
308
// TODO: Remove this once versions are validated against a manifest (at which point all
@@ -347,11 +343,7 @@ fn on_python_layer_error(error: PythonLayerError) {
347
343
fn on_pip_layer_error ( error : PipLayerError ) {
348
344
match error {
349
345
PipLayerError :: InstallPipCommand ( error) => match error {
350
- StreamedCommandError :: Io ( io_error) => log_io_error (
351
- "Unable to install pip" ,
352
- "running 'python' to install pip" ,
353
- & io_error,
354
- ) ,
346
+ StreamedCommandError :: Io ( error) => log_command_io_error ( error) ,
355
347
StreamedCommandError :: NonZeroExitStatus ( exit_status) => log_error (
356
348
"Unable to install pip" ,
357
349
formatdoc ! { "
@@ -375,11 +367,7 @@ fn on_pip_layer_error(error: PipLayerError) {
375
367
fn on_pip_dependencies_layer_error ( error : PipDependenciesLayerError ) {
376
368
match error {
377
369
PipDependenciesLayerError :: CreateVenvCommand ( error) => match error {
378
- StreamedCommandError :: Io ( io_error) => log_io_error (
379
- "Unable to create virtual environment" ,
380
- "running 'python -m venv' to create a virtual environment" ,
381
- & io_error,
382
- ) ,
370
+ StreamedCommandError :: Io ( error) => log_command_io_error ( error) ,
383
371
StreamedCommandError :: NonZeroExitStatus ( exit_status) => log_error (
384
372
"Unable to create virtual environment" ,
385
373
formatdoc ! { "
@@ -391,11 +379,7 @@ fn on_pip_dependencies_layer_error(error: PipDependenciesLayerError) {
391
379
) ,
392
380
} ,
393
381
PipDependenciesLayerError :: PipInstallCommand ( error) => match error {
394
- StreamedCommandError :: Io ( io_error) => log_io_error (
395
- "Unable to install dependencies using pip" ,
396
- "running 'pip install' to install the app's dependencies" ,
397
- & io_error,
398
- ) ,
382
+ StreamedCommandError :: Io ( error) => log_command_io_error ( error) ,
399
383
// TODO: Add more suggestions here as to causes (eg network, invalid requirements.txt,
400
384
// package broken or not compatible with version of Python, missing system dependencies etc)
401
385
StreamedCommandError :: NonZeroExitStatus ( exit_status) => log_error (
@@ -414,11 +398,7 @@ fn on_pip_dependencies_layer_error(error: PipDependenciesLayerError) {
414
398
fn on_poetry_layer_error ( error : PoetryLayerError ) {
415
399
match error {
416
400
PoetryLayerError :: InstallPoetryCommand ( error) => match error {
417
- StreamedCommandError :: Io ( io_error) => log_io_error (
418
- "Unable to install Poetry" ,
419
- "running 'python' to install Poetry" ,
420
- & io_error,
421
- ) ,
401
+ StreamedCommandError :: Io ( error) => log_command_io_error ( error) ,
422
402
StreamedCommandError :: NonZeroExitStatus ( exit_status) => log_error (
423
403
"Unable to install Poetry" ,
424
404
formatdoc ! { "
@@ -442,11 +422,7 @@ fn on_poetry_layer_error(error: PoetryLayerError) {
442
422
fn on_poetry_dependencies_layer_error ( error : PoetryDependenciesLayerError ) {
443
423
match error {
444
424
PoetryDependenciesLayerError :: CreateVenvCommand ( error) => match error {
445
- StreamedCommandError :: Io ( io_error) => log_io_error (
446
- "Unable to create virtual environment" ,
447
- "running 'python -m venv' to create a virtual environment" ,
448
- & io_error,
449
- ) ,
425
+ StreamedCommandError :: Io ( error) => log_command_io_error ( error) ,
450
426
StreamedCommandError :: NonZeroExitStatus ( exit_status) => log_error (
451
427
"Unable to create virtual environment" ,
452
428
formatdoc ! { "
@@ -458,11 +434,7 @@ fn on_poetry_dependencies_layer_error(error: PoetryDependenciesLayerError) {
458
434
) ,
459
435
} ,
460
436
PoetryDependenciesLayerError :: PoetryInstallCommand ( error) => match error {
461
- StreamedCommandError :: Io ( io_error) => log_io_error (
462
- "Unable to install dependencies using Poetry" ,
463
- "running 'poetry install' to install the app's dependencies" ,
464
- & io_error,
465
- ) ,
437
+ StreamedCommandError :: Io ( error) => log_command_io_error ( error) ,
466
438
// TODO: Add more suggestions here as to possible causes (similar to pip)
467
439
StreamedCommandError :: NonZeroExitStatus ( exit_status) => log_error (
468
440
"Unable to install dependencies using Poetry" ,
@@ -480,11 +452,7 @@ fn on_poetry_dependencies_layer_error(error: PoetryDependenciesLayerError) {
480
452
fn on_django_collectstatic_error ( error : DjangoCollectstaticError ) {
481
453
match error {
482
454
DjangoCollectstaticError :: CheckCollectstaticCommandExists ( error) => match error {
483
- CapturedCommandError :: Io ( io_error) => log_io_error (
484
- "Unable to inspect Django configuration" ,
485
- "running 'python manage.py help collectstatic' to inspect the Django configuration" ,
486
- & io_error,
487
- ) ,
455
+ CapturedCommandError :: Io ( error) => log_command_io_error ( error) ,
488
456
CapturedCommandError :: NonZeroExitStatus ( output) => log_error (
489
457
"Unable to inspect Django configuration" ,
490
458
formatdoc ! { "
@@ -509,11 +477,7 @@ fn on_django_collectstatic_error(error: DjangoCollectstaticError) {
509
477
log_file_exists_error ( error) ;
510
478
}
511
479
DjangoCollectstaticError :: CollectstaticCommand ( error) => match error {
512
- StreamedCommandError :: Io ( io_error) => log_io_error (
513
- "Unable to generate Django static files" ,
514
- "running 'python manage.py collectstatic' to generate Django static files" ,
515
- & io_error,
516
- ) ,
480
+ StreamedCommandError :: Io ( error) => log_command_io_error ( error) ,
517
481
StreamedCommandError :: NonZeroExitStatus ( exit_status) => log_error (
518
482
"Unable to generate Django static files" ,
519
483
formatdoc ! { "
@@ -537,21 +501,6 @@ fn on_django_collectstatic_error(error: DjangoCollectstaticError) {
537
501
}
538
502
}
539
503
540
- // This is now only used for Command I/O errors.
541
- // TODO: Replace this with specialised handling for Command I/O errors.
542
- fn log_io_error ( header : & str , occurred_whilst : & str , io_error : & io:: Error ) {
543
- // We don't suggest opening a support ticket, since a subset of I/O errors can be caused
544
- // by issues in the application. In the future, perhaps we should try and split these out?
545
- log_error (
546
- header,
547
- formatdoc ! { "
548
- An unexpected error occurred whilst {occurred_whilst}.
549
-
550
- Details: I/O Error: {io_error}
551
- " } ,
552
- ) ;
553
- }
554
-
555
504
fn log_file_exists_error ( FileExistsError { path, io_error } : FileExistsError ) {
556
505
let filepath = path. to_string_lossy ( ) ;
557
506
let filename = path
@@ -567,7 +516,7 @@ fn log_file_exists_error(FileExistsError { path, io_error }: FileExistsError) {
567
516
568
517
Details: {io_error}
569
518
570
- Try building again to see if the error resolves itself.
519
+ {INTERNAL_ERROR_MESSAGE}
571
520
" } ,
572
521
) ;
573
522
}
@@ -602,7 +551,6 @@ fn log_find_bundled_pip_error(
602
551
) {
603
552
let bundled_wheels_dir = bundled_wheels_dir. to_string_lossy ( ) ;
604
553
605
- // TODO: Decide whether we want users to file a bug or open a support ticket.
606
554
log_error (
607
555
"Unable to locate the Python stdlib's bundled pip" ,
608
556
formatdoc ! { "
@@ -612,9 +560,32 @@ fn log_find_bundled_pip_error(
612
560
613
561
Details: {io_error}
614
562
615
- This is an internal error. Please report it as a bug, here:
616
- https://github.com/heroku/buildpacks-python/issues
563
+ {INTERNAL_ERROR_MESSAGE}
617
564
"
618
565
} ,
619
566
) ;
620
567
}
568
+
569
+ fn log_command_io_error ( CommandIoError { program, io_error } : CommandIoError ) {
570
+ log_error (
571
+ format ! ( "Unable to run {program}" ) ,
572
+ formatdoc ! { "
573
+ An I/O error occurred while trying to run:
574
+ `{program}`
575
+
576
+ Details: {io_error}
577
+
578
+ {INTERNAL_ERROR_MESSAGE}
579
+ " } ,
580
+ ) ;
581
+ }
582
+
583
+ const INTERNAL_ERROR_MESSAGE : & str = indoc ! { "
584
+ This is an unexpected error that could be caused by a bug
585
+ in this buildpack, or an issue with the build environment.
586
+
587
+ Try building again to see if the error resolves itself.
588
+
589
+ If it doesn't, please file a bug report here:
590
+ https://github.com/heroku/buildpacks-python/issues
591
+ " } ;
0 commit comments