diff --git a/cmd/compose/watch.go b/cmd/compose/watch.go index e4f94272637..cc590dfbc11 100644 --- a/cmd/compose/watch.go +++ b/cmd/compose/watch.go @@ -23,6 +23,7 @@ import ( "github.com/compose-spec/compose-go/v2/types" "github.com/docker/cli/cli/command" + "github.com/docker/compose/v2/cmd/formatter" "github.com/docker/compose/v2/internal/locker" "github.com/docker/compose/v2/pkg/api" "github.com/sirupsen/logrus" @@ -31,8 +32,12 @@ import ( type watchOptions struct { *ProjectOptions - quiet bool - noUp bool + quiet bool + noUp bool + logs bool + noColor bool + noPrefix bool + timestamp bool } func watchCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command { @@ -59,6 +64,10 @@ func watchCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) cmd.Flags().BoolVar(&watchOpts.quiet, "quiet", false, "hide build output") cmd.Flags().BoolVar(&watchOpts.noUp, "no-up", false, "Do not build & start services before watching") + cmd.Flags().BoolVar(&watchOpts.logs, "logs", false, "Show containers logs") + cmd.Flags().BoolVar(&watchOpts.noColor, "no-color", false, "Produce monochrome output in logs") + cmd.Flags().BoolVar(&watchOpts.noPrefix, "no-log-prefix", false, "Don't print prefix in logs") + cmd.Flags().BoolVar(&watchOpts.timestamp, "timestamps", false, "Show timestamps in logs") return cmd } @@ -86,6 +95,11 @@ func runWatch(ctx context.Context, dockerCli command.Cli, backend api.Service, w return fmt.Errorf("cannot take exclusive lock for project %q: %w", project.Name, err) } + var consumer api.LogConsumer + if watchOpts.logs { + consumer = formatter.NewLogConsumer(ctx, dockerCli.Out(), dockerCli.Err(), !watchOpts.noColor, !watchOpts.noPrefix, watchOpts.timestamp) + } + if !watchOpts.noUp { for index, service := range project.Services { if service.Build != nil && service.Develop != nil { @@ -105,7 +119,7 @@ func runWatch(ctx context.Context, dockerCli command.Cli, backend api.Service, w }, Start: api.StartOptions{ Project: project, - Attach: nil, + Attach: consumer, CascadeStop: false, Services: services, }, @@ -115,6 +129,7 @@ func runWatch(ctx context.Context, dockerCli command.Cli, backend api.Service, w } } return backend.Watch(ctx, project, services, api.WatchOptions{ - Build: build, + Build: build, + Attach: consumer, }) } diff --git a/pkg/api/api.go b/pkg/api/api.go index 3b8f091611d..887a567530a 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -119,6 +119,8 @@ type VizOptions struct { // WatchOptions group options of the Watch API type WatchOptions struct { Build BuildOptions + // Attach to container and forward logs if not nil + Attach LogConsumer } // BuildOptions group options of the Build API diff --git a/pkg/compose/watch.go b/pkg/compose/watch.go index 2e783026e11..0ecca6ae7f2 100644 --- a/pkg/compose/watch.go +++ b/pkg/compose/watch.go @@ -190,7 +190,7 @@ func (s *composeService) watch(ctx context.Context, project *types.Project, name case batch := <-batchEvents: start := time.Now() logrus.Debugf("batch start: service[%s] count[%d]", name, len(batch)) - if err := s.handleWatchBatch(ctx, project, name, options.Build, batch, syncer); err != nil { + if err := s.handleWatchBatch(ctx, project, name, options, batch, syncer); err != nil { logrus.Warnf("Error handling changed files for service %s: %v", name, err) } logrus.Debugf("batch complete: service[%s] duration[%s] count[%d]", @@ -431,7 +431,7 @@ func (t tarDockerClient) Untar(ctx context.Context, id string, archive io.ReadCl }) } -func (s *composeService) handleWatchBatch(ctx context.Context, project *types.Project, serviceName string, build api.BuildOptions, batch []fileEvent, syncer sync.Syncer) error { +func (s *composeService) handleWatchBatch(ctx context.Context, project *types.Project, serviceName string, options api.WatchOptions, batch []fileEvent, syncer sync.Syncer) error { pathMappings := make([]sync.PathMapping, len(batch)) restartService := false for i := range batch { @@ -443,15 +443,16 @@ func (s *composeService) handleWatchBatch(ctx context.Context, project *types.Pr strings.Join(append([]string{""}, batch[i].HostPath), "\n - "), ) // restrict the build to ONLY this service, not any of its dependencies - build.Services = []string{serviceName} + options.Build.Services = []string{serviceName} err := s.Up(ctx, project, api.UpOptions{ Create: api.CreateOptions{ - Build: &build, + Build: &options.Build, Services: []string{serviceName}, Inherit: true, }, Start: api.StartOptions{ Services: []string{serviceName}, + Attach: options.Attach, Project: project, }, })