diff --git a/Dockerfile b/Dockerfile index a6f74d07d2b..4488f740a06 100644 --- a/Dockerfile +++ b/Dockerfile @@ -101,6 +101,7 @@ RUN --mount=type=bind,target=. \ FROM build-base AS test ARG CGO_ENABLED=0 ARG BUILD_TAGS +ENV COMPOSE_MENU=FALSE RUN --mount=type=bind,target=. \ --mount=type=cache,target=/root/.cache \ --mount=type=cache,target=/go/pkg/mod \ diff --git a/Makefile b/Makefile index df30dc86009..60bbae549ba 100644 --- a/Makefile +++ b/Makefile @@ -13,6 +13,7 @@ # limitations under the License. PKG := github.com/docker/compose/v2 +export COMPOSE_MENU = FALSE VERSION ?= $(shell git describe --match 'v[0-9]*' --dirty='.m' --always --tags) GO_LDFLAGS ?= -w -X ${PKG}/internal.Version=${VERSION} diff --git a/cmd/compose/compose.go b/cmd/compose/compose.go index 5136c40628e..42b7b178d7b 100644 --- a/cmd/compose/compose.go +++ b/cmd/compose/compose.go @@ -65,7 +65,7 @@ const ( ComposeIgnoreOrphans = "COMPOSE_IGNORE_ORPHANS" // ComposeEnvFiles defines the env files to use if --env-file isn't used ComposeEnvFiles = "COMPOSE_ENV_FILES" - // ComposeMenu defines if the navigation menu should be rendered. Can be also set via --navigation-menu + // ComposeMenu defines if the navigation menu should be rendered. Can be also set via --menu ComposeMenu = "COMPOSE_MENU" ) @@ -622,3 +622,15 @@ var printerModes = []string{ ui.ModePlain, ui.ModeQuiet, } + +func SetUnchangedOption(name string, experimentalFlag bool) bool { + var value bool + // If the var is defined we use that value first + if envVar, ok := os.LookupEnv(name); ok { + value = utils.StringToBool(envVar) + } else { + // if not, we try to get it from experimental feature flag + value = experimentalFlag + } + return value +} diff --git a/cmd/compose/up.go b/cmd/compose/up.go index e766b375cb5..1b458e3a0d1 100644 --- a/cmd/compose/up.go +++ b/cmd/compose/up.go @@ -42,21 +42,22 @@ type composeOptions struct { type upOptions struct { *composeOptions - Detach bool - noStart bool - noDeps bool - cascadeStop bool - exitCodeFrom string - noColor bool - noPrefix bool - attachDependencies bool - attach []string - noAttach []string - timestamp bool - wait bool - waitTimeout int - watch bool - navigationMenu bool + Detach bool + noStart bool + noDeps bool + cascadeStop bool + exitCodeFrom string + noColor bool + noPrefix bool + attachDependencies bool + attach []string + noAttach []string + timestamp bool + wait bool + waitTimeout int + watch bool + navigationMenu bool + navigationMenuChanged bool } func (opts upOptions) apply(project *types.Project, services []string) (*types.Project, error) { @@ -88,6 +89,7 @@ func upCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service, ex PreRunE: AdaptCmd(func(ctx context.Context, cmd *cobra.Command, args []string) error { create.pullChanged = cmd.Flags().Changed("pull") create.timeChanged = cmd.Flags().Changed("timeout") + up.navigationMenuChanged = cmd.Flags().Changed("menu") return validateFlags(&up, &create) }), RunE: p.WithServices(dockerCli, func(ctx context.Context, project *types.Project, services []string) error { @@ -129,11 +131,8 @@ func upCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service, ex flags.BoolVar(&up.wait, "wait", false, "Wait for services to be running|healthy. Implies detached mode.") flags.IntVar(&up.waitTimeout, "wait-timeout", 0, "Maximum duration to wait for the project to be running|healthy") flags.BoolVarP(&up.watch, "watch", "w", false, "Watch source code and rebuild/refresh containers when files are updated.") - composeMenu := true - if os.Getenv(ComposeMenu) != "" { - composeMenu = utils.StringToBool(os.Getenv(ComposeMenu)) - } - flags.BoolVar(&up.navigationMenu, "navigation-menu", composeMenu, "While running in attach mode, enable shortcuts and shortcuts info bar.") + flags.BoolVar(&up.navigationMenu, "menu", false, "Enable interactive shortcuts when running attached (Experimental). Incompatible with --detach.") + flags.MarkHidden("menu") //nolint:errcheck return upCmd } @@ -167,7 +166,7 @@ func runUp( ctx context.Context, dockerCli command.Cli, backend api.Service, - _ *experimental.State, + experimentals *experimental.State, createOptions createOptions, upOptions upOptions, buildOptions buildOptions, @@ -187,6 +186,9 @@ func runUp( if err != nil { return err } + if !upOptions.navigationMenuChanged { + upOptions.navigationMenu = SetUnchangedOption(ComposeMenu, experimentals.NavBar()) + } var build *api.BuildOptions if !createOptions.noBuild { @@ -259,16 +261,16 @@ func runUp( return backend.Up(ctx, project, api.UpOptions{ Create: create, Start: api.StartOptions{ - Project: project, - Attach: consumer, - AttachTo: attach, - ExitCodeFrom: upOptions.exitCodeFrom, - CascadeStop: upOptions.cascadeStop, - Wait: upOptions.wait, - WaitTimeout: timeout, - Watch: upOptions.watch, - Services: services, - NavigationBar: upOptions.navigationMenu, + Project: project, + Attach: consumer, + AttachTo: attach, + ExitCodeFrom: upOptions.exitCodeFrom, + CascadeStop: upOptions.cascadeStop, + Wait: upOptions.wait, + WaitTimeout: timeout, + Watch: upOptions.watch, + Services: services, + NavigationMenu: upOptions.navigationMenu, }, }) } diff --git a/cmd/formatter/colors.go b/cmd/formatter/colors.go index 7563b06c5d1..cf6a4b56c37 100644 --- a/cmd/formatter/colors.go +++ b/cmd/formatter/colors.go @@ -35,6 +35,18 @@ var names = []string{ "white", } +const ( + BOLD = "1" + FAINT = "2" + ITALIC = "3" + UNDERLINE = "4" +) + +const ( + RESET = "0" + CYAN = "36" +) + const ( // Never use ANSI codes Never = "never" @@ -72,13 +84,17 @@ var monochrome = func(s string) string { return s } -func ansiColor(code, s string) string { - return fmt.Sprintf("%s%s%s", ansiColorCode(code), s, ansiColorCode("0")) +func ansiColor(code, s string, formatOpts ...string) string { + return fmt.Sprintf("%s%s%s", ansiColorCode(code, formatOpts...), s, ansiColorCode("0")) } // Everything about ansiColorCode color https://hyperskill.org/learn/step/18193 -func ansiColorCode(code string) string { - return fmt.Sprintf("\033[%sm", code) +func ansiColorCode(code string, formatOpts ...string) string { + res := "\033[" + for _, c := range formatOpts { + res = fmt.Sprintf("%s%s;", res, c) + } + return fmt.Sprintf("%s%sm", res, code) } func makeColorFunc(code string) colorFunc { diff --git a/cmd/formatter/logs.go b/cmd/formatter/logs.go index 7f41cf7ec4f..7d2c5451296 100644 --- a/cmd/formatter/logs.go +++ b/cmd/formatter/logs.go @@ -108,21 +108,22 @@ func (l *logConsumer) write(w io.Writer, container, message string) { if l.ctx.Err() != nil { return } - print := func() { + printFn := func() { p := l.getPresenter(container) timestamp := time.Now().Format(jsonmessage.RFC3339NanoFixed) for _, line := range strings.Split(message, "\n") { + ClearLine() if l.timestamp { - fmt.Fprintf(w, "\033[2K%s%s%s\n", p.prefix, timestamp, line) + fmt.Fprintf(w, "%s%s%s\n", p.prefix, timestamp, line) } else { - fmt.Fprintf(w, "\033[2K%s%s\n", p.prefix, line) + fmt.Fprintf(w, "%s%s\n", p.prefix, line) } } } if KeyboardManager != nil { - KeyboardManager.PrintKeyboardInfo(print) + KeyboardManager.PrintKeyboardInfo(printFn) } else { - print() + printFn() } } diff --git a/cmd/formatter/shortcut.go b/cmd/formatter/shortcut.go index 0e8e33ef7a9..feb3eb95fce 100644 --- a/cmd/formatter/shortcut.go +++ b/cmd/formatter/shortcut.go @@ -18,6 +18,7 @@ package formatter import ( "context" + "errors" "fmt" "math" "os" @@ -41,12 +42,12 @@ type KeyboardError struct { timeStart time.Time } -func (ke *KeyboardError) shoudlDisplay() bool { +func (ke *KeyboardError) shouldDisplay() bool { return ke.err != nil && int(time.Since(ke.timeStart).Seconds()) < DISPLAY_ERROR_TIME } func (ke *KeyboardError) printError(height int, info string) { - if ke.shoudlDisplay() { + if ke.shouldDisplay() { errMessage := ke.err.Error() MoveCursor(height-linesOffset(info)-linesOffset(errMessage)-1, 0) @@ -59,10 +60,10 @@ func (ke *KeyboardError) printError(height int, info string) { func (ke *KeyboardError) addError(prefix string, err error) { ke.timeStart = time.Now() - prefix = ansiColor("1;36", fmt.Sprintf("%s →", prefix)) + prefix = ansiColor(CYAN, fmt.Sprintf("%s →", prefix), BOLD) errorString := fmt.Sprintf("%s %s", prefix, err.Error()) - ke.err = fmt.Errorf(errorString) + ke.err = errors.New(errorString) } func (ke *KeyboardError) error() string { @@ -107,13 +108,19 @@ type LogKeyboard struct { IsWatchConfigured bool logLevel KEYBOARD_LOG_LEVEL signalChannel chan<- os.Signal - metrics tracing.KeyboardMetrics } var KeyboardManager *LogKeyboard var eg multierror.Group -func NewKeyboardManager(isDockerDesktopActive, isWatchConfigured bool, sc chan<- os.Signal, watchFn func(ctx context.Context, project *types.Project, services []string, options api.WatchOptions) error) { +func NewKeyboardManager(ctx context.Context, isDockerDesktopActive, isWatchConfigured bool, + sc chan<- os.Signal, + watchFn func(ctx context.Context, + project *types.Project, + services []string, + options api.WatchOptions, + ) error, +) { km := LogKeyboard{} km.IsDockerDesktopActive = isDockerDesktopActive km.IsWatchConfigured = isWatchConfigured @@ -124,22 +131,15 @@ func NewKeyboardManager(isDockerDesktopActive, isWatchConfigured bool, sc chan<- km.signalChannel = sc - km.metrics = tracing.KeyboardMetrics{ - EnabledViewDockerDesktop: isDockerDesktopActive, - HasWatchConfig: isWatchConfigured, - } - KeyboardManager = &km HideCursor() } -func (lk *LogKeyboard) PrintKeyboardInfo(print func()) { - lk.clearNavigationMenu() - print() +func (lk *LogKeyboard) PrintKeyboardInfo(printFn func()) { + printFn() if lk.logLevel == INFO { - lk.createBuffer(0) lk.printNavigationMenu() } } @@ -148,16 +148,16 @@ func (lk *LogKeyboard) PrintKeyboardInfo(print func()) { func (lk *LogKeyboard) createBuffer(lines int) { allocateSpace(lines) - if lk.kError.shoudlDisplay() { + if lk.kError.shouldDisplay() { extraLines := linesOffset(lk.kError.error()) + 1 allocateSpace(extraLines) - lines = lines + extraLines + lines += extraLines } infoMessage := lk.navigationMenu() extraLines := linesOffset(infoMessage) + 1 allocateSpace(extraLines) - lines = lines + extraLines + lines += extraLines if lines > 0 { MoveCursorUp(lines) @@ -165,6 +165,9 @@ func (lk *LogKeyboard) createBuffer(lines int) { } func (lk *LogKeyboard) printNavigationMenu() { + lk.clearNavigationMenu() + lk.createBuffer(0) + if lk.logLevel == INFO { height := goterm.Height() menu := lk.navigationMenu() @@ -187,7 +190,7 @@ func (lk *LogKeyboard) navigationMenu() string { var options string var openDDInfo string if lk.IsDockerDesktopActive { - openDDInfo = shortcutKeyColor("V") + navColor(" View in Docker Desktop") + openDDInfo = shortcutKeyColor("v") + navColor(" View in Docker Desktop") } var watchInfo string if openDDInfo != "" { @@ -197,7 +200,7 @@ func (lk *LogKeyboard) navigationMenu() string { if lk.Watch.Watching { isEnabled = " Disable" } - watchInfo = watchInfo + shortcutKeyColor("W") + navColor(isEnabled+" Watch") + watchInfo = watchInfo + shortcutKeyColor("w") + navColor(isEnabled+" Watch") return options + openDDInfo + watchInfo } @@ -212,64 +215,75 @@ func (lk *LogKeyboard) clearNavigationMenu() { RestoreCursor() } -func (lk *LogKeyboard) PrintEnter() { - lk.clearNavigationMenu() - lk.printNavigationMenu() -} - -func (lk *LogKeyboard) CleanTerminal() { - height := goterm.Height() - for i := 0; i < height; i++ { - NewLine() - ClearLine() - } -} - -func (lk *LogKeyboard) openDockerDesktop(project *types.Project) { +func (lk *LogKeyboard) openDockerDesktop(ctx context.Context, project *types.Project) { if !lk.IsDockerDesktopActive { return } - lk.metrics.ActivateViewDockerDesktop = true - link := fmt.Sprintf("docker-desktop://dashboard/apps/%s", project.Name) - err := open.Run(link) - if err != nil { - lk.kError.addError("View", fmt.Errorf("Could not open Docker Desktop")) - } + eg.Go(tracing.EventWrapFuncForErrGroup(ctx, "menu/gui", tracing.SpanOptions{}, + func(ctx context.Context) error { + link := fmt.Sprintf("docker-desktop://dashboard/apps/%s", project.Name) + err := open.Run(link) + if err != nil { + err = fmt.Errorf("Could not open Docker Desktop") + lk.keyboardError("View", err) + } + return err + }), + ) +} + +func (lk *LogKeyboard) keyboardError(prefix string, err error) { + lk.kError.addError(prefix, err) + + lk.printNavigationMenu() + timer1 := time.NewTimer((DISPLAY_ERROR_TIME + 1) * time.Second) + go func() { + <-timer1.C + lk.printNavigationMenu() + }() } func (lk *LogKeyboard) StartWatch(ctx context.Context, project *types.Project, options api.UpOptions) { if !lk.IsWatchConfigured { - lk.kError.addError("Watch", fmt.Errorf("Watch is not yet configured. Learn more: %s", ansiColor("36", "https://docs.docker.com/compose/file-watch/"))) + eg.Go(tracing.EventWrapFuncForErrGroup(ctx, "menu/watch", tracing.SpanOptions{}, + func(ctx context.Context) error { + err := fmt.Errorf("Watch is not yet configured. Learn more: %s", ansiColor(CYAN, "https://docs.docker.com/compose/file-watch/")) + lk.keyboardError("Watch", err) + return err + })) return } lk.Watch.switchWatching() - if !lk.Watch.isWatching() && lk.Watch.Cancel != nil { + if !lk.Watch.isWatching() { lk.Watch.Cancel() } else { - lk.Watch.newContext(ctx) - eg.Go(func() error { - buildOpts := *options.Create.Build - buildOpts.Quiet = true - err := lk.Watch.WatchFn(lk.Watch.Ctx, project, options.Start.Services, api.WatchOptions{ - Build: &buildOpts, - LogTo: options.Start.Attach, - }) - return err - }) + eg.Go(tracing.EventWrapFuncForErrGroup(ctx, "menu/watch", tracing.SpanOptions{}, + func(ctx context.Context) error { + lk.Watch.newContext(ctx) + buildOpts := *options.Create.Build + buildOpts.Quiet = true + return lk.Watch.WatchFn(lk.Watch.Ctx, project, options.Start.Services, api.WatchOptions{ + Build: &buildOpts, + LogTo: options.Start.Attach, + }) + })) } } +func (lk *LogKeyboard) KeyboardClose() { + _ = keyboard.Close() +} + func (lk *LogKeyboard) HandleKeyEvents(event keyboard.KeyEvent, ctx context.Context, project *types.Project, options api.UpOptions) { switch kRune := event.Rune; kRune { - case 'V': - lk.openDockerDesktop(project) - case 'W': - lk.metrics.ActivateWatch = true + case 'v': + lk.openDockerDesktop(ctx, project) + case 'w': lk.StartWatch(ctx, project, options) } switch key := event.Key; key { case keyboard.KeyCtrlC: - keyboard.Close() + lk.KeyboardClose() lk.clearNavigationMenu() ShowCursor() @@ -279,19 +293,10 @@ func (lk *LogKeyboard) HandleKeyEvents(event keyboard.KeyEvent, ctx context.Cont lk.Watch.Cancel() _ = eg.Wait().ErrorOrNil() // Need to print this ? } - go func() { - // Send telemetry - tracing.SpanWrapFunc("navigation_menu", tracing.KeyboardOptions(lk.metrics), - func(ctx context.Context) error { - return nil - })(ctx) - }() // will notify main thread to kill and will handle gracefully lk.signalChannel <- syscall.SIGINT case keyboard.KeyEnter: - lk.PrintEnter() - case keyboard.KeyCtrlL: - lk.CleanTerminal() + lk.printNavigationMenu() } } @@ -312,6 +317,5 @@ func shortcutKeyColor(key string) string { black := "0;0;0" background := "48;2" white := "255;255;255" - bold := "1" - return ansiColor(foreground+";"+black+";"+background+";"+white+";"+bold, key) + return ansiColor(foreground+";"+black+";"+background+";"+white, key, BOLD) } diff --git a/docs/reference/docker_compose_up.yaml b/docs/reference/docker_compose_up.yaml index ec269c8b85b..967a16379f9 100644 --- a/docs/reference/docker_compose_up.yaml +++ b/docs/reference/docker_compose_up.yaml @@ -108,6 +108,17 @@ options: experimentalcli: false kubernetes: false swarm: false + - option: menu + value_type: bool + default_value: "false" + description: | + Enable interactive shortcuts when running attached (Experimental). Incompatible with --detach. + deprecated: false + hidden: true + experimental: false + experimentalcli: false + kubernetes: false + swarm: false - option: no-attach value_type: stringArray default_value: '[]' diff --git a/internal/tracing/attributes.go b/internal/tracing/attributes.go index 622f5f8b40c..3d27c4796ac 100644 --- a/internal/tracing/attributes.go +++ b/internal/tracing/attributes.go @@ -42,13 +42,6 @@ type Metrics struct { CountIncludesRemote int } -type KeyboardMetrics struct { - EnabledViewDockerDesktop bool - HasWatchConfig bool - ActivateViewDockerDesktop bool - ActivateWatch bool -} - func (s SpanOptions) SpanStartOptions() []trace.SpanStartOption { out := make([]trace.SpanStartOption, len(s)) for i := range s { @@ -142,18 +135,6 @@ func ServiceOptions(service types.ServiceConfig) SpanOptions { } } -func KeyboardOptions(metrics KeyboardMetrics) SpanOptions { - attrs := []attribute.KeyValue{ - attribute.Bool("view.enabled", metrics.EnabledViewDockerDesktop), - attribute.Bool("view.activated", metrics.ActivateViewDockerDesktop), - attribute.Bool("watch.activated", metrics.ActivateWatch), - attribute.Bool("watch.config", metrics.HasWatchConfig), - } - return []trace.SpanStartEventOption{ - trace.WithAttributes(attrs...), - } -} - // ContainerOptions returns common attributes from a Moby container. // // For convenience, it's returned as a SpanOptions object to allow it to be diff --git a/internal/tracing/keyboard_metrics.go b/internal/tracing/keyboard_metrics.go new file mode 100644 index 00000000000..3317879dda1 --- /dev/null +++ b/internal/tracing/keyboard_metrics.go @@ -0,0 +1,36 @@ +/* + Copyright 2024 Docker Compose CLI authors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package tracing + +import ( + "context" + + "go.opentelemetry.io/otel/attribute" +) + +func KeyboardMetrics(ctx context.Context, enabled, isDockerDesktopActive, isWatchConfigured bool) { + commandAvailable := []string{} + if isDockerDesktopActive { + commandAvailable = append(commandAvailable, "gui") + } + if isWatchConfigured { + commandAvailable = append(commandAvailable, "watch") + } + AddAttributeToSpan(ctx, + attribute.Bool("navmenu.enabled", enabled), + attribute.StringSlice("navmenu.command_available", commandAvailable)) +} diff --git a/internal/tracing/wrap.go b/internal/tracing/wrap.go index 812ad22f689..d78b53191b1 100644 --- a/internal/tracing/wrap.go +++ b/internal/tracing/wrap.go @@ -19,6 +19,8 @@ package tracing import ( "context" + "github.com/acarl005/stripansi" + "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/codes" semconv "go.opentelemetry.io/otel/semconv/v1.19.0" "go.opentelemetry.io/otel/trace" @@ -80,12 +82,16 @@ func EventWrapFuncForErrGroup(ctx context.Context, eventName string, opts SpanOp eventOpts := opts.EventOptions() err := fn(ctx) - if err != nil { - eventOpts = append(eventOpts, trace.WithAttributes(semconv.ExceptionMessage(err.Error()))) + eventOpts = append(eventOpts, trace.WithAttributes(semconv.ExceptionMessage(stripansi.Strip(err.Error())))) } span.AddEvent(eventName, eventOpts...) return err } } + +func AddAttributeToSpan(ctx context.Context, attr ...attribute.KeyValue) { + span := trace.SpanFromContext(ctx) + span.SetAttributes(attr...) +} diff --git a/pkg/api/api.go b/pkg/api/api.go index bda2bf9ecb5..ac6a2e0db6d 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -217,9 +217,9 @@ type StartOptions struct { Wait bool WaitTimeout time.Duration // Services passed in the command line to be started - Services []string - Watch bool - NavigationBar bool + Services []string + Watch bool + NavigationMenu bool } // RestartOptions group options of the Restart API diff --git a/pkg/compose/create.go b/pkg/compose/create.go index d6cf1890846..b35c9814f33 100644 --- a/pkg/compose/create.go +++ b/pkg/compose/create.go @@ -152,7 +152,7 @@ func (s *composeService) ensureProjectVolumes(ctx context.Context, project *type } err := func() error { - if s.experiments.AutoFileShares() && s.desktopCli != nil { + if s.experiments.AutoFileShares() && s.isDesktopIntegrationActive() { // collect all the bind mount paths and try to set up file shares in // Docker Desktop for them var paths []string diff --git a/pkg/compose/down.go b/pkg/compose/down.go index d11bfabeeac..ca1f58fe9c9 100644 --- a/pkg/compose/down.go +++ b/pkg/compose/down.go @@ -145,7 +145,7 @@ func (s *composeService) ensureVolumesDown(ctx context.Context, project *types.P }) } - if s.experiments.AutoFileShares() && s.desktopCli != nil { + if s.experiments.AutoFileShares() && s.isDesktopIntegrationActive() { ops = append(ops, func() error { desktop.RemoveFileSharesForProject(ctx, s.desktopCli, project.Name) return nil diff --git a/pkg/compose/up.go b/pkg/compose/up.go index 5f064534146..4d69ee5ec47 100644 --- a/pkg/compose/up.go +++ b/pkg/compose/up.go @@ -75,7 +75,8 @@ func (s *composeService) Up(ctx context.Context, project *types.Project, options first := true gracefulTeardown := func() { printer.Cancel() - fmt.Fprintln(s.stdinfo(), "\033[KGracefully stopping... (press Ctrl+C again to force)") + formatter.ClearLine() + fmt.Fprintln(s.stdinfo(), "Gracefully stopping... (press Ctrl+C again to force)") eg.Go(func() error { err := s.Stop(context.Background(), project.Name, api.StopOptions{ Services: options.Create.Services, @@ -89,16 +90,20 @@ func (s *composeService) Up(ctx context.Context, project *types.Project, options } var kEvents <-chan keyboard.KeyEvent - if options.Start.NavigationBar { + isWatchConfigured := s.shouldWatch(project) + isDockerDesktopActive := s.isDesktopIntegrationActive() + + tracing.KeyboardMetrics(ctx, options.Start.NavigationMenu, isDockerDesktopActive, isWatchConfigured) + if options.Start.NavigationMenu { kEvents, err = keyboard.GetKeys(100) if err != nil { panic(err) } - formatter.NewKeyboardManager(s.isDesktopIntegrationActive(), s.shouldWatch(project), signalChan, s.Watch) + formatter.NewKeyboardManager(ctx, isDockerDesktopActive, isWatchConfigured, signalChan, s.Watch) if options.Start.Watch { formatter.KeyboardManager.StartWatch(ctx, project, options) } - defer keyboard.Close() + defer formatter.KeyboardManager.KeyboardClose() } for { select { @@ -141,7 +146,7 @@ func (s *composeService) Up(ctx context.Context, project *types.Project, options return err }) - if options.Start.Watch && !options.Start.NavigationBar { + if options.Start.Watch && !options.Start.NavigationMenu { eg.Go(func() error { buildOpts := *options.Create.Build buildOpts.Quiet = true diff --git a/pkg/compose/watch.go b/pkg/compose/watch.go index 40840ecabbc..1cf61157e8c 100644 --- a/pkg/compose/watch.go +++ b/pkg/compose/watch.go @@ -176,10 +176,9 @@ func (s *composeService) Watch(ctx context.Context, project *types.Project, serv }) } if !watching { - // options.LogTo.Err(api.WatchLogger, "FAILED") return fmt.Errorf("none of the selected services is configured for watch, consider setting an 'develop' section") } - options.LogTo.Log(api.WatchLogger, "Watch started") + options.LogTo.Log(api.WatchLogger, "Watch enabled") return eg.Wait() } @@ -221,10 +220,10 @@ func (s *composeService) watch(ctx context.Context, project *types.Project, name for { select { case <-quit: - options.LogTo.Log(api.WatchLogger, "Watch stoped") + options.LogTo.Log(api.WatchLogger, "Watch disabled") return nil case err := <-watcher.Errors(): - options.LogTo.Err(api.WatchLogger, "Watch stoped with errors") + options.LogTo.Err(api.WatchLogger, "Watch disabled with errors") return err case event := <-watcher.Events(): hostPath := event.Path()