@@ -16,6 +16,7 @@ import (
1616 "github.com/docker/buildx/controller"
1717 cbuild "github.com/docker/buildx/controller/build"
1818 "github.com/docker/buildx/controller/control"
19+ controllererrors "github.com/docker/buildx/controller/errdefs"
1920 controllerapi "github.com/docker/buildx/controller/pb"
2021 "github.com/docker/buildx/monitor"
2122 "github.com/docker/buildx/store"
@@ -488,15 +489,7 @@ func launchControllerAndRunBuild(dockerCli command.Cli, options buildOptions) er
488489 }
489490 }()
490491
491- f := ioset .NewSingleForwarder ()
492- pr , pw := io .Pipe ()
493- f .SetWriter (pw , func () io.WriteCloser {
494- pw .Close () // propagate EOF
495- logrus .Debug ("propagating stdin close" )
496- return nil
497- })
498- f .SetReader (os .Stdin )
499-
492+ // Start build
500493 opts , err := options .toControllerOptions ()
501494 if err != nil {
502495 return err
@@ -506,38 +499,59 @@ func launchControllerAndRunBuild(dockerCli command.Cli, options buildOptions) er
506499 return err
507500 }
508501
509- // Avoid leaving a stale file if we eventually fail
510- if options .imageIDFile != "" {
511- if err := os .Remove (options .imageIDFile ); err != nil && ! os .IsNotExist (err ) {
512- return errors .Wrap (err , "removing image ID file" )
502+ var ref string
503+ var retErr error
504+ f := ioset .NewSingleForwarder ()
505+ f .SetReader (os .Stdin )
506+ if options .invoke != "debug-shell" {
507+ pr , pw := io .Pipe ()
508+ f .SetWriter (pw , func () io.WriteCloser {
509+ pw .Close () // propagate EOF
510+ logrus .Debug ("propagating stdin close" )
511+ return nil
512+ })
513+
514+ // Avoid leaving a stale file if we eventually fail
515+ if options .imageIDFile != "" {
516+ if err := os .Remove (options .imageIDFile ); err != nil && ! os .IsNotExist (err ) {
517+ return errors .Wrap (err , "removing image ID file" )
518+ }
513519 }
514- }
515520
516- // Start build
517- ref , resp , err := c .Build (ctx , opts , pr , os .Stdout , os .Stderr , progress )
518- if err != nil {
519- return errors .Wrapf (err , "failed to build" ) // TODO: allow invoke even on error
520- }
521- if err := pw .Close (); err != nil {
522- logrus .Debug ("failed to close stdin pipe writer" )
523- }
524- if err := pr .Close (); err != nil {
525- logrus .Debug ("failed to close stdin pipe reader" )
526- }
521+ var resp * client.SolveResponse
522+ ref , resp , err = c .Build (ctx , opts , pr , os .Stdout , os .Stderr , progress )
523+ if err != nil {
524+ var be * controllererrors.BuildError
525+ if errors .As (err , & be ) {
526+ ref = be .Ref
527+ retErr = err
528+ // We can proceed to monitor
529+ } else {
530+ return errors .Wrapf (err , "failed to build" )
531+ }
532+ }
533+ if err := pw .Close (); err != nil {
534+ logrus .Debug ("failed to close stdin pipe writer" )
535+ }
536+ if err := pr .Close (); err != nil {
537+ logrus .Debug ("failed to close stdin pipe reader" )
538+ }
527539
528- if options .quiet {
529- fmt .Println (resp .ExporterResponse [exptypes .ExporterImageDigestKey ])
530- }
531- if options .imageIDFile != "" {
532- dgst := resp .ExporterResponse [exptypes .ExporterImageDigestKey ]
533- if v , ok := resp .ExporterResponse [exptypes .ExporterImageConfigDigestKey ]; ok {
534- dgst = v
540+ if options .quiet {
541+ fmt .Println (resp .ExporterResponse [exptypes .ExporterImageDigestKey ])
542+ }
543+ if options .imageIDFile != "" {
544+ dgst := resp .ExporterResponse [exptypes .ExporterImageDigestKey ]
545+ if v , ok := resp .ExporterResponse [exptypes .ExporterImageConfigDigestKey ]; ok {
546+ dgst = v
547+ }
548+ return os .WriteFile (options .imageIDFile , []byte (dgst ), 0644 )
535549 }
536- return os . WriteFile ( options . imageIDFile , [] byte ( dgst ), 0644 )
550+
537551 }
538552
539553 // post-build operations
540- if options .invoke != "" {
554+ if needsMonitor ( options .invoke , retErr ) {
541555 pr2 , pw2 := io .Pipe ()
542556 f .SetWriter (pw2 , func () io.WriteCloser {
543557 pw2 .Close () // propagate EOF
@@ -550,7 +564,7 @@ func launchControllerAndRunBuild(dockerCli command.Cli, options buildOptions) er
550564 }
551565 return errors .Errorf ("failed to configure terminal: %v" , err )
552566 }
553- err = monitor .RunMonitor (ctx , ref , opts , invokeConfig , c , options . progress , pr2 , os .Stdout , os .Stderr )
567+ err = monitor .RunMonitor (ctx , ref , & opts , invokeConfig , c , progress , pr2 , os .Stdout , os .Stderr )
554568 con .Reset ()
555569 if err := pw2 .Close (); err != nil {
556570 logrus .Debug ("failed to close monitor stdin pipe reader" )
@@ -566,9 +580,26 @@ func launchControllerAndRunBuild(dockerCli command.Cli, options buildOptions) er
566580 return nil
567581}
568582
583+ func needsMonitor (invokeFlag string , retErr error ) bool {
584+ switch invokeFlag {
585+ case "debug-shell" :
586+ return true
587+ case "on-error" :
588+ return retErr != nil
589+ default :
590+ return invokeFlag != ""
591+ }
592+ }
593+
569594func parseInvokeConfig (invoke string ) (cfg controllerapi.ContainerConfig , err error ) {
570595 cfg .Tty = true
571- if invoke == "default" {
596+ switch invoke {
597+ case "default" , "debug-shell" :
598+ return cfg , nil
599+ case "on-error" :
600+ // NOTE: we overwrite the command to run because the original one should fail on the failed step.
601+ // TODO: make this configurable.
602+ cfg .Cmd = []string {"/bin/sh" }
572603 return cfg , nil
573604 }
574605
0 commit comments