@@ -2,16 +2,27 @@ package history
22
33import (
44 "context"
5+ "fmt"
6+ "io"
57 "log"
8+ "os"
9+ "path/filepath"
610 "slices"
11+ "strconv"
12+ "strings"
13+ "text/tabwriter"
14+ "time"
715
16+ "github.com/containerd/platforms"
817 "github.com/docker/buildx/builder"
918 "github.com/docker/buildx/localstate"
1019 "github.com/docker/buildx/util/cobrautil/completion"
1120 "github.com/docker/buildx/util/confutil"
1221 "github.com/docker/cli/cli/command"
1322 "github.com/pkg/errors"
1423 "github.com/spf13/cobra"
24+ "github.com/tonistiigi/go-csvvalue"
25+ "google.golang.org/grpc/codes"
1526)
1627
1728type inspectOptions struct {
@@ -64,21 +75,188 @@ func runInspect(ctx context.Context, dockerCli command.Cli, opts inspectOptions)
6475 log .Printf ("rec %+v" , rec )
6576 log .Printf ("st %+v" , st )
6677
67- // Context
68- // Dockerfile
69- // Target
70- // VCS Repo / Commit
71- // Platform
78+ tw := tabwriter .NewWriter (dockerCli .Out (), 1 , 8 , 1 , '\t' , 0 )
7279
73- // Started
74- // Duration
75- // Number of steps
76- // Cached steps
77- // Status
80+ attrs := rec .FrontendAttrs
81+ delete (attrs , "frontend.caps" )
7882
79- // build-args
80- // exporters (image)
83+ writeAttr := func (k , name string , f func (v string ) (string , bool )) {
84+ if v , ok := attrs [k ]; ok {
85+ if f != nil {
86+ v , ok = f (v )
87+ }
88+ if ok {
89+ fmt .Fprintf (tw , "%s:\t %s\n " , name , v )
90+ }
91+ }
92+ delete (attrs , k )
93+ }
94+
95+ // keyCacheFrom = "cache-from" // for registry only. deprecated in favor of keyCacheImports
96+ // keyCacheImports = "cache-imports" // JSON representation of []CacheOptionsEntry
97+
98+ // // Don't forget to update frontend documentation if you add
99+ // // a new build-arg: frontend/dockerfile/docs/reference.md
100+ // keyCacheNSArg = "build-arg:BUILDKIT_CACHE_MOUNT_NS"
101+ // keyMultiPlatformArg = "build-arg:BUILDKIT_MULTI_PLATFORM"
102+ // keyHostnameArg = "build-arg:BUILDKIT_SANDBOX_HOSTNAME"
103+ // keyDockerfileLintArg = "build-arg:BUILDKIT_DOCKERFILE_CHECK"
104+ // keyContextKeepGitDirArg = "build-arg:BUILDKIT_CONTEXT_KEEP_GIT_DIR"
105+ // keySourceDateEpoch = "build-arg:SOURCE_DATE_EPOCH"
106+
107+ var context string
108+ var dockerfile string
109+ if st != nil {
110+ context = st .LocalPath
111+ dockerfile = st .DockerfilePath
112+ wd , _ := os .Getwd ()
113+
114+ if dockerfile != "" && dockerfile != "-" {
115+ if rel , err := filepath .Rel (context , dockerfile ); err == nil {
116+ dockerfile = rel
117+ }
118+ }
119+ if context != "" {
120+ if rel , err := filepath .Rel (wd , context ); err == nil {
121+ context = rel
122+ }
123+ }
124+ }
125+
126+ if v , ok := attrs ["context" ]; ok && context == "" {
127+ delete (attrs , "context" )
128+ context = v
129+ }
130+ if dockerfile == "" {
131+ if v , ok := attrs ["filename" ]; ok {
132+ dockerfile = v
133+ if dfdir , ok := attrs ["vcs:localdir:dockerfile" ]; ok {
134+ dockerfile = filepath .Join (dfdir , dockerfile )
135+ }
136+ }
137+ }
138+ delete (attrs , "filename" )
139+
140+ if context != "" {
141+ fmt .Fprintf (tw , "Context:\t %s\n " , context )
142+ }
143+ if dockerfile != "" {
144+ fmt .Fprintf (tw , "Dockerfile:\t %s\n " , dockerfile )
145+ }
146+ if _ , ok := attrs ["context" ]; ! ok {
147+ if src , ok := attrs ["vcs:source" ]; ok {
148+ fmt .Fprintf (tw , "VCS Repository:\t %s\n " , src )
149+ }
150+ if rev , ok := attrs ["vcs:revision" ]; ok {
151+ fmt .Fprintf (tw , "VCS Revision:\t %s\n " , rev )
152+ }
153+ }
154+
155+ writeAttr ("target" , "Target" , nil )
156+ writeAttr ("platform" , "Platform" , func (v string ) (string , bool ) {
157+ return tryParseValue (v , func (v string ) (string , error ) {
158+ var pp []string
159+ for _ , v := range strings .Split (v , "," ) {
160+ p , err := platforms .Parse (v )
161+ if err != nil {
162+ return "" , err
163+ }
164+ pp = append (pp , platforms .FormatAll (platforms .Normalize (p )))
165+ }
166+ return strings .Join (pp , ", " ), nil
167+ }), true
168+ })
169+ writeAttr ("build-arg:BUILDKIT_CONTEXT_KEEP_GIT_DIR" , "Keep Git Dir" , func (v string ) (string , bool ) {
170+ return tryParseValue (v , func (v string ) (string , error ) {
171+ b , err := strconv .ParseBool (v )
172+ if err != nil {
173+ return "" , err
174+ }
175+ return strconv .FormatBool (b ), nil
176+ }), true
177+ })
178+
179+ tw .Flush ()
180+
181+ fmt .Fprintln (dockerCli .Out ())
182+
183+ printTable (dockerCli .Out (), attrs , "context:" , "Named Context" )
81184
185+ tw = tabwriter .NewWriter (dockerCli .Out (), 1 , 8 , 1 , '\t' , 0 )
186+
187+ fmt .Fprintf (tw , "Started:\t %s\n " , rec .CreatedAt .AsTime ().Format ("2006-01-02 15:04:05" ))
188+ var duration time.Duration
189+ var status string
190+ if rec .CompletedAt != nil {
191+ duration = rec .CompletedAt .AsTime ().Sub (rec .CreatedAt .AsTime ())
192+ } else {
193+ duration = rec .currentTimestamp .Sub (rec .CreatedAt .AsTime ())
194+ status = " (running)"
195+ }
196+ fmt .Fprintf (tw , "Duration:\t %s%s\n " , formatDuration (duration ), status )
197+ if rec .Error != nil {
198+ if codes .Code (rec .Error .Code ) == codes .Canceled {
199+ fmt .Fprintf (tw , "Status:\t Canceled\n " )
200+ } else {
201+ fmt .Fprintf (tw , "Error:\t %s %s\n " , codes .Code (rec .Error .Code ).String (), rec .Error .Message )
202+ }
203+ }
204+ fmt .Fprintf (tw , "Build Steps:\t %d/%d (%.0f%% cached)\n " , rec .NumCompletedSteps , rec .NumTotalSteps , float64 (rec .NumCachedSteps )/ float64 (rec .NumTotalSteps )* 100 )
205+ tw .Flush ()
206+
207+ fmt .Fprintln (dockerCli .Out ())
208+
209+ tw = tabwriter .NewWriter (dockerCli .Out (), 1 , 8 , 1 , '\t' , 0 )
210+
211+ writeAttr ("force-network-mode" , "Network" , nil )
212+ writeAttr ("hostname" , "Hostname" , nil )
213+ writeAttr ("add-hosts" , "Extra Hosts" , func (v string ) (string , bool ) {
214+ return tryParseValue (v , func (v string ) (string , error ) {
215+ fields , err := csvvalue .Fields (v , nil )
216+ if err != nil {
217+ return "" , err
218+ }
219+ return strings .Join (fields , ", " ), nil
220+ }), true
221+ })
222+ writeAttr ("cgroup-parent" , "Cgroup Parent" , nil )
223+ writeAttr ("image-resolve-mode" , "Image Resolve Mode" , nil )
224+ writeAttr ("multi-platform" , "Force Multi-Platform" , nil )
225+ writeAttr ("build-arg:BUILDKIT_MULTI_PLATFORM" , "Force Multi-Platform" , nil )
226+ writeAttr ("no-cache" , "Disable Cache" , func (v string ) (string , bool ) {
227+ if v == "" {
228+ return "true" , true
229+ }
230+ return v , true
231+ })
232+ writeAttr ("shm-size" , "Shm Size" , nil )
233+ writeAttr ("ulimit" , "Resource Limits" , nil )
234+ writeAttr ("build-arg:BUILDKIT_CACHE_MOUNT_NS" , "Cache Mount Namespace" , nil )
235+ writeAttr ("build-arg:BUILDKIT_DOCKERFILE_CHECK" , "Dockerfile Check Config" , nil )
236+ writeAttr ("build-arg:SOURCE_DATE_EPOCH" , "Source Date Epoch" , nil )
237+ writeAttr ("build-arg:SANDBOX_HOSTNAME" , "Sandbox Hostname" , nil )
238+
239+ var unusedAttrs []string
240+ for k := range attrs {
241+ if strings .HasPrefix (k , "vcs:" ) || strings .HasPrefix (k , "build-arg:" ) || strings .HasPrefix (k , "label:" ) || strings .HasPrefix (k , "context:" ) {
242+ continue
243+ }
244+ unusedAttrs = append (unusedAttrs , k )
245+ }
246+ slices .Sort (unusedAttrs )
247+
248+ for _ , k := range unusedAttrs {
249+ fmt .Fprintf (tw , "%s:\t %s\n " , k , attrs [k ])
250+ }
251+
252+ tw .Flush ()
253+
254+ fmt .Fprintln (dockerCli .Out ())
255+
256+ printTable (dockerCli .Out (), attrs , "build-arg:" , "Build Arg" )
257+ printTable (dockerCli .Out (), attrs , "label:" , "Label" )
258+
259+ // exporters (image)
82260 // commands
83261 // error
84262 // materials
@@ -107,3 +285,33 @@ func inspectCmd(dockerCli command.Cli, rootOpts RootOptions) *cobra.Command {
107285
108286 return cmd
109287}
288+
289+ func tryParseValue (s string , f func (string ) (string , error )) string {
290+ v , err := f (s )
291+ if err != nil {
292+ return fmt .Sprintf ("%s (%v)" , s , err )
293+ }
294+ return v
295+ }
296+
297+ func printTable (w io.Writer , attrs map [string ]string , prefix , title string ) {
298+ var keys []string
299+ for k := range attrs {
300+ if strings .HasPrefix (k , prefix ) {
301+ keys = append (keys , strings .TrimPrefix (k , prefix ))
302+ }
303+ }
304+ slices .Sort (keys )
305+
306+ if len (keys ) == 0 {
307+ return
308+ }
309+
310+ tw := tabwriter .NewWriter (w , 1 , 8 , 1 , '\t' , 0 )
311+ fmt .Fprintf (tw , "%s\t VALUE\n " , strings .ToUpper (title ))
312+ for _ , k := range keys {
313+ fmt .Fprintf (tw , "%s\t %s\n " , k , attrs [prefix + k ])
314+ }
315+ tw .Flush ()
316+ fmt .Fprintln (w )
317+ }
0 commit comments