@@ -14,28 +14,14 @@ import (
1414 "github.com/moby/term"
1515)
1616
17+ var timeNow = time .Now // For overriding in tests.
18+
1719// RFC3339NanoFixed is time.RFC3339Nano with nanoseconds padded using zeros to
1820// ensure the formatted time isalways the same number of characters.
1921const RFC3339NanoFixed = "2006-01-02T15:04:05.000000000Z07:00"
2022
21- // JSONProgress describes a progress message in a JSON stream.
22- type JSONProgress struct {
23- jsonstream.Progress
24-
25- // terminalFd is the fd of the current terminal, if any. It is used
26- // to get the terminal width.
27- terminalFd uintptr
28-
29- // nowFunc is used to override the current time in tests.
30- nowFunc func () time.Time
31-
32- // winSize is used to override the terminal width in tests.
33- winSize int
34- }
35-
36- func (p * JSONProgress ) String () string {
23+ func RenderTUIProgress (p jsonstream.Progress , width uint16 ) string {
3724 var (
38- width = p .width ()
3925 pbBox string
4026 numbersBox string
4127 )
@@ -89,7 +75,7 @@ func (p *JSONProgress) String() string {
8975 var timeLeftBox string
9076 if width > 50 {
9177 if p .Current > 0 && p .Start > 0 && percentage < 50 {
92- fromStart := p . now ().Sub (time .Unix (p .Start , 0 ))
78+ fromStart := timeNow (). UTC ().Sub (time .Unix (p .Start , 0 ))
9379 perEntry := fromStart / time .Duration (p .Current )
9480 left := time .Duration (p .Total - p .Current ) * perEntry
9581 timeLeftBox = " " + left .Round (time .Second ).String ()
@@ -98,40 +84,6 @@ func (p *JSONProgress) String() string {
9884 return pbBox + numbersBox + timeLeftBox
9985}
10086
101- // now returns the current time in UTC, but can be overridden in tests
102- // by setting JSONProgress.nowFunc to a custom function.
103- func (p * JSONProgress ) now () time.Time {
104- if p .nowFunc != nil {
105- return p .nowFunc ()
106- }
107- return time .Now ().UTC ()
108- }
109-
110- // width returns the current terminal's width, but can be overridden
111- // in tests by setting JSONProgress.winSize to a non-zero value.
112- func (p * JSONProgress ) width () int {
113- if p .winSize != 0 {
114- return p .winSize
115- }
116- ws , err := term .GetWinsize (p .terminalFd )
117- if err == nil {
118- return int (ws .Width )
119- }
120- return 200
121- }
122-
123- // JSONMessage defines a message struct. It describes
124- // the created time, where it from, status, ID of the
125- // message. It's used for docker events.
126- type JSONMessage struct {
127- Stream string `json:"stream,omitempty"`
128- Status string `json:"status,omitempty"`
129- Progress * JSONProgress `json:"progressDetail,omitempty"`
130- ID string `json:"id,omitempty"`
131- Error * jsonstream.Error `json:"errorDetail,omitempty"`
132- Aux * json.RawMessage `json:"aux,omitempty"` // Aux contains out-of-band data, such as digests for push signing and image id after building.
133- }
134-
13587// We can probably use [aec.EmptyBuilder] for managing the output, but
13688// currently we're doing it all manually, so defining some consts for
13789// the basics we use.
@@ -164,7 +116,7 @@ func cursorDown(out io.Writer, l uint) {
164116// Display prints the JSONMessage to out. If isTerminal is true, it erases
165117// the entire current line when displaying the progressbar. It returns an
166118// error if the [JSONMessage.Error] field is non-nil.
167- func (jm * JSONMessage ) Display ( out io.Writer , isTerminal bool ) error {
119+ func Display (jm jsonstream. Message , out io.Writer , isTerminal bool , width uint16 ) error {
168120 if jm .Error != nil {
169121 return jm .Error
170122 }
@@ -173,14 +125,17 @@ func (jm *JSONMessage) Display(out io.Writer, isTerminal bool) error {
173125 clearLine (out )
174126 endl = "\r "
175127 _ , _ = fmt .Fprint (out , endl )
176- } else if jm .Progress != nil && jm .Progress .String () != "" { // disable progressbar in non-terminal
128+ } else if jm .Progress != nil && ( jm .Progress .Current > 0 || jm . Progress . Total > 0 ) { // disable progressbar in non-terminal
177129 return nil
178130 }
179131 if jm .ID != "" {
180132 _ , _ = fmt .Fprintf (out , "%s: " , jm .ID )
181133 }
182134 if jm .Progress != nil && isTerminal {
183- _ , _ = fmt .Fprintf (out , "%s %s%s" , jm .Status , jm .Progress .String (), endl )
135+ if width == 0 {
136+ width = 200
137+ }
138+ _ , _ = fmt .Fprintf (out , "%s %s%s" , jm .Status , RenderTUIProgress (* jm .Progress , width ), endl )
184139 } else if jm .Stream != "" {
185140 _ , _ = fmt .Fprintf (out , "%s%s" , jm .Stream , endl )
186141 } else {
@@ -189,16 +144,16 @@ func (jm *JSONMessage) Display(out io.Writer, isTerminal bool) error {
189144 return nil
190145}
191146
192- type JSONMessagesStream iter.Seq2 [JSONMessage , error ]
147+ type JSONMessagesStream iter.Seq2 [jsonstream. Message , error ]
193148
194149// DisplayJSONMessagesStream reads a JSON message stream from in, and writes
195150// each [JSONMessage] to out.
196151// see DisplayJSONMessages for details
197- func DisplayJSONMessagesStream (in io.Reader , out io.Writer , terminalFd uintptr , isTerminal bool , auxCallback func (JSONMessage )) error {
152+ func DisplayJSONMessagesStream (in io.Reader , out io.Writer , terminalFd uintptr , isTerminal bool , auxCallback func (jsonstream. Message )) error {
198153 dec := json .NewDecoder (in )
199- var f JSONMessagesStream = func (yield func (JSONMessage , error ) bool ) {
154+ var f JSONMessagesStream = func (yield func (jsonstream. Message , error ) bool ) {
200155 for {
201- var jm JSONMessage
156+ var jm jsonstream. Message
202157 err := dec .Decode (& jm )
203158 if errors .Is (err , io .EOF ) {
204159 break
@@ -228,8 +183,15 @@ func DisplayJSONMessagesStream(in io.Reader, out io.Writer, terminalFd uintptr,
228183// - auxCallback allows handling the [JSONMessage.Aux] field. It is
229184// called if a JSONMessage contains an Aux field, in which case
230185// DisplayJSONMessagesStream does not present the JSONMessage.
231- func DisplayJSONMessages (messages JSONMessagesStream , out io.Writer , terminalFd uintptr , isTerminal bool , auxCallback func (JSONMessage )) error {
186+ func DisplayJSONMessages (messages JSONMessagesStream , out io.Writer , terminalFd uintptr , isTerminal bool , auxCallback func (jsonstream. Message )) error {
232187 ids := make (map [string ]uint )
188+ var width uint16 = 200
189+ if isTerminal {
190+ ws , err := term .GetWinsize (terminalFd )
191+ if err == nil {
192+ width = ws .Width
193+ }
194+ }
233195
234196 for jm , err := range messages {
235197 var diff uint
@@ -244,9 +206,6 @@ func DisplayJSONMessages(messages JSONMessagesStream, out io.Writer, terminalFd
244206 continue
245207 }
246208
247- if jm .Progress != nil {
248- jm .Progress .terminalFd = terminalFd
249- }
250209 if jm .ID != "" && jm .Progress != nil {
251210 line , ok := ids [jm .ID ]
252211 if ! ok {
@@ -274,7 +233,7 @@ func DisplayJSONMessages(messages JSONMessagesStream, out io.Writer, terminalFd
274233 // with multiple tags).
275234 ids = make (map [string ]uint )
276235 }
277- err := jm . Display (out , isTerminal )
236+ err := Display (jm , out , isTerminal , width )
278237 if jm .ID != "" && isTerminal {
279238 cursorDown (out , diff )
280239 }
0 commit comments