mirror of
				https://gitea.com/Lydanne/buildx.git
				synced 2025-11-04 10:03:42 +08:00 
			
		
		
		
	Add buildx history command
These commands allow working with build records of completed and running builds. Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
This commit is contained in:
		
							
								
								
									
										109
									
								
								commands/history/inspect.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										109
									
								
								commands/history/inspect.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,109 @@
 | 
				
			|||||||
 | 
					package history
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"context"
 | 
				
			||||||
 | 
						"log"
 | 
				
			||||||
 | 
						"slices"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/docker/buildx/builder"
 | 
				
			||||||
 | 
						"github.com/docker/buildx/localstate"
 | 
				
			||||||
 | 
						"github.com/docker/buildx/util/cobrautil/completion"
 | 
				
			||||||
 | 
						"github.com/docker/buildx/util/confutil"
 | 
				
			||||||
 | 
						"github.com/docker/cli/cli/command"
 | 
				
			||||||
 | 
						"github.com/pkg/errors"
 | 
				
			||||||
 | 
						"github.com/spf13/cobra"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type inspectOptions struct {
 | 
				
			||||||
 | 
						builder string
 | 
				
			||||||
 | 
						ref     string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func runInspect(ctx context.Context, dockerCli command.Cli, opts inspectOptions) error {
 | 
				
			||||||
 | 
						b, err := builder.New(dockerCli, builder.WithName(opts.builder))
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						nodes, err := b.LoadNodes(ctx)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						for _, node := range nodes {
 | 
				
			||||||
 | 
							if node.Err != nil {
 | 
				
			||||||
 | 
								return node.Err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						recs, err := queryRecords(ctx, opts.ref, nodes)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if len(recs) == 0 {
 | 
				
			||||||
 | 
							if opts.ref == "" {
 | 
				
			||||||
 | 
								return errors.New("no records found")
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return errors.Errorf("no record found for ref %q", opts.ref)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if opts.ref == "" {
 | 
				
			||||||
 | 
							slices.SortFunc(recs, func(a, b historyRecord) int {
 | 
				
			||||||
 | 
								return b.CreatedAt.AsTime().Compare(a.CreatedAt.AsTime())
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						rec := &recs[0]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ls, err := localstate.New(confutil.NewConfig(dockerCli))
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						st, _ := ls.ReadRef(rec.node.Builder, rec.node.Name, rec.Ref)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						log.Printf("rec %+v", rec)
 | 
				
			||||||
 | 
						log.Printf("st %+v", st)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Context
 | 
				
			||||||
 | 
						// Dockerfile
 | 
				
			||||||
 | 
						// Target
 | 
				
			||||||
 | 
						// VCS Repo / Commit
 | 
				
			||||||
 | 
						// Platform
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Started
 | 
				
			||||||
 | 
						// Duration
 | 
				
			||||||
 | 
						// Number of steps
 | 
				
			||||||
 | 
						// Cached steps
 | 
				
			||||||
 | 
						// Status
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// build-args
 | 
				
			||||||
 | 
						// exporters (image)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// commands
 | 
				
			||||||
 | 
						// error
 | 
				
			||||||
 | 
						// materials
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func inspectCmd(dockerCli command.Cli, rootOpts RootOptions) *cobra.Command {
 | 
				
			||||||
 | 
						var options inspectOptions
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						cmd := &cobra.Command{
 | 
				
			||||||
 | 
							Use:   "inspect [OPTIONS] [REF]",
 | 
				
			||||||
 | 
							Short: "Inspect a build",
 | 
				
			||||||
 | 
							Args:  cobra.MaximumNArgs(1),
 | 
				
			||||||
 | 
							RunE: func(cmd *cobra.Command, args []string) error {
 | 
				
			||||||
 | 
								if len(args) > 0 {
 | 
				
			||||||
 | 
									options.ref = args[0]
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								options.builder = *rootOpts.Builder
 | 
				
			||||||
 | 
								return runInspect(cmd.Context(), dockerCli, options)
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							ValidArgsFunction: completion.Disable,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// flags := cmd.Flags()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return cmd
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										124
									
								
								commands/history/logs.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										124
									
								
								commands/history/logs.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,124 @@
 | 
				
			|||||||
 | 
					package history
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"context"
 | 
				
			||||||
 | 
						"io"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
						"slices"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/docker/buildx/builder"
 | 
				
			||||||
 | 
						"github.com/docker/buildx/util/cobrautil/completion"
 | 
				
			||||||
 | 
						"github.com/docker/buildx/util/progress"
 | 
				
			||||||
 | 
						"github.com/docker/cli/cli/command"
 | 
				
			||||||
 | 
						controlapi "github.com/moby/buildkit/api/services/control"
 | 
				
			||||||
 | 
						"github.com/moby/buildkit/client"
 | 
				
			||||||
 | 
						"github.com/moby/buildkit/util/progress/progressui"
 | 
				
			||||||
 | 
						"github.com/pkg/errors"
 | 
				
			||||||
 | 
						"github.com/spf13/cobra"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type logsOptions struct {
 | 
				
			||||||
 | 
						builder  string
 | 
				
			||||||
 | 
						ref      string
 | 
				
			||||||
 | 
						progress string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func runLogs(ctx context.Context, dockerCli command.Cli, opts logsOptions) error {
 | 
				
			||||||
 | 
						b, err := builder.New(dockerCli, builder.WithName(opts.builder))
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						nodes, err := b.LoadNodes(ctx)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						for _, node := range nodes {
 | 
				
			||||||
 | 
							if node.Err != nil {
 | 
				
			||||||
 | 
								return node.Err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						recs, err := queryRecords(ctx, opts.ref, nodes)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if len(recs) == 0 {
 | 
				
			||||||
 | 
							if opts.ref == "" {
 | 
				
			||||||
 | 
								return errors.New("no records found")
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return errors.Errorf("no record found for ref %q", opts.ref)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if opts.ref == "" {
 | 
				
			||||||
 | 
							slices.SortFunc(recs, func(a, b historyRecord) int {
 | 
				
			||||||
 | 
								return b.CreatedAt.AsTime().Compare(a.CreatedAt.AsTime())
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						rec := &recs[0]
 | 
				
			||||||
 | 
						c, err := rec.node.Driver.Client(ctx)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						cl, err := c.ControlClient().Status(ctx, &controlapi.StatusRequest{
 | 
				
			||||||
 | 
							Ref: rec.Ref,
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var mode progressui.DisplayMode = progressui.DisplayMode(opts.progress)
 | 
				
			||||||
 | 
						if mode == progressui.AutoMode {
 | 
				
			||||||
 | 
							mode = progressui.PlainMode
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						printer, err := progress.NewPrinter(context.TODO(), os.Stderr, mode)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					loop0:
 | 
				
			||||||
 | 
						for {
 | 
				
			||||||
 | 
							select {
 | 
				
			||||||
 | 
							case <-ctx.Done():
 | 
				
			||||||
 | 
								cl.CloseSend()
 | 
				
			||||||
 | 
								return context.Cause(ctx)
 | 
				
			||||||
 | 
							default:
 | 
				
			||||||
 | 
								ev, err := cl.Recv()
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									if errors.Is(err, io.EOF) {
 | 
				
			||||||
 | 
										break loop0
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									return err
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								printer.Write(client.NewSolveStatus(ev))
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return printer.Wait()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func logsCmd(dockerCli command.Cli, rootOpts RootOptions) *cobra.Command {
 | 
				
			||||||
 | 
						var options logsOptions
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						cmd := &cobra.Command{
 | 
				
			||||||
 | 
							Use:   "logs [OPTIONS] [REF]",
 | 
				
			||||||
 | 
							Short: "Print the logs of a build",
 | 
				
			||||||
 | 
							Args:  cobra.MaximumNArgs(1),
 | 
				
			||||||
 | 
							RunE: func(cmd *cobra.Command, args []string) error {
 | 
				
			||||||
 | 
								if len(args) > 0 {
 | 
				
			||||||
 | 
									options.ref = args[0]
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								options.builder = *rootOpts.Builder
 | 
				
			||||||
 | 
								return runLogs(cmd.Context(), dockerCli, options)
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							ValidArgsFunction: completion.Disable,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						flags := cmd.Flags()
 | 
				
			||||||
 | 
						flags.StringVar(&options.progress, "progress", "plain", "Set type of progress output (plain, rawjson, tty)")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return cmd
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										234
									
								
								commands/history/ls.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										234
									
								
								commands/history/ls.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,234 @@
 | 
				
			|||||||
 | 
					package history
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"context"
 | 
				
			||||||
 | 
						"encoding/json"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/containerd/console"
 | 
				
			||||||
 | 
						"github.com/docker/buildx/builder"
 | 
				
			||||||
 | 
						"github.com/docker/buildx/localstate"
 | 
				
			||||||
 | 
						"github.com/docker/buildx/util/cobrautil/completion"
 | 
				
			||||||
 | 
						"github.com/docker/buildx/util/confutil"
 | 
				
			||||||
 | 
						"github.com/docker/buildx/util/desktop"
 | 
				
			||||||
 | 
						"github.com/docker/cli/cli"
 | 
				
			||||||
 | 
						"github.com/docker/cli/cli/command"
 | 
				
			||||||
 | 
						"github.com/docker/cli/cli/command/formatter"
 | 
				
			||||||
 | 
						"github.com/docker/go-units"
 | 
				
			||||||
 | 
						"github.com/spf13/cobra"
 | 
				
			||||||
 | 
						"golang.org/x/exp/slices"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const (
 | 
				
			||||||
 | 
						lsHeaderBuildID  = "BUILD ID"
 | 
				
			||||||
 | 
						lsHeaderName     = "NAME"
 | 
				
			||||||
 | 
						lsHeaderStatus   = "STATUS"
 | 
				
			||||||
 | 
						lsHeaderCreated  = "CREATED AT"
 | 
				
			||||||
 | 
						lsHeaderDuration = "DURATION"
 | 
				
			||||||
 | 
						lsHeaderLink     = ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						lsDefaultTableFormat = "table {{.Ref}}\t{{.Name}}\t{{.Status}}\t{{.CreatedAt}}\t{{.Duration}}\t{{.Link}}"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						headerKeyTimestamp = "buildkit-current-timestamp"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type lsOptions struct {
 | 
				
			||||||
 | 
						builder string
 | 
				
			||||||
 | 
						format  string
 | 
				
			||||||
 | 
						noTrunc bool
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func runLs(ctx context.Context, dockerCli command.Cli, opts lsOptions) error {
 | 
				
			||||||
 | 
						b, err := builder.New(dockerCli, builder.WithName(opts.builder))
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						nodes, err := b.LoadNodes(ctx)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						for _, node := range nodes {
 | 
				
			||||||
 | 
							if node.Err != nil {
 | 
				
			||||||
 | 
								return node.Err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						out, err := queryRecords(ctx, "", nodes)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ls, err := localstate.New(confutil.NewConfig(dockerCli))
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for i, rec := range out {
 | 
				
			||||||
 | 
							st, _ := ls.ReadRef(rec.node.Builder, rec.node.Name, rec.Ref)
 | 
				
			||||||
 | 
							rec.name = buildName(rec.FrontendAttrs, st)
 | 
				
			||||||
 | 
							out[i] = rec
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return lsPrint(dockerCli, out, opts)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func lsCmd(dockerCli command.Cli, rootOpts RootOptions) *cobra.Command {
 | 
				
			||||||
 | 
						var options lsOptions
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						cmd := &cobra.Command{
 | 
				
			||||||
 | 
							Use:   "ls",
 | 
				
			||||||
 | 
							Short: "List build records",
 | 
				
			||||||
 | 
							Args:  cli.NoArgs,
 | 
				
			||||||
 | 
							RunE: func(cmd *cobra.Command, args []string) error {
 | 
				
			||||||
 | 
								options.builder = *rootOpts.Builder
 | 
				
			||||||
 | 
								return runLs(cmd.Context(), dockerCli, options)
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							ValidArgsFunction: completion.Disable,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						flags := cmd.Flags()
 | 
				
			||||||
 | 
						flags.StringVar(&options.format, "format", formatter.TableFormatKey, "Format the output")
 | 
				
			||||||
 | 
						flags.BoolVar(&options.noTrunc, "no-trunc", false, "Don't truncate output")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return cmd
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func lsPrint(dockerCli command.Cli, records []historyRecord, in lsOptions) error {
 | 
				
			||||||
 | 
						if in.format == formatter.TableFormatKey {
 | 
				
			||||||
 | 
							in.format = lsDefaultTableFormat
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ctx := formatter.Context{
 | 
				
			||||||
 | 
							Output: dockerCli.Out(),
 | 
				
			||||||
 | 
							Format: formatter.Format(in.format),
 | 
				
			||||||
 | 
							Trunc:  !in.noTrunc,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						slices.SortFunc(records, func(a, b historyRecord) int {
 | 
				
			||||||
 | 
							if a.CompletedAt == nil && b.CompletedAt != nil {
 | 
				
			||||||
 | 
								return -1
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if a.CompletedAt != nil && b.CompletedAt == nil {
 | 
				
			||||||
 | 
								return 1
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return b.CreatedAt.AsTime().Compare(a.CreatedAt.AsTime())
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var term bool
 | 
				
			||||||
 | 
						if _, err := console.ConsoleFromFile(os.Stdout); err == nil {
 | 
				
			||||||
 | 
							term = true
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						render := func(format func(subContext formatter.SubContext) error) error {
 | 
				
			||||||
 | 
							for _, r := range records {
 | 
				
			||||||
 | 
								if err := format(&lsContext{
 | 
				
			||||||
 | 
									format: formatter.Format(in.format),
 | 
				
			||||||
 | 
									isTerm: term,
 | 
				
			||||||
 | 
									trunc:  !in.noTrunc,
 | 
				
			||||||
 | 
									record: &r,
 | 
				
			||||||
 | 
								}); err != nil {
 | 
				
			||||||
 | 
									return err
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						lsCtx := lsContext{
 | 
				
			||||||
 | 
							isTerm: term,
 | 
				
			||||||
 | 
							trunc:  !in.noTrunc,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						lsCtx.Header = formatter.SubHeaderContext{
 | 
				
			||||||
 | 
							"Ref":       lsHeaderBuildID,
 | 
				
			||||||
 | 
							"Name":      lsHeaderName,
 | 
				
			||||||
 | 
							"Status":    lsHeaderStatus,
 | 
				
			||||||
 | 
							"CreatedAt": lsHeaderCreated,
 | 
				
			||||||
 | 
							"Duration":  lsHeaderDuration,
 | 
				
			||||||
 | 
							"Link":      lsHeaderLink,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return ctx.Write(&lsCtx, render)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type lsContext struct {
 | 
				
			||||||
 | 
						formatter.HeaderContext
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						isTerm bool
 | 
				
			||||||
 | 
						trunc  bool
 | 
				
			||||||
 | 
						format formatter.Format
 | 
				
			||||||
 | 
						record *historyRecord
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (c *lsContext) MarshalJSON() ([]byte, error) {
 | 
				
			||||||
 | 
						m := map[string]interface{}{
 | 
				
			||||||
 | 
							"ref":             c.FullRef(),
 | 
				
			||||||
 | 
							"name":            c.Name(),
 | 
				
			||||||
 | 
							"status":          c.Status(),
 | 
				
			||||||
 | 
							"created_at":      c.record.CreatedAt.AsTime().Format(time.RFC3339Nano),
 | 
				
			||||||
 | 
							"total_steps":     c.record.NumTotalSteps,
 | 
				
			||||||
 | 
							"completed_steps": c.record.NumCompletedSteps,
 | 
				
			||||||
 | 
							"cached_steps":    c.record.NumCachedSteps,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if c.record.CompletedAt != nil {
 | 
				
			||||||
 | 
							m["completed_at"] = c.record.CompletedAt.AsTime().Format(time.RFC3339Nano)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return json.Marshal(m)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (c *lsContext) Ref() string {
 | 
				
			||||||
 | 
						return c.record.Ref
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (c *lsContext) FullRef() string {
 | 
				
			||||||
 | 
						return fmt.Sprintf("%s/%s/%s", c.record.node.Builder, c.record.node.Name, c.record.Ref)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (c *lsContext) Name() string {
 | 
				
			||||||
 | 
						name := c.record.name
 | 
				
			||||||
 | 
						if c.trunc && c.format.IsTable() {
 | 
				
			||||||
 | 
							return trimBeginning(name, 36)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return name
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (c *lsContext) Status() string {
 | 
				
			||||||
 | 
						if c.record.CompletedAt != nil {
 | 
				
			||||||
 | 
							if c.record.Error != nil {
 | 
				
			||||||
 | 
								return "Error"
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return "Completed"
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return "Running"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (c *lsContext) CreatedAt() string {
 | 
				
			||||||
 | 
						return units.HumanDuration(time.Since(c.record.CreatedAt.AsTime())) + " ago"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (c *lsContext) Duration() string {
 | 
				
			||||||
 | 
						lastTime := c.record.currentTimestamp
 | 
				
			||||||
 | 
						if c.record.CompletedAt != nil {
 | 
				
			||||||
 | 
							tm := c.record.CompletedAt.AsTime()
 | 
				
			||||||
 | 
							lastTime = &tm
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if lastTime == nil {
 | 
				
			||||||
 | 
							return ""
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						v := formatDuration(lastTime.Sub(c.record.CreatedAt.AsTime()))
 | 
				
			||||||
 | 
						if c.record.CompletedAt == nil {
 | 
				
			||||||
 | 
							v += "+"
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return v
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (c *lsContext) Link() string {
 | 
				
			||||||
 | 
						url := desktop.BuildURL(c.FullRef())
 | 
				
			||||||
 | 
						if c.format.IsTable() {
 | 
				
			||||||
 | 
							if c.isTerm {
 | 
				
			||||||
 | 
								return desktop.ANSIHyperlink(url, "Open")
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return ""
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return url
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										80
									
								
								commands/history/open.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										80
									
								
								commands/history/open.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,80 @@
 | 
				
			|||||||
 | 
					package history
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"context"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"slices"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/docker/buildx/builder"
 | 
				
			||||||
 | 
						"github.com/docker/buildx/util/cobrautil/completion"
 | 
				
			||||||
 | 
						"github.com/docker/buildx/util/desktop"
 | 
				
			||||||
 | 
						"github.com/docker/cli/cli/command"
 | 
				
			||||||
 | 
						"github.com/pkg/browser"
 | 
				
			||||||
 | 
						"github.com/pkg/errors"
 | 
				
			||||||
 | 
						"github.com/spf13/cobra"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type openOptions struct {
 | 
				
			||||||
 | 
						builder string
 | 
				
			||||||
 | 
						ref     string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func runOpen(ctx context.Context, dockerCli command.Cli, opts openOptions) error {
 | 
				
			||||||
 | 
						b, err := builder.New(dockerCli, builder.WithName(opts.builder))
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						nodes, err := b.LoadNodes(ctx)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						for _, node := range nodes {
 | 
				
			||||||
 | 
							if node.Err != nil {
 | 
				
			||||||
 | 
								return node.Err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						recs, err := queryRecords(ctx, opts.ref, nodes)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if len(recs) == 0 {
 | 
				
			||||||
 | 
							if opts.ref == "" {
 | 
				
			||||||
 | 
								return errors.New("no records found")
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return errors.Errorf("no record found for ref %q", opts.ref)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if opts.ref == "" {
 | 
				
			||||||
 | 
							slices.SortFunc(recs, func(a, b historyRecord) int {
 | 
				
			||||||
 | 
								return b.CreatedAt.AsTime().Compare(a.CreatedAt.AsTime())
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						rec := &recs[0]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						url := desktop.BuildURL(fmt.Sprintf("%s/%s/%s", rec.node.Builder, rec.node.Name, rec.Ref))
 | 
				
			||||||
 | 
						return browser.OpenURL(url)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func openCmd(dockerCli command.Cli, rootOpts RootOptions) *cobra.Command {
 | 
				
			||||||
 | 
						var options openOptions
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						cmd := &cobra.Command{
 | 
				
			||||||
 | 
							Use:   "open [OPTIONS] [REF]",
 | 
				
			||||||
 | 
							Short: "Open a build in Docker Desktop",
 | 
				
			||||||
 | 
							Args:  cobra.MaximumNArgs(1),
 | 
				
			||||||
 | 
							RunE: func(cmd *cobra.Command, args []string) error {
 | 
				
			||||||
 | 
								if len(args) > 0 {
 | 
				
			||||||
 | 
									options.ref = args[0]
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								options.builder = *rootOpts.Builder
 | 
				
			||||||
 | 
								return runOpen(cmd.Context(), dockerCli, options)
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							ValidArgsFunction: completion.Disable,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return cmd
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										151
									
								
								commands/history/rm.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										151
									
								
								commands/history/rm.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,151 @@
 | 
				
			|||||||
 | 
					package history
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"context"
 | 
				
			||||||
 | 
						"io"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/docker/buildx/builder"
 | 
				
			||||||
 | 
						"github.com/docker/buildx/util/cobrautil/completion"
 | 
				
			||||||
 | 
						"github.com/docker/cli/cli/command"
 | 
				
			||||||
 | 
						"github.com/hashicorp/go-multierror"
 | 
				
			||||||
 | 
						controlapi "github.com/moby/buildkit/api/services/control"
 | 
				
			||||||
 | 
						"github.com/pkg/errors"
 | 
				
			||||||
 | 
						"github.com/spf13/cobra"
 | 
				
			||||||
 | 
						"golang.org/x/sync/errgroup"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type rmOptions struct {
 | 
				
			||||||
 | 
						builder string
 | 
				
			||||||
 | 
						refs    []string
 | 
				
			||||||
 | 
						all     bool
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func runRm(ctx context.Context, dockerCli command.Cli, opts rmOptions) error {
 | 
				
			||||||
 | 
						b, err := builder.New(dockerCli, builder.WithName(opts.builder))
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						nodes, err := b.LoadNodes(ctx)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						for _, node := range nodes {
 | 
				
			||||||
 | 
							if node.Err != nil {
 | 
				
			||||||
 | 
								return node.Err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						errs := make([][]error, len(opts.refs))
 | 
				
			||||||
 | 
						for i := range errs {
 | 
				
			||||||
 | 
							errs[i] = make([]error, len(nodes))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						eg, ctx := errgroup.WithContext(ctx)
 | 
				
			||||||
 | 
						for i, node := range nodes {
 | 
				
			||||||
 | 
							node := node
 | 
				
			||||||
 | 
							eg.Go(func() error {
 | 
				
			||||||
 | 
								if node.Driver == nil {
 | 
				
			||||||
 | 
									return nil
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								c, err := node.Driver.Client(ctx)
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									return err
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								refs := opts.refs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if opts.all {
 | 
				
			||||||
 | 
									serv, err := c.ControlClient().ListenBuildHistory(ctx, &controlapi.BuildHistoryRequest{
 | 
				
			||||||
 | 
										EarlyExit: true,
 | 
				
			||||||
 | 
									})
 | 
				
			||||||
 | 
									if err != nil {
 | 
				
			||||||
 | 
										return err
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									defer serv.CloseSend()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									for {
 | 
				
			||||||
 | 
										resp, err := serv.Recv()
 | 
				
			||||||
 | 
										if err != nil {
 | 
				
			||||||
 | 
											if errors.Is(err, io.EOF) {
 | 
				
			||||||
 | 
												break
 | 
				
			||||||
 | 
											}
 | 
				
			||||||
 | 
											return err
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
										if resp.Type == controlapi.BuildHistoryEventType_COMPLETE {
 | 
				
			||||||
 | 
											refs = append(refs, resp.Record.Ref)
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								for j, ref := range refs {
 | 
				
			||||||
 | 
									_, err = c.ControlClient().UpdateBuildHistory(ctx, &controlapi.UpdateBuildHistoryRequest{
 | 
				
			||||||
 | 
										Ref:    ref,
 | 
				
			||||||
 | 
										Delete: true,
 | 
				
			||||||
 | 
									})
 | 
				
			||||||
 | 
									if opts.all {
 | 
				
			||||||
 | 
										if err != nil {
 | 
				
			||||||
 | 
											return err
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									} else {
 | 
				
			||||||
 | 
										errs[j][i] = err
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								return nil
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err := eg.Wait(); err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var out []error
 | 
				
			||||||
 | 
					loop0:
 | 
				
			||||||
 | 
						for _, nodeErrs := range errs {
 | 
				
			||||||
 | 
							var nodeErr error
 | 
				
			||||||
 | 
							for _, err1 := range nodeErrs {
 | 
				
			||||||
 | 
								if err1 == nil {
 | 
				
			||||||
 | 
									continue loop0
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if nodeErr == nil {
 | 
				
			||||||
 | 
									nodeErr = err1
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									nodeErr = multierror.Append(nodeErr, err1)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							out = append(out, nodeErr)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if len(out) == 0 {
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if len(out) == 1 {
 | 
				
			||||||
 | 
							return out[0]
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return multierror.Append(out[0], out[1:]...)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func rmCmd(dockerCli command.Cli, rootOpts RootOptions) *cobra.Command {
 | 
				
			||||||
 | 
						var options rmOptions
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						cmd := &cobra.Command{
 | 
				
			||||||
 | 
							Use:   "rm [OPTIONS] [REF...]",
 | 
				
			||||||
 | 
							Short: "Remove build records",
 | 
				
			||||||
 | 
							RunE: func(cmd *cobra.Command, args []string) error {
 | 
				
			||||||
 | 
								if len(args) == 0 && !options.all {
 | 
				
			||||||
 | 
									return errors.New("rm requires at least one argument")
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if len(args) > 0 && options.all {
 | 
				
			||||||
 | 
									return errors.New("rm requires either --all or at least one argument")
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								options.refs = args
 | 
				
			||||||
 | 
								options.builder = *rootOpts.Builder
 | 
				
			||||||
 | 
								return runRm(cmd.Context(), dockerCli, options)
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							ValidArgsFunction: completion.Disable,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						flags := cmd.Flags()
 | 
				
			||||||
 | 
						flags.BoolVar(&options.all, "all", false, "Remove all build records")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return cmd
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										30
									
								
								commands/history/root.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								commands/history/root.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,30 @@
 | 
				
			|||||||
 | 
					package history
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"github.com/docker/buildx/util/cobrautil/completion"
 | 
				
			||||||
 | 
						"github.com/docker/cli/cli/command"
 | 
				
			||||||
 | 
						"github.com/spf13/cobra"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type RootOptions struct {
 | 
				
			||||||
 | 
						Builder *string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func RootCmd(rootcmd *cobra.Command, dockerCli command.Cli, opts RootOptions) *cobra.Command {
 | 
				
			||||||
 | 
						cmd := &cobra.Command{
 | 
				
			||||||
 | 
							Use:               "history",
 | 
				
			||||||
 | 
							Short:             "Commands to work on build records",
 | 
				
			||||||
 | 
							ValidArgsFunction: completion.Disable,
 | 
				
			||||||
 | 
							RunE:              rootcmd.RunE,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						cmd.AddCommand(
 | 
				
			||||||
 | 
							lsCmd(dockerCli, opts),
 | 
				
			||||||
 | 
							rmCmd(dockerCli, opts),
 | 
				
			||||||
 | 
							logsCmd(dockerCli, opts),
 | 
				
			||||||
 | 
							inspectCmd(dockerCli, opts),
 | 
				
			||||||
 | 
							openCmd(dockerCli, opts),
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return cmd
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										180
									
								
								commands/history/utils.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										180
									
								
								commands/history/utils.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,180 @@
 | 
				
			|||||||
 | 
					package history
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"context"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"io"
 | 
				
			||||||
 | 
						"path/filepath"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
						"sync"
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/docker/buildx/build"
 | 
				
			||||||
 | 
						"github.com/docker/buildx/builder"
 | 
				
			||||||
 | 
						"github.com/docker/buildx/localstate"
 | 
				
			||||||
 | 
						controlapi "github.com/moby/buildkit/api/services/control"
 | 
				
			||||||
 | 
						"github.com/pkg/errors"
 | 
				
			||||||
 | 
						"golang.org/x/sync/errgroup"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func buildName(fattrs map[string]string, ls *localstate.State) string {
 | 
				
			||||||
 | 
						var res string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var target, contextPath, dockerfilePath, vcsSource string
 | 
				
			||||||
 | 
						if v, ok := fattrs["target"]; ok {
 | 
				
			||||||
 | 
							target = v
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if v, ok := fattrs["context"]; ok {
 | 
				
			||||||
 | 
							contextPath = filepath.ToSlash(v)
 | 
				
			||||||
 | 
						} else if v, ok := fattrs["vcs:localdir:context"]; ok && v != "." {
 | 
				
			||||||
 | 
							contextPath = filepath.ToSlash(v)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if v, ok := fattrs["vcs:source"]; ok {
 | 
				
			||||||
 | 
							vcsSource = v
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if v, ok := fattrs["filename"]; ok && v != "Dockerfile" {
 | 
				
			||||||
 | 
							dockerfilePath = filepath.ToSlash(v)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if v, ok := fattrs["vcs:localdir:dockerfile"]; ok && v != "." {
 | 
				
			||||||
 | 
							dockerfilePath = filepath.ToSlash(filepath.Join(v, dockerfilePath))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var localPath string
 | 
				
			||||||
 | 
						if ls != nil && !build.IsRemoteURL(ls.LocalPath) {
 | 
				
			||||||
 | 
							if ls.LocalPath != "" && ls.LocalPath != "-" {
 | 
				
			||||||
 | 
								localPath = filepath.ToSlash(ls.LocalPath)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if ls.DockerfilePath != "" && ls.DockerfilePath != "-" && ls.DockerfilePath != "Dockerfile" {
 | 
				
			||||||
 | 
								dockerfilePath = filepath.ToSlash(ls.DockerfilePath)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// remove default dockerfile name
 | 
				
			||||||
 | 
						const defaultFilename = "/Dockerfile"
 | 
				
			||||||
 | 
						hasDefaultFileName := strings.HasSuffix(dockerfilePath, defaultFilename) || dockerfilePath == ""
 | 
				
			||||||
 | 
						dockerfilePath = strings.TrimSuffix(dockerfilePath, defaultFilename)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// dockerfile is a subpath of context
 | 
				
			||||||
 | 
						if strings.HasPrefix(dockerfilePath, localPath) && len(dockerfilePath) > len(localPath) {
 | 
				
			||||||
 | 
							res = dockerfilePath[strings.LastIndex(localPath, "/")+1:]
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							// Otherwise, use basename
 | 
				
			||||||
 | 
							bpath := localPath
 | 
				
			||||||
 | 
							if len(dockerfilePath) > 0 {
 | 
				
			||||||
 | 
								bpath = dockerfilePath
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if len(bpath) > 0 {
 | 
				
			||||||
 | 
								lidx := strings.LastIndex(bpath, "/")
 | 
				
			||||||
 | 
								res = bpath[lidx+1:]
 | 
				
			||||||
 | 
								if !hasDefaultFileName {
 | 
				
			||||||
 | 
									if lidx != -1 {
 | 
				
			||||||
 | 
										res = filepath.ToSlash(filepath.Join(filepath.Base(bpath[:lidx]), res))
 | 
				
			||||||
 | 
									} else {
 | 
				
			||||||
 | 
										res = filepath.ToSlash(filepath.Join(filepath.Base(bpath), res))
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if len(contextPath) > 0 {
 | 
				
			||||||
 | 
							res = contextPath
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if len(target) > 0 {
 | 
				
			||||||
 | 
							if len(res) > 0 {
 | 
				
			||||||
 | 
								res = res + " (" + target + ")"
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								res = target
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if res == "" && vcsSource != "" {
 | 
				
			||||||
 | 
							return vcsSource
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return res
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func trimBeginning(s string, n int) string {
 | 
				
			||||||
 | 
						if len(s) <= n {
 | 
				
			||||||
 | 
							return s
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return ".." + s[len(s)-n+2:]
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type historyRecord struct {
 | 
				
			||||||
 | 
						*controlapi.BuildHistoryRecord
 | 
				
			||||||
 | 
						currentTimestamp *time.Time
 | 
				
			||||||
 | 
						node             *builder.Node
 | 
				
			||||||
 | 
						name             string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func queryRecords(ctx context.Context, ref string, nodes []builder.Node) ([]historyRecord, error) {
 | 
				
			||||||
 | 
						var mu sync.Mutex
 | 
				
			||||||
 | 
						var out []historyRecord
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						eg, ctx := errgroup.WithContext(ctx)
 | 
				
			||||||
 | 
						for _, node := range nodes {
 | 
				
			||||||
 | 
							node := node
 | 
				
			||||||
 | 
							eg.Go(func() error {
 | 
				
			||||||
 | 
								if node.Driver == nil {
 | 
				
			||||||
 | 
									return nil
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								var records []historyRecord
 | 
				
			||||||
 | 
								c, err := node.Driver.Client(ctx)
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									return err
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								serv, err := c.ControlClient().ListenBuildHistory(ctx, &controlapi.BuildHistoryRequest{
 | 
				
			||||||
 | 
									EarlyExit: true,
 | 
				
			||||||
 | 
									Ref:       ref,
 | 
				
			||||||
 | 
								})
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									return err
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								md, err := serv.Header()
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									return err
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								var ts *time.Time
 | 
				
			||||||
 | 
								if v, ok := md[headerKeyTimestamp]; ok {
 | 
				
			||||||
 | 
									t, err := time.Parse(time.RFC3339Nano, v[0])
 | 
				
			||||||
 | 
									if err != nil {
 | 
				
			||||||
 | 
										return err
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									ts = &t
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								defer serv.CloseSend()
 | 
				
			||||||
 | 
								for {
 | 
				
			||||||
 | 
									he, err := serv.Recv()
 | 
				
			||||||
 | 
									if err != nil {
 | 
				
			||||||
 | 
										if errors.Is(err, io.EOF) {
 | 
				
			||||||
 | 
											break
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
										return err
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									if he.Type == controlapi.BuildHistoryEventType_DELETED || he.Record == nil {
 | 
				
			||||||
 | 
										continue
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									records = append(records, historyRecord{
 | 
				
			||||||
 | 
										BuildHistoryRecord: he.Record,
 | 
				
			||||||
 | 
										currentTimestamp:   ts,
 | 
				
			||||||
 | 
										node:               &node,
 | 
				
			||||||
 | 
									})
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								mu.Lock()
 | 
				
			||||||
 | 
								out = append(out, records...)
 | 
				
			||||||
 | 
								mu.Unlock()
 | 
				
			||||||
 | 
								return nil
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err := eg.Wait(); err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return out, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func formatDuration(d time.Duration) string {
 | 
				
			||||||
 | 
						if d < time.Minute {
 | 
				
			||||||
 | 
							return fmt.Sprintf("%.1fs", d.Seconds())
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return fmt.Sprintf("%dm %2ds", int(d.Minutes()), int(d.Seconds())%60)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -5,6 +5,7 @@ import (
 | 
				
			|||||||
	"os"
 | 
						"os"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	debugcmd "github.com/docker/buildx/commands/debug"
 | 
						debugcmd "github.com/docker/buildx/commands/debug"
 | 
				
			||||||
 | 
						historycmd "github.com/docker/buildx/commands/history"
 | 
				
			||||||
	imagetoolscmd "github.com/docker/buildx/commands/imagetools"
 | 
						imagetoolscmd "github.com/docker/buildx/commands/imagetools"
 | 
				
			||||||
	"github.com/docker/buildx/controller/remote"
 | 
						"github.com/docker/buildx/controller/remote"
 | 
				
			||||||
	"github.com/docker/buildx/util/cobrautil/completion"
 | 
						"github.com/docker/buildx/util/cobrautil/completion"
 | 
				
			||||||
@@ -106,6 +107,7 @@ func addCommands(cmd *cobra.Command, opts *rootOptions, dockerCli command.Cli) {
 | 
				
			|||||||
		pruneCmd(dockerCli, opts),
 | 
							pruneCmd(dockerCli, opts),
 | 
				
			||||||
		duCmd(dockerCli, opts),
 | 
							duCmd(dockerCli, opts),
 | 
				
			||||||
		imagetoolscmd.RootCmd(cmd, dockerCli, imagetoolscmd.RootOptions{Builder: &opts.builder}),
 | 
							imagetoolscmd.RootCmd(cmd, dockerCli, imagetoolscmd.RootOptions{Builder: &opts.builder}),
 | 
				
			||||||
 | 
							historycmd.RootCmd(cmd, dockerCli, historycmd.RootOptions{Builder: &opts.builder}),
 | 
				
			||||||
	)
 | 
						)
 | 
				
			||||||
	if confutil.IsExperimental() {
 | 
						if confutil.IsExperimental() {
 | 
				
			||||||
		cmd.AddCommand(debugcmd.RootCmd(dockerCli,
 | 
							cmd.AddCommand(debugcmd.RootCmd(dockerCli,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -17,6 +17,7 @@ Extended build capabilities with BuildKit
 | 
				
			|||||||
| [`debug`](buildx_debug.md)           | Start debugger (EXPERIMENTAL)                   |
 | 
					| [`debug`](buildx_debug.md)           | Start debugger (EXPERIMENTAL)                   |
 | 
				
			||||||
| [`dial-stdio`](buildx_dial-stdio.md) | Proxy current stdio streams to builder instance |
 | 
					| [`dial-stdio`](buildx_dial-stdio.md) | Proxy current stdio streams to builder instance |
 | 
				
			||||||
| [`du`](buildx_du.md)                 | Disk usage                                      |
 | 
					| [`du`](buildx_du.md)                 | Disk usage                                      |
 | 
				
			||||||
 | 
					| [`history`](buildx_history.md)       | Commands to work on build records               |
 | 
				
			||||||
| [`imagetools`](buildx_imagetools.md) | Commands to work on images in registry          |
 | 
					| [`imagetools`](buildx_imagetools.md) | Commands to work on images in registry          |
 | 
				
			||||||
| [`inspect`](buildx_inspect.md)       | Inspect current builder instance                |
 | 
					| [`inspect`](buildx_inspect.md)       | Inspect current builder instance                |
 | 
				
			||||||
| [`ls`](buildx_ls.md)                 | List builder instances                          |
 | 
					| [`ls`](buildx_ls.md)                 | List builder instances                          |
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										26
									
								
								docs/reference/buildx_history.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								docs/reference/buildx_history.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,26 @@
 | 
				
			|||||||
 | 
					# docker buildx history
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<!---MARKER_GEN_START-->
 | 
				
			||||||
 | 
					Commands to work on build records
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Subcommands
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					| Name                                   | Description                    |
 | 
				
			||||||
 | 
					|:---------------------------------------|:-------------------------------|
 | 
				
			||||||
 | 
					| [`inspect`](buildx_history_inspect.md) | Inspect a build                |
 | 
				
			||||||
 | 
					| [`logs`](buildx_history_logs.md)       | Print the logs of a build      |
 | 
				
			||||||
 | 
					| [`ls`](buildx_history_ls.md)           | List build records             |
 | 
				
			||||||
 | 
					| [`open`](buildx_history_open.md)       | Open a build in Docker Desktop |
 | 
				
			||||||
 | 
					| [`rm`](buildx_history_rm.md)           | Remove build records           |
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Options
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					| Name            | Type     | Default | Description                              |
 | 
				
			||||||
 | 
					|:----------------|:---------|:--------|:-----------------------------------------|
 | 
				
			||||||
 | 
					| `--builder`     | `string` |         | Override the configured builder instance |
 | 
				
			||||||
 | 
					| `-D`, `--debug` | `bool`   |         | Enable debug logging                     |
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<!---MARKER_GEN_END-->
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										15
									
								
								docs/reference/buildx_history_inspect.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								docs/reference/buildx_history_inspect.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,15 @@
 | 
				
			|||||||
 | 
					# docker buildx history inspect
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<!---MARKER_GEN_START-->
 | 
				
			||||||
 | 
					Inspect a build
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Options
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					| Name            | Type     | Default | Description                              |
 | 
				
			||||||
 | 
					|:----------------|:---------|:--------|:-----------------------------------------|
 | 
				
			||||||
 | 
					| `--builder`     | `string` |         | Override the configured builder instance |
 | 
				
			||||||
 | 
					| `-D`, `--debug` | `bool`   |         | Enable debug logging                     |
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<!---MARKER_GEN_END-->
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										16
									
								
								docs/reference/buildx_history_logs.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								docs/reference/buildx_history_logs.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,16 @@
 | 
				
			|||||||
 | 
					# docker buildx history logs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<!---MARKER_GEN_START-->
 | 
				
			||||||
 | 
					Print the logs of a build
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Options
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					| Name            | Type     | Default | Description                                       |
 | 
				
			||||||
 | 
					|:----------------|:---------|:--------|:--------------------------------------------------|
 | 
				
			||||||
 | 
					| `--builder`     | `string` |         | Override the configured builder instance          |
 | 
				
			||||||
 | 
					| `-D`, `--debug` | `bool`   |         | Enable debug logging                              |
 | 
				
			||||||
 | 
					| `--progress`    | `string` | `plain` | Set type of progress output (plain, rawjson, tty) |
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<!---MARKER_GEN_END-->
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										17
									
								
								docs/reference/buildx_history_ls.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								docs/reference/buildx_history_ls.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,17 @@
 | 
				
			|||||||
 | 
					# docker buildx history ls
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<!---MARKER_GEN_START-->
 | 
				
			||||||
 | 
					List build records
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Options
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					| Name            | Type     | Default | Description                              |
 | 
				
			||||||
 | 
					|:----------------|:---------|:--------|:-----------------------------------------|
 | 
				
			||||||
 | 
					| `--builder`     | `string` |         | Override the configured builder instance |
 | 
				
			||||||
 | 
					| `-D`, `--debug` | `bool`   |         | Enable debug logging                     |
 | 
				
			||||||
 | 
					| `--format`      | `string` | `table` | Format the output                        |
 | 
				
			||||||
 | 
					| `--no-trunc`    | `bool`   |         | Don't truncate output                    |
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<!---MARKER_GEN_END-->
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										15
									
								
								docs/reference/buildx_history_open.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								docs/reference/buildx_history_open.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,15 @@
 | 
				
			|||||||
 | 
					# docker buildx history open
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<!---MARKER_GEN_START-->
 | 
				
			||||||
 | 
					Open a build in Docker Desktop
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Options
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					| Name            | Type     | Default | Description                              |
 | 
				
			||||||
 | 
					|:----------------|:---------|:--------|:-----------------------------------------|
 | 
				
			||||||
 | 
					| `--builder`     | `string` |         | Override the configured builder instance |
 | 
				
			||||||
 | 
					| `-D`, `--debug` | `bool`   |         | Enable debug logging                     |
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<!---MARKER_GEN_END-->
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										16
									
								
								docs/reference/buildx_history_rm.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								docs/reference/buildx_history_rm.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,16 @@
 | 
				
			|||||||
 | 
					# docker buildx history rm
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<!---MARKER_GEN_START-->
 | 
				
			||||||
 | 
					Remove build records
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Options
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					| Name            | Type     | Default | Description                              |
 | 
				
			||||||
 | 
					|:----------------|:---------|:--------|:-----------------------------------------|
 | 
				
			||||||
 | 
					| `--all`         | `bool`   |         | Remove all build records                 |
 | 
				
			||||||
 | 
					| `--builder`     | `string` |         | Override the configured builder instance |
 | 
				
			||||||
 | 
					| `-D`, `--debug` | `bool`   |         | Enable debug logging                     |
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<!---MARKER_GEN_END-->
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										5
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										5
									
								
								go.mod
									
									
									
									
									
								
							@@ -25,6 +25,7 @@ require (
 | 
				
			|||||||
	github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
 | 
						github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
 | 
				
			||||||
	github.com/google/uuid v1.6.0
 | 
						github.com/google/uuid v1.6.0
 | 
				
			||||||
	github.com/hashicorp/go-cty-funcs v0.0.0-20241120183456-c51673e0b3dd
 | 
						github.com/hashicorp/go-cty-funcs v0.0.0-20241120183456-c51673e0b3dd
 | 
				
			||||||
 | 
						github.com/hashicorp/go-multierror v1.1.1
 | 
				
			||||||
	github.com/hashicorp/hcl/v2 v2.23.0
 | 
						github.com/hashicorp/hcl/v2 v2.23.0
 | 
				
			||||||
	github.com/in-toto/in-toto-golang v0.5.0
 | 
						github.com/in-toto/in-toto-golang v0.5.0
 | 
				
			||||||
	github.com/mitchellh/hashstructure/v2 v2.0.2
 | 
						github.com/mitchellh/hashstructure/v2 v2.0.2
 | 
				
			||||||
@@ -35,6 +36,7 @@ require (
 | 
				
			|||||||
	github.com/opencontainers/go-digest v1.0.0
 | 
						github.com/opencontainers/go-digest v1.0.0
 | 
				
			||||||
	github.com/opencontainers/image-spec v1.1.0
 | 
						github.com/opencontainers/image-spec v1.1.0
 | 
				
			||||||
	github.com/pelletier/go-toml v1.9.5
 | 
						github.com/pelletier/go-toml v1.9.5
 | 
				
			||||||
 | 
						github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c
 | 
				
			||||||
	github.com/pkg/errors v0.9.1
 | 
						github.com/pkg/errors v0.9.1
 | 
				
			||||||
	github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10
 | 
						github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10
 | 
				
			||||||
	github.com/serialx/hashring v0.0.0-20200727003509-22c0c7ab6b1b
 | 
						github.com/serialx/hashring v0.0.0-20200727003509-22c0c7ab6b1b
 | 
				
			||||||
@@ -49,6 +51,7 @@ require (
 | 
				
			|||||||
	go.opentelemetry.io/otel/metric v1.31.0
 | 
						go.opentelemetry.io/otel/metric v1.31.0
 | 
				
			||||||
	go.opentelemetry.io/otel/sdk v1.31.0
 | 
						go.opentelemetry.io/otel/sdk v1.31.0
 | 
				
			||||||
	go.opentelemetry.io/otel/trace v1.31.0
 | 
						go.opentelemetry.io/otel/trace v1.31.0
 | 
				
			||||||
 | 
						golang.org/x/exp v0.0.0-20240909161429-701f63a606c0
 | 
				
			||||||
	golang.org/x/mod v0.21.0
 | 
						golang.org/x/mod v0.21.0
 | 
				
			||||||
	golang.org/x/sync v0.10.0
 | 
						golang.org/x/sync v0.10.0
 | 
				
			||||||
	golang.org/x/sys v0.28.0
 | 
						golang.org/x/sys v0.28.0
 | 
				
			||||||
@@ -114,7 +117,6 @@ require (
 | 
				
			|||||||
	github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 // indirect
 | 
						github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 // indirect
 | 
				
			||||||
	github.com/hashicorp/errwrap v1.1.0 // indirect
 | 
						github.com/hashicorp/errwrap v1.1.0 // indirect
 | 
				
			||||||
	github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
 | 
						github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
 | 
				
			||||||
	github.com/hashicorp/go-multierror v1.1.1 // indirect
 | 
					 | 
				
			||||||
	github.com/imdario/mergo v0.3.16 // indirect
 | 
						github.com/imdario/mergo v0.3.16 // indirect
 | 
				
			||||||
	github.com/inconshreveable/mousetrap v1.1.0 // indirect
 | 
						github.com/inconshreveable/mousetrap v1.1.0 // indirect
 | 
				
			||||||
	github.com/josharian/intern v1.0.0 // indirect
 | 
						github.com/josharian/intern v1.0.0 // indirect
 | 
				
			||||||
@@ -166,7 +168,6 @@ require (
 | 
				
			|||||||
	go.opentelemetry.io/otel/sdk/metric v1.31.0 // indirect
 | 
						go.opentelemetry.io/otel/sdk/metric v1.31.0 // indirect
 | 
				
			||||||
	go.opentelemetry.io/proto/otlp v1.3.1 // indirect
 | 
						go.opentelemetry.io/proto/otlp v1.3.1 // indirect
 | 
				
			||||||
	golang.org/x/crypto v0.31.0 // indirect
 | 
						golang.org/x/crypto v0.31.0 // indirect
 | 
				
			||||||
	golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 // indirect
 | 
					 | 
				
			||||||
	golang.org/x/net v0.33.0 // indirect
 | 
						golang.org/x/net v0.33.0 // indirect
 | 
				
			||||||
	golang.org/x/oauth2 v0.23.0 // indirect
 | 
						golang.org/x/oauth2 v0.23.0 // indirect
 | 
				
			||||||
	golang.org/x/time v0.6.0 // indirect
 | 
						golang.org/x/time v0.6.0 // indirect
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										2
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								go.sum
									
									
									
									
									
								
							@@ -359,6 +359,8 @@ github.com/opentracing/opentracing-go v1.1.0 h1:pWlfV3Bxv7k65HYwkikxat0+s3pV4bsq
 | 
				
			|||||||
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
 | 
					github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
 | 
				
			||||||
github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
 | 
					github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
 | 
				
			||||||
github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
 | 
					github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
 | 
				
			||||||
 | 
					github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=
 | 
				
			||||||
 | 
					github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU=
 | 
				
			||||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 | 
					github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 | 
				
			||||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 | 
					github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 | 
				
			||||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
 | 
					github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -28,13 +28,14 @@ func BuildBackendEnabled() bool {
 | 
				
			|||||||
	return bbEnabled
 | 
						return bbEnabled
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func BuildURL(ref string) string {
 | 
				
			||||||
 | 
						return fmt.Sprintf("docker-desktop://dashboard/build/%s", ref)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func BuildDetailsOutput(refs map[string]string, term bool) string {
 | 
					func BuildDetailsOutput(refs map[string]string, term bool) string {
 | 
				
			||||||
	if len(refs) == 0 {
 | 
						if len(refs) == 0 {
 | 
				
			||||||
		return ""
 | 
							return ""
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	refURL := func(ref string) string {
 | 
					 | 
				
			||||||
		return fmt.Sprintf("docker-desktop://dashboard/build/%s", ref)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	var out bytes.Buffer
 | 
						var out bytes.Buffer
 | 
				
			||||||
	out.WriteString("View build details: ")
 | 
						out.WriteString("View build details: ")
 | 
				
			||||||
	multiTargets := len(refs) > 1
 | 
						multiTargets := len(refs) > 1
 | 
				
			||||||
@@ -43,9 +44,10 @@ func BuildDetailsOutput(refs map[string]string, term bool) string {
 | 
				
			|||||||
			out.WriteString(fmt.Sprintf("\n  %s: ", target))
 | 
								out.WriteString(fmt.Sprintf("\n  %s: ", target))
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if term {
 | 
							if term {
 | 
				
			||||||
			out.WriteString(hyperlink(refURL(ref)))
 | 
								url := BuildURL(ref)
 | 
				
			||||||
 | 
								out.WriteString(ANSIHyperlink(url, url))
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			out.WriteString(refURL(ref))
 | 
								out.WriteString(BuildURL(ref))
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return out.String()
 | 
						return out.String()
 | 
				
			||||||
@@ -57,9 +59,9 @@ func PrintBuildDetails(w io.Writer, refs map[string]string, term bool) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func hyperlink(url string) string {
 | 
					func ANSIHyperlink(url, text string) string {
 | 
				
			||||||
	// create an escape sequence using the OSC 8 format: https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda
 | 
						// 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)
 | 
						return fmt.Sprintf("\033]8;;%s\033\\%s\033]8;;\033\\", url, text)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type ErrorWithBuildRef struct {
 | 
					type ErrorWithBuildRef struct {
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										23
									
								
								vendor/github.com/pkg/browser/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								vendor/github.com/pkg/browser/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,23 @@
 | 
				
			|||||||
 | 
					Copyright (c) 2014, Dave Cheney <dave@cheney.net>
 | 
				
			||||||
 | 
					All rights reserved.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Redistribution and use in source and binary forms, with or without
 | 
				
			||||||
 | 
					modification, are permitted provided that the following conditions are met:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					* Redistributions of source code must retain the above copyright notice, this
 | 
				
			||||||
 | 
					  list of conditions and the following disclaimer.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					* Redistributions in binary form must reproduce the above copyright notice,
 | 
				
			||||||
 | 
					  this list of conditions and the following disclaimer in the documentation
 | 
				
			||||||
 | 
					  and/or other materials provided with the distribution.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 | 
				
			||||||
 | 
					AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 | 
				
			||||||
 | 
					IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 | 
				
			||||||
 | 
					DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
 | 
				
			||||||
 | 
					FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 | 
				
			||||||
 | 
					DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 | 
				
			||||||
 | 
					SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 | 
				
			||||||
 | 
					CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 | 
				
			||||||
 | 
					OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 | 
				
			||||||
 | 
					OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | 
				
			||||||
							
								
								
									
										55
									
								
								vendor/github.com/pkg/browser/README.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								vendor/github.com/pkg/browser/README.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,55 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					# browser
 | 
				
			||||||
 | 
					    import "github.com/pkg/browser"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Package browser provides helpers to open files, readers, and urls in a browser window.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The choice of which browser is started is entirely client dependant.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Variables
 | 
				
			||||||
 | 
					``` go
 | 
				
			||||||
 | 
					var Stderr io.Writer = os.Stderr
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					Stderr is the io.Writer to which executed commands write standard error.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					``` go
 | 
				
			||||||
 | 
					var Stdout io.Writer = os.Stdout
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					Stdout is the io.Writer to which executed commands write standard output.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## func OpenFile
 | 
				
			||||||
 | 
					``` go
 | 
				
			||||||
 | 
					func OpenFile(path string) error
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					OpenFile opens new browser window for the file path.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## func OpenReader
 | 
				
			||||||
 | 
					``` go
 | 
				
			||||||
 | 
					func OpenReader(r io.Reader) error
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					OpenReader consumes the contents of r and presents the
 | 
				
			||||||
 | 
					results in a new browser window.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## func OpenURL
 | 
				
			||||||
 | 
					``` go
 | 
				
			||||||
 | 
					func OpenURL(url string) error
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					OpenURL opens a new browser window pointing to url.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- - -
 | 
				
			||||||
 | 
					Generated by [godoc2md](http://godoc.org/github.com/davecheney/godoc2md)
 | 
				
			||||||
							
								
								
									
										57
									
								
								vendor/github.com/pkg/browser/browser.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								vendor/github.com/pkg/browser/browser.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,57 @@
 | 
				
			|||||||
 | 
					// Package browser provides helpers to open files, readers, and urls in a browser window.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// The choice of which browser is started is entirely client dependant.
 | 
				
			||||||
 | 
					package browser
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"io"
 | 
				
			||||||
 | 
						"io/ioutil"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
						"os/exec"
 | 
				
			||||||
 | 
						"path/filepath"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Stdout is the io.Writer to which executed commands write standard output.
 | 
				
			||||||
 | 
					var Stdout io.Writer = os.Stdout
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Stderr is the io.Writer to which executed commands write standard error.
 | 
				
			||||||
 | 
					var Stderr io.Writer = os.Stderr
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// OpenFile opens new browser window for the file path.
 | 
				
			||||||
 | 
					func OpenFile(path string) error {
 | 
				
			||||||
 | 
						path, err := filepath.Abs(path)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return OpenURL("file://" + path)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// OpenReader consumes the contents of r and presents the
 | 
				
			||||||
 | 
					// results in a new browser window.
 | 
				
			||||||
 | 
					func OpenReader(r io.Reader) error {
 | 
				
			||||||
 | 
						f, err := ioutil.TempFile("", "browser.*.html")
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return fmt.Errorf("browser: could not create temporary file: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if _, err := io.Copy(f, r); err != nil {
 | 
				
			||||||
 | 
							f.Close()
 | 
				
			||||||
 | 
							return fmt.Errorf("browser: caching temporary file failed: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if err := f.Close(); err != nil {
 | 
				
			||||||
 | 
							return fmt.Errorf("browser: caching temporary file failed: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return OpenFile(f.Name())
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// OpenURL opens a new browser window pointing to url.
 | 
				
			||||||
 | 
					func OpenURL(url string) error {
 | 
				
			||||||
 | 
						return openBrowser(url)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func runCmd(prog string, args ...string) error {
 | 
				
			||||||
 | 
						cmd := exec.Command(prog, args...)
 | 
				
			||||||
 | 
						cmd.Stdout = Stdout
 | 
				
			||||||
 | 
						cmd.Stderr = Stderr
 | 
				
			||||||
 | 
						return cmd.Run()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										5
									
								
								vendor/github.com/pkg/browser/browser_darwin.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								vendor/github.com/pkg/browser/browser_darwin.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,5 @@
 | 
				
			|||||||
 | 
					package browser
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func openBrowser(url string) error {
 | 
				
			||||||
 | 
						return runCmd("open", url)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										14
									
								
								vendor/github.com/pkg/browser/browser_freebsd.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								vendor/github.com/pkg/browser/browser_freebsd.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,14 @@
 | 
				
			|||||||
 | 
					package browser
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"errors"
 | 
				
			||||||
 | 
						"os/exec"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func openBrowser(url string) error {
 | 
				
			||||||
 | 
						err := runCmd("xdg-open", url)
 | 
				
			||||||
 | 
						if e, ok := err.(*exec.Error); ok && e.Err == exec.ErrNotFound {
 | 
				
			||||||
 | 
							return errors.New("xdg-open: command not found - install xdg-utils from ports(8)")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										21
									
								
								vendor/github.com/pkg/browser/browser_linux.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								vendor/github.com/pkg/browser/browser_linux.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,21 @@
 | 
				
			|||||||
 | 
					package browser
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"os/exec"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func openBrowser(url string) error {
 | 
				
			||||||
 | 
						providers := []string{"xdg-open", "x-www-browser", "www-browser"}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// There are multiple possible providers to open a browser on linux
 | 
				
			||||||
 | 
						// One of them is xdg-open, another is x-www-browser, then there's www-browser, etc.
 | 
				
			||||||
 | 
						// Look for one that exists and run it
 | 
				
			||||||
 | 
						for _, provider := range providers {
 | 
				
			||||||
 | 
							if _, err := exec.LookPath(provider); err == nil {
 | 
				
			||||||
 | 
								return runCmd(provider, url)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return &exec.Error{Name: strings.Join(providers, ","), Err: exec.ErrNotFound}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										14
									
								
								vendor/github.com/pkg/browser/browser_netbsd.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								vendor/github.com/pkg/browser/browser_netbsd.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,14 @@
 | 
				
			|||||||
 | 
					package browser
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"errors"
 | 
				
			||||||
 | 
						"os/exec"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func openBrowser(url string) error {
 | 
				
			||||||
 | 
						err := runCmd("xdg-open", url)
 | 
				
			||||||
 | 
						if e, ok := err.(*exec.Error); ok && e.Err == exec.ErrNotFound {
 | 
				
			||||||
 | 
							return errors.New("xdg-open: command not found - install xdg-utils from pkgsrc(7)")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										14
									
								
								vendor/github.com/pkg/browser/browser_openbsd.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								vendor/github.com/pkg/browser/browser_openbsd.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,14 @@
 | 
				
			|||||||
 | 
					package browser
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"errors"
 | 
				
			||||||
 | 
						"os/exec"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func openBrowser(url string) error {
 | 
				
			||||||
 | 
						err := runCmd("xdg-open", url)
 | 
				
			||||||
 | 
						if e, ok := err.(*exec.Error); ok && e.Err == exec.ErrNotFound {
 | 
				
			||||||
 | 
							return errors.New("xdg-open: command not found - install xdg-utils from ports(8)")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										12
									
								
								vendor/github.com/pkg/browser/browser_unsupported.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								vendor/github.com/pkg/browser/browser_unsupported.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,12 @@
 | 
				
			|||||||
 | 
					// +build !linux,!windows,!darwin,!openbsd,!freebsd,!netbsd
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package browser
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"runtime"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func openBrowser(url string) error {
 | 
				
			||||||
 | 
						return fmt.Errorf("openBrowser: unsupported operating system: %v", runtime.GOOS)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										7
									
								
								vendor/github.com/pkg/browser/browser_windows.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								vendor/github.com/pkg/browser/browser_windows.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,7 @@
 | 
				
			|||||||
 | 
					package browser
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import "golang.org/x/sys/windows"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func openBrowser(url string) error {
 | 
				
			||||||
 | 
						return windows.ShellExecute(0, nil, windows.StringToUTF16Ptr(url), nil, nil, windows.SW_SHOWNORMAL)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										3
									
								
								vendor/modules.txt
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								vendor/modules.txt
									
									
									
									
										vendored
									
									
								
							@@ -636,6 +636,9 @@ github.com/opencontainers/image-spec/specs-go/v1
 | 
				
			|||||||
# github.com/pelletier/go-toml v1.9.5
 | 
					# github.com/pelletier/go-toml v1.9.5
 | 
				
			||||||
## explicit; go 1.12
 | 
					## explicit; go 1.12
 | 
				
			||||||
github.com/pelletier/go-toml
 | 
					github.com/pelletier/go-toml
 | 
				
			||||||
 | 
					# github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c
 | 
				
			||||||
 | 
					## explicit; go 1.14
 | 
				
			||||||
 | 
					github.com/pkg/browser
 | 
				
			||||||
# github.com/pkg/errors v0.9.1
 | 
					# github.com/pkg/errors v0.9.1
 | 
				
			||||||
## explicit
 | 
					## explicit
 | 
				
			||||||
github.com/pkg/errors
 | 
					github.com/pkg/errors
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user