Skip to content

Commit 816b7cf

Browse files
committed
build: display build details link
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
1 parent ab5f5e4 commit 816b7cf

File tree

8 files changed

+150
-2
lines changed

8 files changed

+150
-2
lines changed

build/build.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import (
2626
"github.com/docker/buildx/builder"
2727
"github.com/docker/buildx/driver"
2828
"github.com/docker/buildx/localstate"
29+
"github.com/docker/buildx/util/desktop"
2930
"github.com/docker/buildx/util/dockerutil"
3031
"github.com/docker/buildx/util/imagetools"
3132
"github.com/docker/buildx/util/progress"
@@ -822,6 +823,7 @@ func BuildWithResultHandler(ctx context.Context, nodes []builder.Node, opt map[s
822823

823824
for i, dp := range dps {
824825
i, dp, so := i, dp, *dp.so
826+
node := nodes[dp.driverIndex]
825827
if multiDriver {
826828
for i, e := range so.Exports {
827829
switch e.Type {
@@ -940,6 +942,16 @@ func BuildWithResultHandler(ctx context.Context, nodes []builder.Node, opt map[s
940942
} else {
941943
rr, err = c.Build(ctx, so, "buildx", buildFunc, ch)
942944
}
945+
if node.Driver.Features(ctx)[driver.HistoryAPI] && desktop.BuildBackendEnabled() {
946+
buildRef := fmt.Sprintf("%s/%s/%s", node.Builder, node.Name, so.Ref)
947+
if err != nil {
948+
return &desktop.ErrorWithBuildRef{
949+
Ref: buildRef,
950+
Err: err,
951+
}
952+
}
953+
progress.WriteBuildRef(w, k, buildRef)
954+
}
943955
if err != nil {
944956
return err
945957
}

cmd/buildx/main.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"os"
66

77
"github.com/docker/buildx/commands"
8+
"github.com/docker/buildx/util/desktop"
89
"github.com/docker/buildx/version"
910
"github.com/docker/cli/cli"
1011
"github.com/docker/cli/cli-plugins/manager"
@@ -86,6 +87,9 @@ func main() {
8687
} else {
8788
fmt.Fprintf(cmd.Err(), "ERROR: %v\n", err)
8889
}
90+
if ebr, ok := err.(*desktop.ErrorWithBuildRef); ok {
91+
ebr.Print(cmd.Err())
92+
}
8993

9094
os.Exit(1)
9195
}

commands/bake.go

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,15 @@ import (
66
"fmt"
77
"os"
88

9+
"github.com/containerd/console"
910
"github.com/containerd/containerd/platforms"
1011
"github.com/docker/buildx/bake"
1112
"github.com/docker/buildx/build"
1213
"github.com/docker/buildx/builder"
1314
"github.com/docker/buildx/util/buildflags"
1415
"github.com/docker/buildx/util/cobrautil/completion"
1516
"github.com/docker/buildx/util/confutil"
17+
"github.com/docker/buildx/util/desktop"
1618
"github.com/docker/buildx/util/dockerutil"
1719
"github.com/docker/buildx/util/progress"
1820
"github.com/docker/buildx/util/tracing"
@@ -117,7 +119,13 @@ func runBake(dockerCli command.Cli, targets []string, in bakeOptions, cFlags com
117119
progressTextDesc = fmt.Sprintf("building with %q instance using %s driver", b.Name, b.Driver)
118120
}
119121

120-
printer, err := progress.NewPrinter(ctx2, os.Stderr, os.Stderr, cFlags.progress,
122+
var term bool
123+
if _, err := console.ConsoleFromFile(os.Stderr); err == nil {
124+
term = true
125+
}
126+
127+
var printer *progress.Printer
128+
printer, err = progress.NewPrinter(ctx2, os.Stderr, os.Stderr, cFlags.progress,
121129
progress.WithDesc(progressTextDesc, progressConsoleDesc),
122130
)
123131
if err != nil {
@@ -130,6 +138,9 @@ func runBake(dockerCli command.Cli, targets []string, in bakeOptions, cFlags com
130138
if err == nil {
131139
err = err1
132140
}
141+
if err == nil && cFlags.progress != progress.PrinterModeQuiet {
142+
desktop.PrintBuildDetails(os.Stderr, printer.BuildRefs(), term)
143+
}
133144
}
134145
}()
135146

commands/build.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import (
2626
"github.com/docker/buildx/store"
2727
"github.com/docker/buildx/store/storeutil"
2828
"github.com/docker/buildx/util/buildflags"
29+
"github.com/docker/buildx/util/desktop"
2930
"github.com/docker/buildx/util/ioset"
3031
"github.com/docker/buildx/util/progress"
3132
"github.com/docker/buildx/util/tracing"
@@ -238,6 +239,11 @@ func runBuild(dockerCli command.Cli, options buildOptions) (err error) {
238239
return err
239240
}
240241

242+
var term bool
243+
if _, err := console.ConsoleFromFile(os.Stderr); err == nil {
244+
term = true
245+
}
246+
241247
ctx2, cancel := context.WithCancel(context.TODO())
242248
defer cancel()
243249
progressMode, err := options.toProgress()
@@ -273,7 +279,9 @@ func runBuild(dockerCli command.Cli, options buildOptions) (err error) {
273279
return retErr
274280
}
275281

276-
if progressMode == progress.PrinterModeQuiet {
282+
if progressMode != progress.PrinterModeQuiet {
283+
desktop.PrintBuildDetails(os.Stderr, printer.BuildRefs(), term)
284+
} else {
277285
fmt.Println(getImageID(resp.ExporterResponse))
278286
}
279287
if options.imageIDFile != "" {

controller/pb/progress.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@ func (w *writer) Write(status *client.SolveStatus) {
1919
w.ch <- ToControlStatus(status)
2020
}
2121

22+
func (w *writer) WriteBuildRef(target string, ref string) {
23+
return
24+
}
25+
2226
func (w *writer) ValidateLogSource(digest.Digest, interface{}) bool {
2327
return true
2428
}

util/desktop/desktop.go

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
package desktop
2+
3+
import (
4+
"bytes"
5+
"fmt"
6+
"io"
7+
"os"
8+
"path/filepath"
9+
"sync"
10+
11+
"github.com/containerd/console"
12+
)
13+
14+
var (
15+
bbEnabledOnce sync.Once
16+
bbEnabled bool
17+
)
18+
19+
func BuildBackendEnabled() bool {
20+
bbEnabledOnce.Do(func() {
21+
home, err := os.UserHomeDir()
22+
if err != nil {
23+
return
24+
}
25+
_, err = os.Stat(filepath.Join(home, ".docker", "desktop-build", ".lastaccess"))
26+
bbEnabled = err == nil
27+
})
28+
return bbEnabled
29+
}
30+
31+
func BuildDetailsOutput(refs map[string]string, term bool) string {
32+
if len(refs) == 0 {
33+
return ""
34+
}
35+
refURL := func(ref string) string {
36+
return fmt.Sprintf("docker-desktop://dashboard/build/%s", ref)
37+
}
38+
var out bytes.Buffer
39+
out.WriteString("View build details: ")
40+
multiTargets := len(refs) > 1
41+
for target, ref := range refs {
42+
if multiTargets {
43+
out.WriteString(fmt.Sprintf("\n %s: ", target))
44+
}
45+
if term {
46+
out.WriteString(hyperlink(refURL(ref)))
47+
} else {
48+
out.WriteString(refURL(ref))
49+
}
50+
}
51+
return out.String()
52+
}
53+
54+
func PrintBuildDetails(w io.Writer, refs map[string]string, term bool) {
55+
if out := BuildDetailsOutput(refs, term); out != "" {
56+
fmt.Fprintf(w, "\n%s\n", out)
57+
}
58+
}
59+
60+
func hyperlink(url string) string {
61+
// create an escape sequence using the OSC 8 format: https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda
62+
return fmt.Sprintf("\033]8;;%s\033\\%s\033]8;;\033\\", url, url)
63+
}
64+
65+
type ErrorWithBuildRef struct {
66+
Ref string
67+
Err error
68+
Msg string
69+
}
70+
71+
func (e *ErrorWithBuildRef) Error() string {
72+
return e.Err.Error()
73+
}
74+
75+
func (e *ErrorWithBuildRef) Unwrap() error {
76+
return e.Err
77+
}
78+
79+
func (e *ErrorWithBuildRef) Print(w io.Writer) error {
80+
var term bool
81+
if _, err := console.ConsoleFromFile(os.Stderr); err == nil {
82+
term = true
83+
}
84+
fmt.Fprintf(w, "\n%s", BuildDetailsOutput(map[string]string{"default": e.Ref}, term))
85+
return nil
86+
}

util/progress/printer.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,11 @@ type Printer struct {
3333
warnings []client.VertexWarning
3434
logMu sync.Mutex
3535
logSourceMap map[digest.Digest]interface{}
36+
37+
// TODO: remove once we can use result context to pass build ref
38+
// see https://github.com/docker/buildx/pull/1861
39+
buildRefsMu sync.Mutex
40+
buildRefs map[string]string
3641
}
3742

3843
func (p *Printer) Wait() error {
@@ -143,6 +148,19 @@ func NewPrinter(ctx context.Context, w io.Writer, out console.File, mode string,
143148
return pw, nil
144149
}
145150

151+
func (p *Printer) WriteBuildRef(target string, ref string) {
152+
p.buildRefsMu.Lock()
153+
defer p.buildRefsMu.Unlock()
154+
if p.buildRefs == nil {
155+
p.buildRefs = map[string]string{}
156+
}
157+
p.buildRefs[target] = ref
158+
}
159+
160+
func (p *Printer) BuildRefs() map[string]string {
161+
return p.buildRefs
162+
}
163+
146164
type printerOpts struct {
147165
displayOpts []progressui.DisplaySolveStatusOpt
148166

util/progress/writer.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010

1111
type Writer interface {
1212
Write(*client.SolveStatus)
13+
WriteBuildRef(string, string)
1314
ValidateLogSource(digest.Digest, interface{}) bool
1415
ClearLogSource(interface{})
1516
}
@@ -41,6 +42,10 @@ func Write(w Writer, name string, f func() error) {
4142
})
4243
}
4344

45+
func WriteBuildRef(w Writer, target string, ref string) {
46+
w.WriteBuildRef(target, ref)
47+
}
48+
4449
func NewChannel(w Writer) (chan *client.SolveStatus, chan struct{}) {
4550
ch := make(chan *client.SolveStatus)
4651
done := make(chan struct{})

0 commit comments

Comments
 (0)