github.com/khulnasoft/cli@v0.0.0-20240402070845-01bcad7beefa/cli/command/image/formatter_history.go (about) 1 package image 2 3 import ( 4 "strconv" 5 "strings" 6 "time" 7 8 "github.com/docker/docker/api/types/image" 9 "github.com/docker/docker/pkg/stringid" 10 units "github.com/khulnasoft-lab/go-units" 11 "github.com/khulnasoft/cli/cli/command/formatter" 12 ) 13 14 const ( 15 defaultHistoryTableFormat = "table {{.ID}}\t{{.CreatedSince}}\t{{.CreatedBy}}\t{{.Size}}\t{{.Comment}}" 16 nonHumanHistoryTableFormat = "table {{.ID}}\t{{.CreatedAt}}\t{{.CreatedBy}}\t{{.Size}}\t{{.Comment}}" 17 18 historyIDHeader = "IMAGE" 19 createdByHeader = "CREATED BY" 20 commentHeader = "COMMENT" 21 ) 22 23 // NewHistoryFormat returns a format for rendering an HistoryContext 24 func NewHistoryFormat(source string, quiet bool, human bool) formatter.Format { 25 if source == formatter.TableFormatKey { 26 switch { 27 case quiet: 28 return formatter.DefaultQuietFormat 29 case !human: 30 return nonHumanHistoryTableFormat 31 default: 32 return defaultHistoryTableFormat 33 } 34 } 35 36 return formatter.Format(source) 37 } 38 39 // HistoryWrite writes the context 40 func HistoryWrite(ctx formatter.Context, human bool, histories []image.HistoryResponseItem) error { 41 render := func(format func(subContext formatter.SubContext) error) error { 42 for _, history := range histories { 43 historyCtx := &historyContext{trunc: ctx.Trunc, h: history, human: human} 44 if err := format(historyCtx); err != nil { 45 return err 46 } 47 } 48 return nil 49 } 50 historyCtx := &historyContext{} 51 historyCtx.Header = formatter.SubHeaderContext{ 52 "ID": historyIDHeader, 53 "CreatedSince": formatter.CreatedSinceHeader, 54 "CreatedAt": formatter.CreatedAtHeader, 55 "CreatedBy": createdByHeader, 56 "Size": formatter.SizeHeader, 57 "Comment": commentHeader, 58 } 59 return ctx.Write(historyCtx, render) 60 } 61 62 type historyContext struct { 63 formatter.HeaderContext 64 trunc bool 65 human bool 66 h image.HistoryResponseItem 67 } 68 69 func (c *historyContext) MarshalJSON() ([]byte, error) { 70 return formatter.MarshalJSON(c) 71 } 72 73 func (c *historyContext) ID() string { 74 if c.trunc { 75 return stringid.TruncateID(c.h.ID) 76 } 77 return c.h.ID 78 } 79 80 func (c *historyContext) CreatedAt() string { 81 return time.Unix(c.h.Created, 0).Format(time.RFC3339) 82 } 83 84 // epoch is the time before which created-at dates are not displayed with human units. 85 var epoch = time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC).Unix() 86 87 func (c *historyContext) CreatedSince() string { 88 if !c.human { 89 return c.CreatedAt() 90 } 91 if c.h.Created <= epoch { 92 return "N/A" 93 } 94 created := units.HumanDuration(time.Now().UTC().Sub(time.Unix(c.h.Created, 0))) 95 return created + " ago" 96 } 97 98 func (c *historyContext) CreatedBy() string { 99 createdBy := strings.ReplaceAll(c.h.CreatedBy, "\t", " ") 100 if c.trunc { 101 return formatter.Ellipsis(createdBy, 45) 102 } 103 return createdBy 104 } 105 106 func (c *historyContext) Size() string { 107 if c.human { 108 return units.HumanSizeWithPrecision(float64(c.h.Size), 3) 109 } 110 return strconv.FormatInt(c.h.Size, 10) 111 } 112 113 func (c *historyContext) Comment() string { 114 return c.h.Comment 115 }