11package history
22
33import (
4+ "bytes"
45 "cmp"
56 "context"
67 "encoding/json"
@@ -24,16 +25,24 @@ import (
2425 "github.com/docker/buildx/util/confutil"
2526 "github.com/docker/buildx/util/desktop"
2627 "github.com/docker/cli/cli/command"
28+ "github.com/docker/cli/cli/debug"
2729 slsa "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/common"
2830 slsa02 "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/v0.2"
2931 controlapi "github.com/moby/buildkit/api/services/control"
32+ "github.com/moby/buildkit/client"
33+ "github.com/moby/buildkit/solver/errdefs"
3034 provenancetypes "github.com/moby/buildkit/solver/llbsolver/provenance/types"
35+ "github.com/moby/buildkit/util/grpcerrors"
36+ "github.com/moby/buildkit/util/stack"
3137 "github.com/opencontainers/go-digest"
3238 ocispecs "github.com/opencontainers/image-spec/specs-go/v1"
3339 "github.com/pkg/errors"
3440 "github.com/spf13/cobra"
3541 "github.com/tonistiigi/go-csvvalue"
42+ spb "google.golang.org/genproto/googleapis/rpc/status"
3643 "google.golang.org/grpc/codes"
44+ "google.golang.org/grpc/status"
45+ proto "google.golang.org/protobuf/proto"
3746)
3847
3948type inspectOptions struct {
@@ -184,16 +193,16 @@ func runInspect(ctx context.Context, dockerCli command.Cli, opts inspectOptions)
184193
185194 tw = tabwriter .NewWriter (dockerCli .Out (), 1 , 8 , 1 , '\t' , 0 )
186195
187- fmt .Fprintf (tw , "Started:\t %s\n " , rec .CreatedAt .AsTime ().Format ("2006-01-02 15:04:05" ))
196+ fmt .Fprintf (tw , "Started:\t %s\n " , rec .CreatedAt .AsTime ().Local (). Format ("2006-01-02 15:04:05" ))
188197 var duration time.Duration
189- var status string
198+ var statusStr string
190199 if rec .CompletedAt != nil {
191200 duration = rec .CompletedAt .AsTime ().Sub (rec .CreatedAt .AsTime ())
192201 } else {
193202 duration = rec .currentTimestamp .Sub (rec .CreatedAt .AsTime ())
194- status = " (running)"
203+ statusStr = " (running)"
195204 }
196- fmt .Fprintf (tw , "Duration:\t %s%s\n " , formatDuration (duration ), status )
205+ fmt .Fprintf (tw , "Duration:\t %s%s\n " , formatDuration (duration ), statusStr )
197206 if rec .Error != nil {
198207 if codes .Code (rec .Error .Code ) == codes .Canceled {
199208 fmt .Fprintf (tw , "Status:\t Canceled\n " )
@@ -309,9 +318,51 @@ func runInspect(ctx context.Context, dockerCli command.Cli, opts inspectOptions)
309318 fmt .Fprintln (dockerCli .Out ())
310319 }
311320
321+ if rec .ExternalError != nil {
322+ dt , err := content .ReadBlob (ctx , store , ociDesc (rec .ExternalError ))
323+ if err != nil {
324+ return errors .Wrapf (err , "failed to read external error %s" , rec .ExternalError .Digest )
325+ }
326+ var st spb.Status
327+ if err := proto .Unmarshal (dt , & st ); err != nil {
328+ return errors .Wrapf (err , "failed to unmarshal external error %s" , rec .ExternalError .Digest )
329+ }
330+ retErr := grpcerrors .FromGRPC (status .ErrorProto (& st ))
331+ for _ , s := range errdefs .Sources (retErr ) {
332+ s .Print (dockerCli .Out ())
333+ }
334+ fmt .Fprintln (dockerCli .Out ())
335+
336+ var ve * errdefs.VertexError
337+ if errors .As (retErr , & ve ) {
338+ dgst , err := digest .Parse (ve .Vertex .Digest )
339+ if err != nil {
340+ return errors .Wrapf (err , "failed to parse vertex digest %s" , ve .Vertex .Digest )
341+ }
342+ name , logs , err := loadVertexLogs (ctx , c , rec .Ref , dgst , 16 )
343+ if err != nil {
344+ return errors .Wrapf (err , "failed to load vertex logs %s" , dgst )
345+ }
346+ if len (logs ) > 0 {
347+ fmt .Fprintln (dockerCli .Out (), "Logs:" )
348+ fmt .Fprintf (dockerCli .Out (), "> => %s:\n " , name )
349+ for _ , l := range logs {
350+ fmt .Fprintln (dockerCli .Out (), "> " + l )
351+ }
352+ fmt .Fprintln (dockerCli .Out ())
353+ }
354+ }
355+
356+ if debug .IsEnabled () {
357+ fmt .Fprintf (dockerCli .Out (), "\n %+v\n " , stack .Formatter (retErr ))
358+ } else if len (stack .Traces (retErr )) > 0 {
359+ fmt .Fprintf (dockerCli .Out (), "Enable --debug to see stack traces for error\n " )
360+ }
361+ }
362+
312363 fmt .Fprintf (dockerCli .Out (), "Print build logs: docker buildx history logs %s\n " , rec .Ref )
313364
314- fmt .Fprintf (dockerCli .Out (), "View build in Docker Desktop: %s\n " , desktop .BuildURL (rec .Ref ))
365+ fmt .Fprintf (dockerCli .Out (), "View build in Docker Desktop: %s\n " , desktop .BuildURL (fmt . Sprintf ( "%s/%s/%s" , rec .node . Builder , rec . node . Name , rec . Ref ) ))
315366
316367 return nil
317368}
@@ -342,6 +393,73 @@ func inspectCmd(dockerCli command.Cli, rootOpts RootOptions) *cobra.Command {
342393 return cmd
343394}
344395
396+ func loadVertexLogs (ctx context.Context , c * client.Client , ref string , dgst digest.Digest , limit int ) (string , []string , error ) {
397+ st , err := c .ControlClient ().Status (ctx , & controlapi.StatusRequest {
398+ Ref : ref ,
399+ })
400+ if err != nil {
401+ return "" , nil , err
402+ }
403+
404+ var name string
405+ var logs []string
406+ lastState := map [int ]int {}
407+
408+ loop0:
409+ for {
410+ select {
411+ case <- ctx .Done ():
412+ st .CloseSend ()
413+ return "" , nil , context .Cause (ctx )
414+ default :
415+ ev , err := st .Recv ()
416+ if err != nil {
417+ if errors .Is (err , io .EOF ) {
418+ break loop0
419+ }
420+ return "" , nil , err
421+ }
422+ ss := client .NewSolveStatus (ev )
423+ for _ , v := range ss .Vertexes {
424+ if v .Digest == dgst {
425+ name = v .Name
426+ break
427+ }
428+ }
429+ for _ , l := range ss .Logs {
430+ if l .Vertex == dgst {
431+ parts := bytes .Split (l .Data , []byte ("\n " ))
432+ for i , p := range parts {
433+ var wrote bool
434+ if i == 0 {
435+ idx , ok := lastState [l .Stream ]
436+ if ok && idx != - 1 {
437+ logs [idx ] = logs [idx ] + string (p )
438+ wrote = true
439+ }
440+ }
441+ if ! wrote {
442+ if len (p ) > 0 {
443+ logs = append (logs , string (p ))
444+ }
445+ lastState [l .Stream ] = len (logs ) - 1
446+ }
447+ if i == len (parts )- 1 && len (p ) == 0 {
448+ lastState [l .Stream ] = - 1
449+ }
450+ }
451+ }
452+ }
453+ }
454+ }
455+
456+ if limit > 0 && len (logs ) > limit {
457+ logs = logs [len (logs )- limit :]
458+ }
459+
460+ return name , logs , nil
461+ }
462+
345463type attachment struct {
346464 platform * ocispecs.Platform
347465 descr ocispecs.Descriptor
0 commit comments