mirror of
				https://gitea.com/Lydanne/buildx.git
				synced 2025-11-04 18:13:42 +08:00 
			
		
		
		
	Merge pull request #1841 from crazy-max/desktop
build: display build details link
This commit is contained in:
		@@ -26,6 +26,7 @@ import (
 | 
				
			|||||||
	"github.com/docker/buildx/builder"
 | 
						"github.com/docker/buildx/builder"
 | 
				
			||||||
	"github.com/docker/buildx/driver"
 | 
						"github.com/docker/buildx/driver"
 | 
				
			||||||
	"github.com/docker/buildx/localstate"
 | 
						"github.com/docker/buildx/localstate"
 | 
				
			||||||
 | 
						"github.com/docker/buildx/util/desktop"
 | 
				
			||||||
	"github.com/docker/buildx/util/dockerutil"
 | 
						"github.com/docker/buildx/util/dockerutil"
 | 
				
			||||||
	"github.com/docker/buildx/util/imagetools"
 | 
						"github.com/docker/buildx/util/imagetools"
 | 
				
			||||||
	"github.com/docker/buildx/util/progress"
 | 
						"github.com/docker/buildx/util/progress"
 | 
				
			||||||
@@ -822,6 +823,7 @@ func BuildWithResultHandler(ctx context.Context, nodes []builder.Node, opt map[s
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
			for i, dp := range dps {
 | 
								for i, dp := range dps {
 | 
				
			||||||
				i, dp, so := i, dp, *dp.so
 | 
									i, dp, so := i, dp, *dp.so
 | 
				
			||||||
 | 
									node := nodes[dp.driverIndex]
 | 
				
			||||||
				if multiDriver {
 | 
									if multiDriver {
 | 
				
			||||||
					for i, e := range so.Exports {
 | 
										for i, e := range so.Exports {
 | 
				
			||||||
						switch e.Type {
 | 
											switch e.Type {
 | 
				
			||||||
@@ -940,6 +942,16 @@ func BuildWithResultHandler(ctx context.Context, nodes []builder.Node, opt map[s
 | 
				
			|||||||
					} else {
 | 
										} else {
 | 
				
			||||||
						rr, err = c.Build(ctx, so, "buildx", buildFunc, ch)
 | 
											rr, err = c.Build(ctx, so, "buildx", buildFunc, ch)
 | 
				
			||||||
					}
 | 
										}
 | 
				
			||||||
 | 
										if node.Driver.Features(ctx)[driver.HistoryAPI] && desktop.BuildBackendEnabled() {
 | 
				
			||||||
 | 
											buildRef := fmt.Sprintf("%s/%s/%s", node.Builder, node.Name, so.Ref)
 | 
				
			||||||
 | 
											if err != nil {
 | 
				
			||||||
 | 
												return &desktop.ErrorWithBuildRef{
 | 
				
			||||||
 | 
													Ref: buildRef,
 | 
				
			||||||
 | 
													Err: err,
 | 
				
			||||||
 | 
												}
 | 
				
			||||||
 | 
											}
 | 
				
			||||||
 | 
											progress.WriteBuildRef(w, k, buildRef)
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
					if err != nil {
 | 
										if err != nil {
 | 
				
			||||||
						return err
 | 
											return err
 | 
				
			||||||
					}
 | 
										}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,6 +5,7 @@ import (
 | 
				
			|||||||
	"os"
 | 
						"os"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/docker/buildx/commands"
 | 
						"github.com/docker/buildx/commands"
 | 
				
			||||||
 | 
						"github.com/docker/buildx/util/desktop"
 | 
				
			||||||
	"github.com/docker/buildx/version"
 | 
						"github.com/docker/buildx/version"
 | 
				
			||||||
	"github.com/docker/cli/cli"
 | 
						"github.com/docker/cli/cli"
 | 
				
			||||||
	"github.com/docker/cli/cli-plugins/manager"
 | 
						"github.com/docker/cli/cli-plugins/manager"
 | 
				
			||||||
@@ -86,6 +87,9 @@ func main() {
 | 
				
			|||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		fmt.Fprintf(cmd.Err(), "ERROR: %v\n", err)
 | 
							fmt.Fprintf(cmd.Err(), "ERROR: %v\n", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						if ebr, ok := err.(*desktop.ErrorWithBuildRef); ok {
 | 
				
			||||||
 | 
							ebr.Print(cmd.Err())
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	os.Exit(1)
 | 
						os.Exit(1)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,6 +6,7 @@ import (
 | 
				
			|||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"os"
 | 
						"os"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/containerd/console"
 | 
				
			||||||
	"github.com/containerd/containerd/platforms"
 | 
						"github.com/containerd/containerd/platforms"
 | 
				
			||||||
	"github.com/docker/buildx/bake"
 | 
						"github.com/docker/buildx/bake"
 | 
				
			||||||
	"github.com/docker/buildx/build"
 | 
						"github.com/docker/buildx/build"
 | 
				
			||||||
@@ -13,6 +14,7 @@ import (
 | 
				
			|||||||
	"github.com/docker/buildx/util/buildflags"
 | 
						"github.com/docker/buildx/util/buildflags"
 | 
				
			||||||
	"github.com/docker/buildx/util/cobrautil/completion"
 | 
						"github.com/docker/buildx/util/cobrautil/completion"
 | 
				
			||||||
	"github.com/docker/buildx/util/confutil"
 | 
						"github.com/docker/buildx/util/confutil"
 | 
				
			||||||
 | 
						"github.com/docker/buildx/util/desktop"
 | 
				
			||||||
	"github.com/docker/buildx/util/dockerutil"
 | 
						"github.com/docker/buildx/util/dockerutil"
 | 
				
			||||||
	"github.com/docker/buildx/util/progress"
 | 
						"github.com/docker/buildx/util/progress"
 | 
				
			||||||
	"github.com/docker/buildx/util/tracing"
 | 
						"github.com/docker/buildx/util/tracing"
 | 
				
			||||||
@@ -117,6 +119,11 @@ func runBake(dockerCli command.Cli, targets []string, in bakeOptions, cFlags com
 | 
				
			|||||||
		progressTextDesc = fmt.Sprintf("building with %q instance using %s driver", b.Name, b.Driver)
 | 
							progressTextDesc = fmt.Sprintf("building with %q instance using %s driver", b.Name, b.Driver)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var term bool
 | 
				
			||||||
 | 
						if _, err := console.ConsoleFromFile(os.Stderr); err == nil {
 | 
				
			||||||
 | 
							term = true
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	printer, err := progress.NewPrinter(ctx2, os.Stderr, os.Stderr, cFlags.progress,
 | 
						printer, err := progress.NewPrinter(ctx2, os.Stderr, os.Stderr, cFlags.progress,
 | 
				
			||||||
		progress.WithDesc(progressTextDesc, progressConsoleDesc),
 | 
							progress.WithDesc(progressTextDesc, progressConsoleDesc),
 | 
				
			||||||
	)
 | 
						)
 | 
				
			||||||
@@ -130,6 +137,9 @@ func runBake(dockerCli command.Cli, targets []string, in bakeOptions, cFlags com
 | 
				
			|||||||
			if err == nil {
 | 
								if err == nil {
 | 
				
			||||||
				err = err1
 | 
									err = err1
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
								if err == nil && cFlags.progress != progress.PrinterModeQuiet {
 | 
				
			||||||
 | 
									desktop.PrintBuildDetails(os.Stderr, printer.BuildRefs(), term)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}()
 | 
						}()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -26,6 +26,7 @@ import (
 | 
				
			|||||||
	"github.com/docker/buildx/store"
 | 
						"github.com/docker/buildx/store"
 | 
				
			||||||
	"github.com/docker/buildx/store/storeutil"
 | 
						"github.com/docker/buildx/store/storeutil"
 | 
				
			||||||
	"github.com/docker/buildx/util/buildflags"
 | 
						"github.com/docker/buildx/util/buildflags"
 | 
				
			||||||
 | 
						"github.com/docker/buildx/util/desktop"
 | 
				
			||||||
	"github.com/docker/buildx/util/ioset"
 | 
						"github.com/docker/buildx/util/ioset"
 | 
				
			||||||
	"github.com/docker/buildx/util/progress"
 | 
						"github.com/docker/buildx/util/progress"
 | 
				
			||||||
	"github.com/docker/buildx/util/tracing"
 | 
						"github.com/docker/buildx/util/tracing"
 | 
				
			||||||
@@ -238,6 +239,11 @@ func runBuild(dockerCli command.Cli, options buildOptions) (err error) {
 | 
				
			|||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var term bool
 | 
				
			||||||
 | 
						if _, err := console.ConsoleFromFile(os.Stderr); err == nil {
 | 
				
			||||||
 | 
							term = true
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ctx2, cancel := context.WithCancel(context.TODO())
 | 
						ctx2, cancel := context.WithCancel(context.TODO())
 | 
				
			||||||
	defer cancel()
 | 
						defer cancel()
 | 
				
			||||||
	progressMode, err := options.toProgress()
 | 
						progressMode, err := options.toProgress()
 | 
				
			||||||
@@ -273,7 +279,9 @@ func runBuild(dockerCli command.Cli, options buildOptions) (err error) {
 | 
				
			|||||||
		return retErr
 | 
							return retErr
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if options.quiet {
 | 
						if progressMode != progress.PrinterModeQuiet {
 | 
				
			||||||
 | 
							desktop.PrintBuildDetails(os.Stderr, printer.BuildRefs(), term)
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
		fmt.Println(getImageID(resp.ExporterResponse))
 | 
							fmt.Println(getImageID(resp.ExporterResponse))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if options.imageIDFile != "" {
 | 
						if options.imageIDFile != "" {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -19,6 +19,10 @@ func (w *writer) Write(status *client.SolveStatus) {
 | 
				
			|||||||
	w.ch <- ToControlStatus(status)
 | 
						w.ch <- ToControlStatus(status)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (w *writer) WriteBuildRef(target string, ref string) {
 | 
				
			||||||
 | 
						return
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (w *writer) ValidateLogSource(digest.Digest, interface{}) bool {
 | 
					func (w *writer) ValidateLogSource(digest.Digest, interface{}) bool {
 | 
				
			||||||
	return true
 | 
						return true
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										86
									
								
								util/desktop/desktop.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										86
									
								
								util/desktop/desktop.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,86 @@
 | 
				
			|||||||
 | 
					package desktop
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"bytes"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"io"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
						"path/filepath"
 | 
				
			||||||
 | 
						"sync"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/containerd/console"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var (
 | 
				
			||||||
 | 
						bbEnabledOnce sync.Once
 | 
				
			||||||
 | 
						bbEnabled     bool
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func BuildBackendEnabled() bool {
 | 
				
			||||||
 | 
						bbEnabledOnce.Do(func() {
 | 
				
			||||||
 | 
							home, err := os.UserHomeDir()
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							_, err = os.Stat(filepath.Join(home, ".docker", "desktop-build", ".lastaccess"))
 | 
				
			||||||
 | 
							bbEnabled = err == nil
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						return bbEnabled
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func BuildDetailsOutput(refs map[string]string, term bool) string {
 | 
				
			||||||
 | 
						if len(refs) == 0 {
 | 
				
			||||||
 | 
							return ""
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						refURL := func(ref string) string {
 | 
				
			||||||
 | 
							return fmt.Sprintf("docker-desktop://dashboard/build/%s", ref)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						var out bytes.Buffer
 | 
				
			||||||
 | 
						out.WriteString("View build details: ")
 | 
				
			||||||
 | 
						multiTargets := len(refs) > 1
 | 
				
			||||||
 | 
						for target, ref := range refs {
 | 
				
			||||||
 | 
							if multiTargets {
 | 
				
			||||||
 | 
								out.WriteString(fmt.Sprintf("\n  %s: ", target))
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if term {
 | 
				
			||||||
 | 
								out.WriteString(hyperlink(refURL(ref)))
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								out.WriteString(refURL(ref))
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return out.String()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func PrintBuildDetails(w io.Writer, refs map[string]string, term bool) {
 | 
				
			||||||
 | 
						if out := BuildDetailsOutput(refs, term); out != "" {
 | 
				
			||||||
 | 
							fmt.Fprintf(w, "\n%s\n", out)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func hyperlink(url string) string {
 | 
				
			||||||
 | 
						// create an escape sequence using the OSC 8 format: https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda
 | 
				
			||||||
 | 
						return fmt.Sprintf("\033]8;;%s\033\\%s\033]8;;\033\\", url, url)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type ErrorWithBuildRef struct {
 | 
				
			||||||
 | 
						Ref string
 | 
				
			||||||
 | 
						Err error
 | 
				
			||||||
 | 
						Msg string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (e *ErrorWithBuildRef) Error() string {
 | 
				
			||||||
 | 
						return e.Err.Error()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (e *ErrorWithBuildRef) Unwrap() error {
 | 
				
			||||||
 | 
						return e.Err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (e *ErrorWithBuildRef) Print(w io.Writer) error {
 | 
				
			||||||
 | 
						var term bool
 | 
				
			||||||
 | 
						if _, err := console.ConsoleFromFile(os.Stderr); err == nil {
 | 
				
			||||||
 | 
							term = true
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						fmt.Fprintf(w, "\n%s", BuildDetailsOutput(map[string]string{"default": e.Ref}, term))
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -33,6 +33,11 @@ type Printer struct {
 | 
				
			|||||||
	warnings     []client.VertexWarning
 | 
						warnings     []client.VertexWarning
 | 
				
			||||||
	logMu        sync.Mutex
 | 
						logMu        sync.Mutex
 | 
				
			||||||
	logSourceMap map[digest.Digest]interface{}
 | 
						logSourceMap map[digest.Digest]interface{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// TODO: remove once we can use result context to pass build ref
 | 
				
			||||||
 | 
						//  see https://github.com/docker/buildx/pull/1861
 | 
				
			||||||
 | 
						buildRefsMu sync.Mutex
 | 
				
			||||||
 | 
						buildRefs   map[string]string
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (p *Printer) Wait() error {
 | 
					func (p *Printer) Wait() error {
 | 
				
			||||||
@@ -143,6 +148,19 @@ func NewPrinter(ctx context.Context, w io.Writer, out console.File, mode string,
 | 
				
			|||||||
	return pw, nil
 | 
						return pw, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (p *Printer) WriteBuildRef(target string, ref string) {
 | 
				
			||||||
 | 
						p.buildRefsMu.Lock()
 | 
				
			||||||
 | 
						defer p.buildRefsMu.Unlock()
 | 
				
			||||||
 | 
						if p.buildRefs == nil {
 | 
				
			||||||
 | 
							p.buildRefs = map[string]string{}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						p.buildRefs[target] = ref
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (p *Printer) BuildRefs() map[string]string {
 | 
				
			||||||
 | 
						return p.buildRefs
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type printerOpts struct {
 | 
					type printerOpts struct {
 | 
				
			||||||
	displayOpts []progressui.DisplaySolveStatusOpt
 | 
						displayOpts []progressui.DisplaySolveStatusOpt
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -10,6 +10,7 @@ import (
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
type Writer interface {
 | 
					type Writer interface {
 | 
				
			||||||
	Write(*client.SolveStatus)
 | 
						Write(*client.SolveStatus)
 | 
				
			||||||
 | 
						WriteBuildRef(string, string)
 | 
				
			||||||
	ValidateLogSource(digest.Digest, interface{}) bool
 | 
						ValidateLogSource(digest.Digest, interface{}) bool
 | 
				
			||||||
	ClearLogSource(interface{})
 | 
						ClearLogSource(interface{})
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -41,6 +42,10 @@ func Write(w Writer, name string, f func() error) {
 | 
				
			|||||||
	})
 | 
						})
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func WriteBuildRef(w Writer, target string, ref string) {
 | 
				
			||||||
 | 
						w.WriteBuildRef(target, ref)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func NewChannel(w Writer) (chan *client.SolveStatus, chan struct{}) {
 | 
					func NewChannel(w Writer) (chan *client.SolveStatus, chan struct{}) {
 | 
				
			||||||
	ch := make(chan *client.SolveStatus)
 | 
						ch := make(chan *client.SolveStatus)
 | 
				
			||||||
	done := make(chan struct{})
 | 
						done := make(chan struct{})
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user