@@ -238,6 +238,11 @@ func runBuild(dockerCli command.Cli, options buildOptions) (err error) {
238238 return err
239239 }
240240
241+ var isTerminal bool
242+ if _ , err := console .ConsoleFromFile (os .Stdout ); err == nil {
243+ isTerminal = true
244+ }
245+
241246 ctx2 , cancel := context .WithCancel (context .TODO ())
242247 defer cancel ()
243248 progressMode , err := options .toProgress ()
@@ -252,6 +257,7 @@ func runBuild(dockerCli command.Cli, options buildOptions) (err error) {
252257 ),
253258 progress .WithOnClose (func () {
254259 printWarnings (os .Stderr , printer .Warnings (), progressMode )
260+ printBuildDetails (os .Stderr , printer .BuildRefs (), isTerminal , progressMode )
255261 }),
256262 )
257263 if err != nil {
@@ -270,7 +276,7 @@ func runBuild(dockerCli command.Cli, options buildOptions) (err error) {
270276 retErr = err
271277 }
272278 if retErr != nil {
273- return retErr
279+ return wrapBuildError ( retErr , isTerminal , false )
274280 }
275281
276282 if options .quiet {
@@ -614,10 +620,15 @@ func decodeExporterResponse(exporterResponse map[string]string) map[string]inter
614620 return out
615621}
616622
617- func wrapBuildError (err error , bake bool ) error {
623+ func wrapBuildError (err error , term bool , bake bool ) error {
618624 if err == nil {
619625 return nil
620626 }
627+ if ebr , ok := err .(* build.ErrorWithBuildRef ); ok {
628+ return errors .Errorf ("%v\n \n %s" , ebr .Err , buildDetailsOutput (map [string ]string {
629+ "default" : ebr .Ref ,
630+ }, term , progress .PrinterModeAuto ))
631+ }
621632 st , ok := grpcerrors .AsGRPCStatus (err )
622633 if ok {
623634 if st .Code () == codes .Unimplemented && strings .Contains (st .Message (), "unsupported frontend capability moby.buildkit.frontend.contexts" ) {
@@ -843,3 +854,37 @@ func printValue(printer printFunc, version string, format string, res map[string
843854 }
844855 return printer ([]byte (res ["result.json" ]), os .Stdout )
845856}
857+
858+ func buildDetailsOutput (refs map [string ]string , term bool , mode string ) string {
859+ if len (refs ) == 0 || mode == progress .PrinterModeQuiet {
860+ return ""
861+ }
862+ refURL := func (ref string ) string {
863+ return fmt .Sprintf ("docker-desktop://dashboard/build/%s" , ref )
864+ }
865+ var out bytes.Buffer
866+ out .WriteString ("View build details: " )
867+ multiTargets := len (refs ) > 1
868+ for target , ref := range refs {
869+ if multiTargets {
870+ out .WriteString (fmt .Sprintf ("\n %s: " , target ))
871+ }
872+ if term {
873+ out .WriteString (hyperlink (refURL (ref )))
874+ } else {
875+ out .WriteString (refURL (ref ))
876+ }
877+ }
878+ return out .String ()
879+ }
880+
881+ func hyperlink (url string ) string {
882+ // create an escape sequence using the OSC 8 format: https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda
883+ return fmt .Sprintf ("\033 ]8;;%s\033 \\ %s\033 ]8;;\033 \\ " , url , url )
884+ }
885+
886+ func printBuildDetails (w io.Writer , refs map [string ]string , term bool , mode string ) {
887+ if out := buildDetailsOutput (refs , term , mode ); out != "" {
888+ fmt .Fprintf (w , "\n %s\n " , out )
889+ }
890+ }
0 commit comments