Просмотр исходного кода

distinguish event (short) status text and details

Signed-off-by: Nicolas De Loof <[email protected]>
Nicolas De Loof 1 месяц назад
Родитель
Сommit
d70bb8cf5e

+ 1 - 0
.gitignore

@@ -3,3 +3,4 @@ bin/
 coverage.out
 covdatafiles/
 .DS_Store
+pkg/e2e/*.tar

+ 1 - 0
go.mod

@@ -63,6 +63,7 @@ require (
 
 require (
 	github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect
+	github.com/BurntSushi/toml v1.4.0 // indirect
 	github.com/Masterminds/semver/v3 v3.4.0 // indirect
 	github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect
 	github.com/aws/aws-sdk-go-v2 v1.30.3 // indirect

+ 2 - 1
go.sum

@@ -4,8 +4,9 @@ github.com/AlecAivazis/survey/v2 v2.3.7 h1:6I/u8FvytdGsgonrYsVn2t8t4QiRnh6QSTqkk
 github.com/AlecAivazis/survey/v2 v2.3.7/go.mod h1:xUTIdE4KCOIjsBAE1JYsUPoCqYdZ1reCfTwbto0Fduo=
 github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEKWjV8V+WSxDXJ4NFATAsZjh8iIbsQIg=
 github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
-github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0=
+github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
 github.com/DefangLabs/secret-detector v0.0.0-20250403165618-22662109213e h1:rd4bOvKmDIx0WeTv9Qz+hghsgyjikFiPrseXHlKepO0=
 github.com/DefangLabs/secret-detector v0.0.0-20250403165618-22662109213e/go.mod h1:blbwPQh4DTlCZEfk1BLU4oMIhLda2U+A840Uag9DsZw=
 github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0=

+ 9 - 13
pkg/compose/commit.go

@@ -41,21 +41,18 @@ func (s *composeService) commit(ctx context.Context, projectName string, options
 	}
 
 	name := getCanonicalContainerName(ctr)
-	msg := fmt.Sprintf("Commit %s", name)
 
 	s.events.On(progress.Event{
-		ID:         name,
-		Text:       msg,
-		Status:     progress.Working,
-		StatusText: "Committing",
+		ID:     name,
+		Status: progress.Working,
+		Text:   progress.StatusCommitting,
 	})
 
 	if s.dryRun {
 		s.events.On(progress.Event{
-			ID:         name,
-			Text:       msg,
-			Status:     progress.Done,
-			StatusText: "Committed",
+			ID:     name,
+			Status: progress.Done,
+			Text:   progress.StatusCommitted,
 		})
 
 		return nil
@@ -73,10 +70,9 @@ func (s *composeService) commit(ctx context.Context, projectName string, options
 	}
 
 	s.events.On(progress.Event{
-		ID:         name,
-		Text:       msg,
-		Status:     progress.Done,
-		StatusText: fmt.Sprintf("Committed as %s", response.ID),
+		ID:     name,
+		Text:   fmt.Sprintf("Committed as %s", response.ID),
+		Status: progress.Done,
 	})
 
 	return nil

+ 12 - 8
pkg/compose/convergence.go

@@ -504,7 +504,9 @@ func (s *composeService) waitDependencies(ctx context.Context, project *types.Pr
 							logrus.Warnf("optional dependency %q failed to start: %s", dep, err.Error())
 							return nil
 						}
-						s.events.On(containerEvents(waitingFor, progress.ErrorEvent)...)
+						s.events.On(containerEvents(waitingFor, func(s string) progress.Event {
+							return progress.ErrorEventf(s, "dependency %s failed to start", dep)
+						})...)
 						return fmt.Errorf("dependency failed to start: %w", err)
 					}
 					if healthy {
@@ -532,7 +534,9 @@ func (s *composeService) waitDependencies(ctx context.Context, project *types.Pr
 						}
 
 						msg := fmt.Sprintf("service %s", messageSuffix)
-						s.events.On(containerReasonEvents(waitingFor, progress.ErrorMessageEvent, msg)...)
+						s.events.On(containerEvents(waitingFor, func(s string) progress.Event {
+							return progress.ErrorEventf(s, "service %s", messageSuffix)
+						})...)
 						return errors.New(msg)
 					}
 				default:
@@ -600,9 +604,9 @@ func (s *composeService) createContainer(ctx context.Context, project *types.Pro
 	if err != nil {
 		if ctx.Err() == nil {
 			s.events.On(progress.Event{
-				ID:         eventName,
-				Status:     progress.Error,
-				StatusText: err.Error(),
+				ID:     eventName,
+				Status: progress.Error,
+				Text:   err.Error(),
 			})
 		}
 		return ctr, err
@@ -619,9 +623,9 @@ func (s *composeService) recreateContainer(ctx context.Context, project *types.P
 	defer func() {
 		if err != nil && ctx.Err() == nil {
 			s.events.On(progress.Event{
-				ID:         eventName,
-				Status:     progress.Error,
-				StatusText: err.Error(),
+				ID:     eventName,
+				Status: progress.Error,
+				Text:   err.Error(),
 			})
 		}
 	}()

+ 10 - 10
pkg/compose/cp.go

@@ -86,24 +86,24 @@ func (s *composeService) copy(ctx context.Context, projectName string, options a
 			name := getCanonicalContainerName(ctr)
 			var msg string
 			if direction == fromService {
-				msg = fmt.Sprintf("copy %s:%s to %s", name, srcPath, dstPath)
+				msg = fmt.Sprintf("%s:%s to %s", name, srcPath, dstPath)
 			} else {
-				msg = fmt.Sprintf("copy %s to %s:%s", srcPath, name, dstPath)
+				msg = fmt.Sprintf("%s to %s:%s", srcPath, name, dstPath)
 			}
 			s.events.On(progress.Event{
-				ID:         name,
-				Text:       msg,
-				Status:     progress.Working,
-				StatusText: "Copying",
+				ID:      name,
+				Text:    progress.StatusCopying,
+				Details: msg,
+				Status:  progress.Working,
 			})
 			if err := copyFunc(ctx, ctr.ID, srcPath, dstPath, options); err != nil {
 				return err
 			}
 			s.events.On(progress.Event{
-				ID:         name,
-				Text:       msg,
-				Status:     progress.Done,
-				StatusText: "Copied",
+				ID:      name,
+				Text:    progress.StatusCopied,
+				Details: msg,
+				Status:  progress.Done,
 			})
 			return nil
 		})

+ 2 - 2
pkg/compose/create.go

@@ -1398,7 +1398,7 @@ func (s *composeService) resolveOrCreateNetwork(ctx context.Context, project *ty
 
 	resp, err := s.apiClient().NetworkCreate(ctx, n.Name, createOpts)
 	if err != nil {
-		s.events.On(progress.ErrorEvent(networkEventName))
+		s.events.On(progress.ErrorEvent(networkEventName, err.Error()))
 		return "", fmt.Errorf("failed to create network %s: %w", n.Name, err)
 	}
 	s.events.On(progress.CreatedEvent(networkEventName))
@@ -1632,7 +1632,7 @@ func (s *composeService) createVolume(ctx context.Context, volume types.VolumeCo
 		DriverOpts: volume.DriverOpts,
 	})
 	if err != nil {
-		s.events.On(progress.ErrorEvent(eventName))
+		s.events.On(progress.ErrorEvent(eventName, err.Error()))
 		return err
 	}
 	s.events.On(progress.CreatedEvent(eventName))

+ 3 - 3
pkg/compose/down.go

@@ -235,7 +235,7 @@ func (s *composeService) removeNetwork(ctx context.Context, composeNetworkName s
 			if errdefs.IsNotFound(err) {
 				continue
 			}
-			s.events.On(progress.ErrorEvent(eventName))
+			s.events.On(progress.ErrorEvent(eventName, err.Error()))
 			return fmt.Errorf("failed to remove network %s: %w", name, err)
 		}
 		s.events.On(progress.RemovedEvent(eventName))
@@ -317,7 +317,7 @@ func (s *composeService) stopContainer(ctx context.Context, service *types.Servi
 	timeoutInSecond := utils.DurationSecondToInt(timeout)
 	err := s.apiClient().ContainerStop(ctx, ctr.ID, containerType.StopOptions{Timeout: timeoutInSecond})
 	if err != nil {
-		s.events.On(progress.ErrorMessageEvent(eventName, "Error while Stopping"))
+		s.events.On(progress.ErrorEvent(eventName, "Error while Stopping"))
 		return err
 	}
 	s.events.On(progress.StoppedEvent(eventName))
@@ -360,7 +360,7 @@ func (s *composeService) stopAndRemoveContainer(ctx context.Context, ctr contain
 		RemoveVolumes: volumes,
 	})
 	if err != nil && !errdefs.IsNotFound(err) && !errdefs.IsConflict(err) {
-		s.events.On(progress.ErrorMessageEvent(eventName, "Error while Removing"))
+		s.events.On(progress.ErrorEvent(eventName, "Error while Removing"))
 		return err
 	}
 	s.events.On(progress.RemovedEvent(eventName))

+ 7 - 16
pkg/compose/export.go

@@ -51,13 +51,10 @@ func (s *composeService) export(ctx context.Context, projectName string, options
 	}
 
 	name := getCanonicalContainerName(container)
-	msg := fmt.Sprintf("export %s to %s", name, options.Output)
-
 	s.events.On(progress.Event{
-		ID:         name,
-		Text:       msg,
-		Status:     progress.Working,
-		StatusText: "Exporting",
+		ID:     name,
+		Text:   progress.StatusExporting,
+		Status: progress.Working,
 	})
 
 	responseBody, err := s.apiClient().ContainerExport(ctx, container.ID)
@@ -67,12 +64,7 @@ func (s *composeService) export(ctx context.Context, projectName string, options
 
 	defer func() {
 		if err := responseBody.Close(); err != nil {
-			s.events.On(progress.Event{
-				ID:         name,
-				Text:       msg,
-				Status:     progress.Error,
-				StatusText: fmt.Sprintf("Failed to close response body: %v", err),
-			})
+			s.events.On(progress.ErrorEventf(name, "Failed to close response body: %s", err.Error()))
 		}
 	}()
 
@@ -93,10 +85,9 @@ func (s *composeService) export(ctx context.Context, projectName string, options
 	}
 
 	s.events.On(progress.Event{
-		ID:         name,
-		Text:       msg,
-		Status:     progress.Done,
-		StatusText: "Exported",
+		ID:     name,
+		Text:   progress.StatusExported,
+		Status: progress.Done,
 	})
 
 	return nil

+ 1 - 1
pkg/compose/kill.go

@@ -66,7 +66,7 @@ func (s *composeService) kill(ctx context.Context, projectName string, options a
 			s.events.On(progress.KillingEvent(eventName))
 			err := s.apiClient().ContainerKill(ctx, ctr.ID, options.Signal)
 			if err != nil {
-				s.events.On(progress.ErrorMessageEvent(eventName, "Error while Killing"))
+				s.events.On(progress.ErrorEvent(eventName, "Error while Killing"))
 				return err
 			}
 			s.events.On(progress.KilledEvent(eventName))

+ 10 - 11
pkg/compose/model.go

@@ -132,31 +132,30 @@ func (m *modelAPI) PullModel(ctx context.Context, model types.ModelConfig, quiet
 
 		if !quietPull {
 			events.On(progress.Event{
-				ID:         model.Name,
-				Status:     progress.Working,
-				Text:       "Pulling",
-				StatusText: msg,
+				ID:     model.Name,
+				Status: progress.Working,
+				Text:   progress.StatusPulling,
 			})
 		}
 	}
 
 	err = cmd.Wait()
 	if err != nil {
-		events.On(progress.ErrorMessageEvent(model.Name, err.Error()))
+		events.On(progress.ErrorEvent(model.Name, err.Error()))
 	}
 	events.On(progress.Event{
-		ID:         model.Name,
-		Status:     progress.Working,
-		StatusText: "Pulled",
+		ID:     model.Name,
+		Status: progress.Working,
+		Text:   progress.StatusPulled,
 	})
 	return err
 }
 
 func (m *modelAPI) ConfigureModel(ctx context.Context, config types.ModelConfig, events progress.EventProcessor) error {
 	events.On(progress.Event{
-		ID:         config.Name,
-		Status:     progress.Working,
-		StatusText: "Configuring",
+		ID:     config.Name,
+		Status: progress.Working,
+		Text:   "Configuring",
 	})
 	// configure [--context-size=<n>] MODEL [-- <runtime-flags...>]
 	args := []string{"configure"}

+ 1 - 1
pkg/compose/plugins.go

@@ -143,7 +143,7 @@ func (s *composeService) executePlugin(cmd *exec.Cmd, command string, service ty
 
 	err = cmd.Wait()
 	if err != nil {
-		s.events.On(progress.ErrorMessageEvent(service.Name, err.Error()))
+		s.events.On(progress.ErrorEvent(service.Name, err.Error()))
 		return nil, fmt.Errorf("failed to %s service provider: %s", action, err.Error())
 	}
 	switch command {

+ 28 - 35
pkg/compose/pull.go

@@ -68,9 +68,10 @@ func (s *composeService) pull(ctx context.Context, project *types.Project, opts
 	for name, service := range project.Services {
 		if service.Image == "" {
 			s.events.On(progress.Event{
-				ID:     name,
-				Status: progress.Done,
-				Text:   "Skipped - No image to be pulled",
+				ID:      name,
+				Status:  progress.Done,
+				Text:    "Skipped",
+				Details: "No image to be pulled",
 			})
 			continue
 		}
@@ -86,9 +87,10 @@ func (s *composeService) pull(ctx context.Context, project *types.Project, opts
 		case types.PullPolicyMissing, types.PullPolicyIfNotPresent:
 			if imageAlreadyPresent(service.Image, images) {
 				s.events.On(progress.Event{
-					ID:     "Image " + service.Image,
-					Status: progress.Done,
-					Text:   "Skipped - Image is already present locally",
+					ID:      "Image " + service.Image,
+					Status:  progress.Done,
+					Text:    "Skipped",
+					Details: "Image is already present locally",
 				})
 				continue
 			}
@@ -96,9 +98,10 @@ func (s *composeService) pull(ctx context.Context, project *types.Project, opts
 
 		if service.Build != nil && opts.IgnoreBuildable {
 			s.events.On(progress.Event{
-				ID:     "Image " + service.Image,
-				Status: progress.Done,
-				Text:   "Skipped - Image can be built",
+				ID:      "Image " + service.Image,
+				Status:  progress.Done,
+				Text:    "Skipped",
+				Details: "Image can be built",
 			})
 			continue
 		}
@@ -119,11 +122,8 @@ func (s *composeService) pull(ctx context.Context, project *types.Project, opts
 				}
 				if !opts.IgnoreFailures && service.Build == nil {
 					if s.dryRun {
-						s.events.On(progress.Event{
-							ID:     "Image " + service.Image,
-							Status: progress.Error,
-							Text:   fmt.Sprintf(" - Pull error for image: %s", service.Image),
-						})
+						s.events.On(progress.ErrorEventf("Image "+service.Image,
+							"error pulling image: %s", service.Image))
 					}
 					// fail fast if image can't be pulled nor built
 					return err
@@ -197,9 +197,9 @@ func (s *composeService) pullServiceImage(ctx context.Context, service types.Ser
 
 	if ctx.Err() != nil {
 		s.events.On(progress.Event{
-			ID:         resource,
-			Status:     progress.Warning,
-			StatusText: "Interrupted",
+			ID:     resource,
+			Status: progress.Warning,
+			Text:   "Interrupted",
 		})
 		return "", nil
 	}
@@ -208,21 +208,15 @@ func (s *composeService) pullServiceImage(ctx context.Context, service types.Ser
 	// then the status should be warning instead of error
 	if err != nil && service.Build != nil {
 		s.events.On(progress.Event{
-			ID:         resource,
-			Status:     progress.Warning,
-			Text:       "Warning",
-			StatusText: getUnwrappedErrorMessage(err),
+			ID:     resource,
+			Status: progress.Warning,
+			Text:   getUnwrappedErrorMessage(err),
 		})
 		return "", err
 	}
 
 	if err != nil {
-		s.events.On(progress.Event{
-			ID:         resource,
-			Status:     progress.Error,
-			Text:       "Error",
-			StatusText: getUnwrappedErrorMessage(err),
-		})
+		s.events.On(progress.ErrorEvent(resource, getUnwrappedErrorMessage(err)))
 		return "", err
 	}
 
@@ -442,13 +436,12 @@ func toPullProgressEvent(parent string, jm jsonmessage.JSONMessage, events progr
 	}
 
 	events.On(progress.Event{
-		ID:         jm.ID,
-		ParentID:   parent,
-		Current:    current,
-		Total:      total,
-		Percent:    percent,
-		Text:       jm.Status,
-		Status:     status,
-		StatusText: text,
+		ID:       jm.ID,
+		ParentID: parent,
+		Current:  current,
+		Total:    total,
+		Percent:  percent,
+		Status:   status,
+		Text:     text,
 	})
 }

+ 7 - 8
pkg/compose/push.go

@@ -161,14 +161,13 @@ func toPushProgressEvent(prefix string, jm jsonmessage.JSONMessage, events progr
 	}
 
 	events.On(progress.Event{
-		ParentID:   prefix,
-		ID:         jm.ID,
-		Text:       jm.Status,
-		Status:     status,
-		Current:    current,
-		Total:      total,
-		Percent:    percent,
-		StatusText: text,
+		ParentID: prefix,
+		ID:       jm.ID,
+		Text:     text,
+		Status:   status,
+		Current:  current,
+		Total:    total,
+		Percent:  percent,
 	})
 }
 

+ 9 - 9
pkg/e2e/build_test.go

@@ -536,7 +536,7 @@ func TestBuildDependsOn(t *testing.T) {
 
 	res := c.RunDockerComposeCmd(t, "-f", "fixtures/build-dependencies/compose-depends_on.yaml", "--progress=plain", "up", "test2")
 	out := res.Combined()
-	assert.Check(t, strings.Contains(out, "test1  Built"))
+	assert.Check(t, strings.Contains(out, "test1 Built"))
 }
 
 func TestBuildSubset(t *testing.T) {
@@ -548,7 +548,7 @@ func TestBuildSubset(t *testing.T) {
 
 	res := c.RunDockerComposeCmd(t, "-f", "fixtures/build-test/subset/compose.yaml", "build", "main")
 	out := res.Combined()
-	assert.Check(t, strings.Contains(out, "main  Built"))
+	assert.Check(t, strings.Contains(out, "main Built"))
 }
 
 func TestBuildDependentImage(t *testing.T) {
@@ -560,11 +560,11 @@ func TestBuildDependentImage(t *testing.T) {
 
 	res := c.RunDockerComposeCmd(t, "-f", "fixtures/build-test/dependencies/compose.yaml", "build", "firstbuild")
 	out := res.Combined()
-	assert.Check(t, strings.Contains(out, "firstbuild  Built"))
+	assert.Check(t, strings.Contains(out, "firstbuild Built"))
 
 	res = c.RunDockerComposeCmd(t, "-f", "fixtures/build-test/dependencies/compose.yaml", "build", "secondbuild")
 	out = res.Combined()
-	assert.Check(t, strings.Contains(out, "secondbuild  Built"))
+	assert.Check(t, strings.Contains(out, "secondbuild Built"))
 }
 
 func TestBuildSubDependencies(t *testing.T) {
@@ -576,11 +576,11 @@ func TestBuildSubDependencies(t *testing.T) {
 
 	res := c.RunDockerComposeCmd(t, "-f", "fixtures/build-test/sub-dependencies/compose.yaml", "build", "main")
 	out := res.Combined()
-	assert.Check(t, strings.Contains(out, "main  Built"))
+	assert.Check(t, strings.Contains(out, "main Built"))
 
 	res = c.RunDockerComposeCmd(t, "-f", "fixtures/build-test/sub-dependencies/compose.yaml", "up", "--build", "main")
 	out = res.Combined()
-	assert.Check(t, strings.Contains(out, "main  Built"))
+	assert.Check(t, strings.Contains(out, "main Built"))
 }
 
 func TestBuildLongOutputLine(t *testing.T) {
@@ -592,11 +592,11 @@ func TestBuildLongOutputLine(t *testing.T) {
 
 	res := c.RunDockerComposeCmd(t, "-f", "fixtures/build-test/long-output-line/compose.yaml", "build", "long-line")
 	out := res.Combined()
-	assert.Check(t, strings.Contains(out, "long-line  Built"))
+	assert.Check(t, strings.Contains(out, "long-line Built"))
 
 	res = c.RunDockerComposeCmd(t, "-f", "fixtures/build-test/long-output-line/compose.yaml", "up", "--build", "long-line")
 	out = res.Combined()
-	assert.Check(t, strings.Contains(out, "long-line  Built"))
+	assert.Check(t, strings.Contains(out, "long-line Built"))
 }
 
 func TestBuildDependentImageWithProfile(t *testing.T) {
@@ -608,7 +608,7 @@ func TestBuildDependentImageWithProfile(t *testing.T) {
 
 	res := c.RunDockerComposeCmd(t, "-f", "fixtures/build-test/profiles/compose.yaml", "build", "secret-build-test")
 	out := res.Combined()
-	assert.Check(t, strings.Contains(out, "secret-build-test  Built"))
+	assert.Check(t, strings.Contains(out, "secret-build-test Built"))
 }
 
 func TestBuildTLS(t *testing.T) {

+ 2 - 2
pkg/e2e/compose_run_test.go

@@ -187,8 +187,8 @@ func TestLocalComposeRun(t *testing.T) {
 		res.Assert(t, icmd.Success)
 
 		res = c.RunDockerComposeCmd(t, "-f", "./fixtures/run-test/pull.yaml", "run", "--pull", "always", "backend")
-		assert.Assert(t, strings.Contains(res.Combined(), "Image nginx  Pulling"), res.Combined())
-		assert.Assert(t, strings.Contains(res.Combined(), "Image nginx  Pulled"), res.Combined())
+		assert.Assert(t, strings.Contains(res.Combined(), "Image nginx Pulling"), res.Combined())
+		assert.Assert(t, strings.Contains(res.Combined(), "Image nginx Pulled"), res.Combined())
 	})
 
 	t.Run("compose run --env-from-file", func(t *testing.T) {

+ 6 - 6
pkg/e2e/networks_test.go

@@ -213,10 +213,10 @@ func TestNetworkRecreate(t *testing.T) {
 	err := res.Stderr()
 	fmt.Println(err)
 	res.Assert(t, icmd.Expected{Err: `
- Container network_recreate-web-1  Stopped
- Network network_recreate_test  Removed
- Network network_recreate_test  Creating
- Network network_recreate_test  Created
- Container network_recreate-web-1  Starting
- Container network_recreate-web-1  Started`})
+ Container network_recreate-web-1 Stopped 
+ Network network_recreate_test Removed 
+ Network network_recreate_test Creating 
+ Network network_recreate_test Created 
+ Container network_recreate-web-1 Starting 
+ Container network_recreate-web-1 Started`})
 }

+ 1 - 1
pkg/e2e/ps_test.go

@@ -38,7 +38,7 @@ func TestPs(t *testing.T) {
 		_ = c.RunDockerComposeCmd(t, "--project-name", projectName, "down")
 	})
 
-	assert.Contains(t, res.Combined(), "Container e2e-ps-busybox-1  Started", res.Combined())
+	assert.Contains(t, res.Combined(), "Container e2e-ps-busybox-1 Started", res.Combined())
 
 	t.Run("table", func(t *testing.T) {
 		res = c.RunDockerComposeCmd(t, "-f", "./fixtures/ps-test/compose.yaml", "--project-name", projectName, "ps")

+ 7 - 7
pkg/e2e/pull_test.go

@@ -34,15 +34,15 @@ func TestComposePull(t *testing.T) {
 		res := c.RunDockerComposeCmd(t, "--project-directory", "fixtures/compose-pull/simple", "pull")
 		output := res.Combined()
 
-		assert.Assert(t, strings.Contains(output, "Image alpine:3.14  Pulled"))
-		assert.Assert(t, strings.Contains(output, "Image alpine:3.15  Pulled"))
+		assert.Assert(t, strings.Contains(output, "Image alpine:3.14 Pulled"))
+		assert.Assert(t, strings.Contains(output, "Image alpine:3.15 Pulled"))
 
 		// verify default policy is 'always' for pull command
 		res = c.RunDockerComposeCmd(t, "--project-directory", "fixtures/compose-pull/simple", "pull")
 		output = res.Combined()
 
-		assert.Assert(t, strings.Contains(output, "Image alpine:3.14  Pulled"))
-		assert.Assert(t, strings.Contains(output, "Image alpine:3.15  Pulled"))
+		assert.Assert(t, strings.Contains(output, "Image alpine:3.14 Pulled"))
+		assert.Assert(t, strings.Contains(output, "Image alpine:3.15 Pulled"))
 	})
 
 	t.Run("Verify skipped pull if image is already present locally", func(t *testing.T) {
@@ -52,16 +52,16 @@ func TestComposePull(t *testing.T) {
 		res := c.RunDockerComposeCmd(t, "--project-directory", "fixtures/compose-pull/image-present-locally", "pull")
 		output := res.Combined()
 
-		assert.Assert(t, strings.Contains(output, "alpine:3.13.12 Skipped - Image is already present locally"))
+		assert.Assert(t, strings.Contains(output, "alpine:3.13.12 Skipped Image is already present locally"))
 		// image with :latest tag gets pulled regardless if pull_policy: missing or if_not_present
-		assert.Assert(t, strings.Contains(output, "alpine:latest  Pulled"))
+		assert.Assert(t, strings.Contains(output, "alpine:latest Pulled"))
 	})
 
 	t.Run("Verify skipped no image to be pulled", func(t *testing.T) {
 		res := c.RunDockerComposeCmd(t, "--project-directory", "fixtures/compose-pull/no-image-name-given", "pull")
 		output := res.Combined()
 
-		assert.Assert(t, strings.Contains(output, "Skipped - No image to be pulled"))
+		assert.Assert(t, strings.Contains(output, "Skipped No image to be pulled"))
 	})
 
 	t.Run("Verify pull failure", func(t *testing.T) {

+ 10 - 10
pkg/e2e/restart_test.go

@@ -42,7 +42,7 @@ func TestRestart(t *testing.T) {
 		c.RunDockerComposeCmd(t, "--project-name", projectName, "down")
 
 		res := c.RunDockerComposeCmd(t, "-f", "./fixtures/restart-test/compose.yaml", "--project-name", projectName, "up", "-d")
-		assert.Assert(t, strings.Contains(res.Combined(), "Container e2e-restart-restart-1  Started"), res.Combined())
+		assert.Assert(t, strings.Contains(res.Combined(), "Container e2e-restart-restart-1 Started"), res.Combined())
 
 		c.WaitForCmdResult(t, c.NewDockerComposeCmd(t, "--project-name", projectName, "ps", "-a", "--format",
 			"json"),
@@ -80,9 +80,9 @@ func TestRestartWithDependencies(t *testing.T) {
 
 	res := c.RunDockerComposeCmd(t, "restart", baseService)
 	out := res.Combined()
-	assert.Assert(t, strings.Contains(out, fmt.Sprintf("Container e2e-restart-deps-%s-1  Restarting", baseService)), out)
-	assert.Assert(t, strings.Contains(out, fmt.Sprintf("Container e2e-restart-deps-%s-1  Healthy", baseService)), out)
-	assert.Assert(t, strings.Contains(out, fmt.Sprintf("Container e2e-restart-deps-%s-1  Started", depWithRestart)), out)
+	assert.Assert(t, strings.Contains(out, fmt.Sprintf("Container e2e-restart-deps-%s-1 Restarting", baseService)), out)
+	assert.Assert(t, strings.Contains(out, fmt.Sprintf("Container e2e-restart-deps-%s-1 Healthy", baseService)), out)
+	assert.Assert(t, strings.Contains(out, fmt.Sprintf("Container e2e-restart-deps-%s-1 Started", depWithRestart)), out)
 	assert.Assert(t, !strings.Contains(out, depNoRestart), out)
 
 	c = NewParallelCLI(t, WithEnv(
@@ -91,11 +91,11 @@ func TestRestartWithDependencies(t *testing.T) {
 	))
 	res = c.RunDockerComposeCmd(t, "-f", "./fixtures/restart-test/compose-depends-on.yaml", "up", "-d")
 	out = res.Combined()
-	assert.Assert(t, strings.Contains(out, fmt.Sprintf("Container e2e-restart-deps-%s-1  Stopped", depWithRestart)), out)
-	assert.Assert(t, strings.Contains(out, fmt.Sprintf("Container e2e-restart-deps-%s-1  Recreated", baseService)), out)
-	assert.Assert(t, strings.Contains(out, fmt.Sprintf("Container e2e-restart-deps-%s-1  Healthy", baseService)), out)
-	assert.Assert(t, strings.Contains(out, fmt.Sprintf("Container e2e-restart-deps-%s-1  Started", depWithRestart)), out)
-	assert.Assert(t, strings.Contains(out, fmt.Sprintf("Container e2e-restart-deps-%s-1  Running", depNoRestart)), out)
+	assert.Assert(t, strings.Contains(out, fmt.Sprintf("Container e2e-restart-deps-%s-1 Stopped", depWithRestart)), out)
+	assert.Assert(t, strings.Contains(out, fmt.Sprintf("Container e2e-restart-deps-%s-1 Recreated", baseService)), out)
+	assert.Assert(t, strings.Contains(out, fmt.Sprintf("Container e2e-restart-deps-%s-1 Healthy", baseService)), out)
+	assert.Assert(t, strings.Contains(out, fmt.Sprintf("Container e2e-restart-deps-%s-1 Started", depWithRestart)), out)
+	assert.Assert(t, strings.Contains(out, fmt.Sprintf("Container e2e-restart-deps-%s-1 Running", depNoRestart)), out)
 }
 
 func TestRestartWithProfiles(t *testing.T) {
@@ -111,5 +111,5 @@ func TestRestartWithProfiles(t *testing.T) {
 
 	res := c.RunDockerComposeCmd(t, "restart", "test")
 	fmt.Println(res.Combined())
-	assert.Assert(t, strings.Contains(res.Combined(), "Container e2e-restart-profiles-test-1  Started"), res.Combined())
+	assert.Assert(t, strings.Contains(res.Combined(), "Container e2e-restart-profiles-test-1 Started"), res.Combined())
 }

+ 19 - 19
pkg/e2e/start_stop_test.go

@@ -39,7 +39,7 @@ func TestStartStop(t *testing.T) {
 	t.Run("Up a project", func(t *testing.T) {
 		res := c.RunDockerComposeCmd(t, "-f", "./fixtures/start-stop/compose.yaml", "--project-name", projectName, "up",
 			"-d")
-		assert.Assert(t, strings.Contains(res.Combined(), "Container e2e-start-stop-no-dependencies-simple-1  Started"), res.Combined())
+		assert.Assert(t, strings.Contains(res.Combined(), "Container e2e-start-stop-no-dependencies-simple-1 Started"), res.Combined())
 
 		res = c.RunDockerComposeCmd(t, "ls", "--all")
 		testify.Regexp(t, getProjectRegx("running"), res.Stdout())
@@ -87,14 +87,14 @@ func TestStartStopWithDependencies(t *testing.T) {
 	t.Run("Up", func(t *testing.T) {
 		res := c.RunDockerComposeCmd(t, "-f", "./fixtures/dependencies/compose.yaml", "--project-name", projectName,
 			"up", "-d")
-		assert.Assert(t, strings.Contains(res.Combined(), "Container e2e-start-stop-with-dependencies-foo-1  Started"), res.Combined())
-		assert.Assert(t, strings.Contains(res.Combined(), "Container e2e-start-stop-with-dependencies-bar-1  Started"), res.Combined())
+		assert.Assert(t, strings.Contains(res.Combined(), "Container e2e-start-stop-with-dependencies-foo-1 Started"), res.Combined())
+		assert.Assert(t, strings.Contains(res.Combined(), "Container e2e-start-stop-with-dependencies-bar-1 Started"), res.Combined())
 	})
 
 	t.Run("stop foo", func(t *testing.T) {
 		res := c.RunDockerComposeCmd(t, "--project-name", projectName, "stop", "foo")
 
-		assert.Assert(t, strings.Contains(res.Combined(), "Container e2e-start-stop-with-dependencies-foo-1  Stopped"), res.Combined())
+		assert.Assert(t, strings.Contains(res.Combined(), "Container e2e-start-stop-with-dependencies-foo-1 Stopped"), res.Combined())
 
 		res = c.RunDockerComposeCmd(t, "--project-name", projectName, "ps", "--status", "running")
 		assert.Assert(t, strings.Contains(res.Combined(), "e2e-start-stop-with-dependencies-bar-1"), res.Combined())
@@ -103,12 +103,12 @@ func TestStartStopWithDependencies(t *testing.T) {
 
 	t.Run("start foo", func(t *testing.T) {
 		res := c.RunDockerComposeCmd(t, "--project-name", projectName, "stop")
-		assert.Assert(t, strings.Contains(res.Combined(), "Container e2e-start-stop-with-dependencies-bar-1  Stopped"), res.Combined())
+		assert.Assert(t, strings.Contains(res.Combined(), "Container e2e-start-stop-with-dependencies-bar-1 Stopped"), res.Combined())
 
 		res = c.RunDockerComposeCmd(t, "--project-name", projectName, "start", "foo")
 		out := res.Combined()
-		assert.Assert(t, strings.Contains(out, "Container e2e-start-stop-with-dependencies-bar-1  Started"), out)
-		assert.Assert(t, strings.Contains(out, "Container e2e-start-stop-with-dependencies-foo-1  Started"), out)
+		assert.Assert(t, strings.Contains(out, "Container e2e-start-stop-with-dependencies-bar-1 Started"), out)
+		assert.Assert(t, strings.Contains(out, "Container e2e-start-stop-with-dependencies-foo-1 Started"), out)
 
 		res = c.RunDockerComposeCmd(t, "--project-name", projectName, "ps", "--status", "running")
 		out = res.Combined()
@@ -120,8 +120,8 @@ func TestStartStopWithDependencies(t *testing.T) {
 		_ = c.RunDockerComposeCmd(t, "--project-name", projectName, "down")
 		res := c.RunDockerComposeCmd(t, "-f", "./fixtures/links/compose.yaml", "--project-name", projectName, "up",
 			"--no-deps", "-d", "foo")
-		assert.Assert(t, strings.Contains(res.Combined(), "Container e2e-start-stop-with-dependencies-foo-1  Started"), res.Combined())
-		assert.Assert(t, !strings.Contains(res.Combined(), "Container e2e-start-stop-with-dependencies-bar-1  Started"), res.Combined())
+		assert.Assert(t, strings.Contains(res.Combined(), "Container e2e-start-stop-with-dependencies-foo-1 Started"), res.Combined())
+		assert.Assert(t, !strings.Contains(res.Combined(), "Container e2e-start-stop-with-dependencies-bar-1 Started"), res.Combined())
 	})
 
 	t.Run("down", func(t *testing.T) {
@@ -136,8 +136,8 @@ func TestStartStopWithOneOffs(t *testing.T) {
 	t.Run("Up", func(t *testing.T) {
 		res := c.RunDockerComposeCmd(t, "-f", "./fixtures/dependencies/compose.yaml", "--project-name", projectName,
 			"up", "-d")
-		assert.Assert(t, strings.Contains(res.Combined(), "Container e2e-start-stop-with-oneoffs-foo-1  Started"), res.Combined())
-		assert.Assert(t, strings.Contains(res.Combined(), "Container e2e-start-stop-with-oneoffs-bar-1  Started"), res.Combined())
+		assert.Assert(t, strings.Contains(res.Combined(), "Container e2e-start-stop-with-oneoffs-foo-1 Started"), res.Combined())
+		assert.Assert(t, strings.Contains(res.Combined(), "Container e2e-start-stop-with-oneoffs-bar-1 Started"), res.Combined())
 	})
 
 	t.Run("run one-off", func(t *testing.T) {
@@ -213,7 +213,7 @@ func TestStopAlreadyStopped(t *testing.T) {
 	// 		container is already stopped
 	res.Assert(t, icmd.Expected{
 		ExitCode: 0,
-		Err:      "Container e2e-start-stop-svc-already-stopped-simple-1  Stopped",
+		Err:      "Container e2e-start-stop-svc-already-stopped-simple-1 Stopped",
 	})
 }
 
@@ -230,14 +230,14 @@ func TestStartStopMultipleServices(t *testing.T) {
 	res := cli.RunDockerComposeCmd(t, "stop", "simple", "another")
 	services := []string{"simple", "another"}
 	for _, svc := range services {
-		stopMsg := fmt.Sprintf("Container e2e-start-stop-svc-multiple-%s-1  Stopped", svc)
+		stopMsg := fmt.Sprintf("Container e2e-start-stop-svc-multiple-%s-1 Stopped", svc)
 		assert.Assert(t, strings.Contains(res.Stderr(), stopMsg),
 			fmt.Sprintf("Missing stop message for %s\n%s", svc, res.Combined()))
 	}
 
 	res = cli.RunDockerComposeCmd(t, "start", "simple", "another")
 	for _, svc := range services {
-		startMsg := fmt.Sprintf("Container e2e-start-stop-svc-multiple-%s-1  Started", svc)
+		startMsg := fmt.Sprintf("Container e2e-start-stop-svc-multiple-%s-1 Started", svc)
 		assert.Assert(t, strings.Contains(res.Stderr(), startMsg),
 			fmt.Sprintf("Missing start message for %s\n%s", svc, res.Combined()))
 	}
@@ -256,7 +256,7 @@ func TestStartSingleServiceAndDependency(t *testing.T) {
 	res := cli.RunDockerComposeCmd(t, "start", "desired")
 	desiredServices := []string{"desired", "dep_1", "dep_2"}
 	for _, s := range desiredServices {
-		startMsg := fmt.Sprintf("Container e2e-start-single-deps-%s-1  Started", s)
+		startMsg := fmt.Sprintf("Container e2e-start-single-deps-%s-1 Started", s)
 		assert.Assert(t, strings.Contains(res.Combined(), startMsg),
 			fmt.Sprintf("Missing start message for service: %s\n%s", s, res.Combined()))
 	}
@@ -277,8 +277,8 @@ func TestStartStopMultipleFiles(t *testing.T) {
 	cli.RunDockerComposeCmd(t, "-f", "./fixtures/start-stop/other.yaml", "up", "-d")
 
 	res := cli.RunDockerComposeCmd(t, "-f", "./fixtures/start-stop/compose.yaml", "stop")
-	assert.Assert(t, strings.Contains(res.Combined(), "Container e2e-start-stop-svc-multiple-files-simple-1  Stopped"), res.Combined())
-	assert.Assert(t, strings.Contains(res.Combined(), "Container e2e-start-stop-svc-multiple-files-another-1  Stopped"), res.Combined())
-	assert.Assert(t, !strings.Contains(res.Combined(), "Container e2e-start-stop-svc-multiple-files-a-different-one-1  Stopped"), res.Combined())
-	assert.Assert(t, !strings.Contains(res.Combined(), "Container e2e-start-stop-svc-multiple-files-and-another-one-1  Stopped"), res.Combined())
+	assert.Assert(t, strings.Contains(res.Combined(), "Container e2e-start-stop-svc-multiple-files-simple-1 Stopped"), res.Combined())
+	assert.Assert(t, strings.Contains(res.Combined(), "Container e2e-start-stop-svc-multiple-files-another-1 Stopped"), res.Combined())
+	assert.Assert(t, !strings.Contains(res.Combined(), "Container e2e-start-stop-svc-multiple-files-a-different-one-1 Stopped"), res.Combined())
+	assert.Assert(t, !strings.Contains(res.Combined(), "Container e2e-start-stop-svc-multiple-files-and-another-one-1 Stopped"), res.Combined())
 }

+ 6 - 6
pkg/e2e/up_test.go

@@ -175,8 +175,8 @@ func TestUpWithAllResources(t *testing.T) {
 	})
 
 	res := c.RunDockerComposeCmd(t, "-f", "./fixtures/resources/compose.yaml", "--all-resources", "--project-name", projectName, "up")
-	assert.Assert(t, strings.Contains(res.Combined(), fmt.Sprintf(`Volume %s_my_vol  Created`, projectName)), res.Combined())
-	assert.Assert(t, strings.Contains(res.Combined(), fmt.Sprintf(`Network %s_my_net  Created`, projectName)), res.Combined())
+	assert.Assert(t, strings.Contains(res.Combined(), fmt.Sprintf(`Volume %s_my_vol Created`, projectName)), res.Combined())
+	assert.Assert(t, strings.Contains(res.Combined(), fmt.Sprintf(`Network %s_my_net Created`, projectName)), res.Combined())
 }
 
 func TestUpProfile(t *testing.T) {
@@ -187,9 +187,9 @@ func TestUpProfile(t *testing.T) {
 	})
 
 	res := c.RunDockerComposeCmd(t, "-f", "./fixtures/profiles/docker-compose.yaml", "--project-name", projectName, "up", "foo")
-	assert.Assert(t, strings.Contains(res.Combined(), `Container db_c  Created`), res.Combined())
-	assert.Assert(t, strings.Contains(res.Combined(), `Container foo_c  Created`), res.Combined())
-	assert.Assert(t, !strings.Contains(res.Combined(), `Container bar_c  Created`), res.Combined())
+	assert.Assert(t, strings.Contains(res.Combined(), `Container db_c Created`), res.Combined())
+	assert.Assert(t, strings.Contains(res.Combined(), `Container foo_c Created`), res.Combined())
+	assert.Assert(t, !strings.Contains(res.Combined(), `Container bar_c Created`), res.Combined())
 }
 
 func TestUpImageID(t *testing.T) {
@@ -218,7 +218,7 @@ func TestUpStopWithLogsMixed(t *testing.T) {
 	res := c.RunDockerComposeCmd(t, "-f", "./fixtures/stop/compose.yaml", "--project-name", projectName, "up", "--abort-on-container-exit")
 	// assert we still get service2 logs after service 1 Stopped event
 	res.Assert(t, icmd.Expected{
-		Err: "Container compose-e2e-stop-logs-service1-1  Stopped",
+		Err: "Container compose-e2e-stop-logs-service1-1 Stopped",
 	})
 	// assert we get stop hook logs
 	res.Assert(t, icmd.Expected{Out: "service2-1 ->  | stop hook running...\nservice2-1     | 64 bytes"})

+ 49 - 22
pkg/progress/event.go

@@ -16,7 +16,10 @@
 
 package progress
 
-import "context"
+import (
+	"context"
+	"fmt"
+)
 
 // EventStatus indicates the status of an action
 type EventStatus int
@@ -54,28 +57,52 @@ const (
 	StatusBuilt      = "Built"
 	StatusPulling    = "Pulling"
 	StatusPulled     = "Pulled"
+	StatusCommitting = "Committing"
+	StatusCommitted  = "Committed"
+	StatusCopying    = "Copying"
+	StatusCopied     = "Copied"
+	StatusExporting  = "Exporting"
+	StatusExported   = "Exported"
 )
 
 // Event represents a progress event.
 type Event struct {
-	ID         string
-	ParentID   string
-	Text       string
-	Status     EventStatus
-	StatusText string
-	Current    int64
-	Percent    int
-	Total      int64
+	ID       string
+	ParentID string
+	Text     string
+	Details  string
+	Status   EventStatus
+	Current  int64
+	Percent  int
+	Total    int64
+}
+
+func (e *Event) StatusText() string {
+	switch e.Status {
+	case Working:
+		return "Working"
+	case Warning:
+		return "Warning"
+	case Done:
+		return "Done"
+	default:
+		return "Error"
+	}
 }
 
-// ErrorMessageEvent creates a new Error Event with message
-func ErrorMessageEvent(id string, msg string) Event {
-	return NewEvent(id, Error, msg)
+// ErrorEvent creates a new Error Event with message
+func ErrorEvent(id string, msg string) Event {
+	return Event{
+		ID:      id,
+		Status:  Error,
+		Text:    StatusError,
+		Details: msg,
+	}
 }
 
-// ErrorEvent creates a new Error Event
-func ErrorEvent(id string) Event {
-	return NewEvent(id, Error, StatusError)
+// ErrorEventf creates a new Error Event with format message
+func ErrorEventf(id string, msg string, args ...any) Event {
+	return ErrorEvent(id, fmt.Sprintf(msg, args...))
 }
 
 // CreatingEvent creates a new Create in progress Event
@@ -181,18 +208,18 @@ func PulledEvent(id string) Event {
 // SkippedEvent creates a new Skipped Event
 func SkippedEvent(id string, reason string) Event {
 	return Event{
-		ID:         id,
-		Status:     Warning,
-		StatusText: "Skipped: " + reason,
+		ID:     id,
+		Status: Warning,
+		Text:   "Skipped: " + reason,
 	}
 }
 
 // NewEvent new event
-func NewEvent(id string, status EventStatus, statusText string) Event {
+func NewEvent(id string, status EventStatus, text string) Event {
 	return Event{
-		ID:         id,
-		Status:     status,
-		StatusText: statusText,
+		ID:     id,
+		Status: status,
+		Text:   text,
 	}
 }
 

+ 4 - 2
pkg/progress/json.go

@@ -39,8 +39,9 @@ type jsonMessage struct {
 	Tail     bool   `json:"tail,omitempty"`
 	ID       string `json:"id,omitempty"`
 	ParentID string `json:"parent_id,omitempty"`
-	Text     string `json:"text,omitempty"`
 	Status   string `json:"status,omitempty"`
+	Text     string `json:"text,omitempty"`
+	Details  string `json:"details,omitempty"`
 	Current  int64  `json:"current,omitempty"`
 	Total    int64  `json:"total,omitempty"`
 	Percent  int    `json:"percent,omitempty"`
@@ -54,8 +55,9 @@ func (p *jsonWriter) Event(e Event) {
 		DryRun:   p.dryRun,
 		Tail:     false,
 		ID:       e.ID,
+		Status:   e.StatusText(),
 		Text:     e.Text,
-		Status:   e.StatusText,
+		Details:  e.Details,
 		ParentID: e.ParentID,
 		Current:  e.Current,
 		Total:    e.Total,

+ 9 - 9
pkg/progress/json_test.go

@@ -32,13 +32,13 @@ func TestJsonWriter_Event(t *testing.T) {
 	}
 
 	event := Event{
-		ID:         "service1",
-		ParentID:   "project",
-		Text:       "Creating",
-		StatusText: "Working",
-		Current:    50,
-		Total:      100,
-		Percent:    50,
+		ID:       "service1",
+		ParentID: "project",
+		Status:   Working,
+		Text:     StatusCreating,
+		Current:  50,
+		Total:    100,
+		Percent:  50,
 	}
 	w.Event(event)
 
@@ -50,8 +50,8 @@ func TestJsonWriter_Event(t *testing.T) {
 		DryRun:   true,
 		ID:       event.ID,
 		ParentID: event.ParentID,
-		Text:     event.Text,
-		Status:   event.StatusText,
+		Text:     StatusCreating,
+		Status:   "Working",
 		Current:  event.Current,
 		Total:    event.Total,
 		Percent:  event.Percent,

+ 1 - 1
pkg/progress/plain.go

@@ -43,7 +43,7 @@ func (p *plainWriter) Event(e Event) {
 	if p.dryRun {
 		prefix = api.DRYRUN_PREFIX
 	}
-	_, _ = fmt.Fprintln(p.out, prefix, e.ID, e.Text, e.StatusText)
+	_, _ = fmt.Fprintln(p.out, prefix, e.ID, e.Text, e.Details)
 }
 
 func (p *plainWriter) On(events ...Event) {

+ 38 - 41
pkg/progress/tty.go

@@ -58,17 +58,17 @@ type ttyWriter struct {
 }
 
 type task struct {
-	ID         string
-	parentID   string
-	startTime  time.Time
-	endTime    time.Time
-	text       string
-	status     EventStatus
-	statusText string
-	current    int64
-	percent    int
-	total      int64
-	spinner    *Spinner
+	ID        string
+	parentID  string
+	startTime time.Time
+	endTime   time.Time
+	text      string
+	details   string
+	status    EventStatus
+	current   int64
+	percent   int
+	total     int64
+	spinner   *Spinner
 }
 
 func (t *task) stop() {
@@ -112,7 +112,7 @@ func (w *ttyWriter) On(events ...Event) {
 	w.mtx.Lock()
 	defer w.mtx.Unlock()
 	for _, e := range events {
-		if w.operation != "start" && (e.StatusText == "Started" || e.StatusText == "Starting") {
+		if w.operation != "start" && (e.Text == StatusStarted || e.Text == StatusStarting) {
 			// skip those events to avoid mix with container logs
 			continue
 		}
@@ -122,8 +122,7 @@ func (w *ttyWriter) On(events ...Event) {
 
 func (w *ttyWriter) event(e Event) {
 	// Suspend print while a build is in progress, to avoid collision with buildkit Display
-	if e.StatusText == StatusBuilding {
-		fmt.Println("suspend during build")
+	if e.Text == StatusBuilding {
 		w.ticker.Stop()
 		w.suspended = true
 	} else if w.suspended {
@@ -142,7 +141,7 @@ func (w *ttyWriter) event(e Event) {
 		}
 		last.status = e.Status
 		last.text = e.Text
-		last.statusText = e.StatusText
+		last.details = e.Details
 		// progress can only go up
 		if e.Total > last.total {
 			last.total = e.Total
@@ -160,16 +159,16 @@ func (w *ttyWriter) event(e Event) {
 		w.tasks[e.ID] = last
 	} else {
 		t := task{
-			ID:         e.ID,
-			parentID:   e.ParentID,
-			startTime:  time.Now(),
-			text:       e.Text,
-			status:     e.Status,
-			statusText: e.StatusText,
-			current:    e.Current,
-			percent:    e.Percent,
-			total:      e.Total,
-			spinner:    NewSpinner(),
+			ID:        e.ID,
+			parentID:  e.ParentID,
+			startTime: time.Now(),
+			text:      e.Text,
+			details:   e.Details,
+			status:    e.Status,
+			current:   e.Current,
+			percent:   e.Percent,
+			total:     e.Total,
+			spinner:   NewSpinner(),
 		}
 		if e.Status == Done || e.Status == Error {
 			t.stop()
@@ -197,7 +196,7 @@ func (w *ttyWriter) printEvent(e Event) {
 	case Error:
 		color = ErrorColor
 	}
-	_, _ = fmt.Fprintf(w.out, "%s %s %s\n", e.ID, e.Text, color(e.StatusText))
+	_, _ = fmt.Fprintf(w.out, "%s %s %s\n", e.ID, color(e.Text), e.Details)
 }
 
 func (w *ttyWriter) print() {
@@ -228,7 +227,7 @@ func (w *ttyWriter) print() {
 
 	var statusPadding int
 	for _, t := range w.tasks {
-		l := len(fmt.Sprintf("%s %s", t.ID, t.text))
+		l := len(t.ID)
 		if statusPadding < l {
 			statusPadding = l
 		}
@@ -314,20 +313,17 @@ func (w *ttyWriter) lineText(t task, pad string, terminalWidth, statusPadding in
 		hideDetails = true
 	}
 
-	var txt string
+	txt := t.ID
 	if len(completion) > 0 {
-		var details string
+		var progress string
 		if !hideDetails {
-			details = fmt.Sprintf(" %7s / %-7s", units.HumanSize(float64(current)), units.HumanSize(float64(total)))
+			progress = fmt.Sprintf(" %7s / %-7s", units.HumanSize(float64(current)), units.HumanSize(float64(total)))
 		}
-		txt = fmt.Sprintf("%s [%s]%s %s",
+		txt = fmt.Sprintf("%s [%s]%s",
 			t.ID,
 			SuccessColor(strings.Join(completion, "")),
-			details,
-			t.text,
+			progress,
 		)
-	} else {
-		txt = fmt.Sprintf("%s %s", t.ID, t.text)
 	}
 	textLen := len(txt)
 	padding := statusPadding - textLen
@@ -336,19 +332,20 @@ func (w *ttyWriter) lineText(t task, pad string, terminalWidth, statusPadding in
 	}
 	// calculate the max length for the status text, on errors it
 	// is 2-3 lines long and breaks the line formatting
-	maxStatusLen := terminalWidth - textLen - statusPadding - 15
-	status := t.statusText
+	maxDetailsLen := terminalWidth - textLen - statusPadding - 15
+	details := t.details
 	// in some cases (debugging under VS Code), terminalWidth is set to zero by goterm.Width() ; ensuring we don't tweak strings with negative char index
-	if maxStatusLen > 0 && len(status) > maxStatusLen {
-		status = status[:maxStatusLen] + "..."
+	if maxDetailsLen > 0 && len(details) > maxDetailsLen {
+		details = details[:maxDetailsLen] + "..."
 	}
-	text := fmt.Sprintf("%s %s%s %s%s %s",
+	text := fmt.Sprintf("%s %s%s %s %s%s %s",
 		pad,
 		spinner(t),
 		prefix,
 		txt,
 		strings.Repeat(" ", padding),
-		colorFn(t.status)(status),
+		colorFn(t.status)(t.text),
+		details,
 	)
 	timer := fmt.Sprintf("%.1fs ", elapsed)
 	o := align(text, TimerColor(timer), terminalWidth)