feat: add Docker Desktop Logs view hints and navigation shortcut#13721
feat: add Docker Desktop Logs view hints and navigation shortcut#13721
Conversation
a7d9937 to
3db809c
Compare
There was a problem hiding this comment.
Pull request overview
Adds Docker Desktop “Logs view” discoverability to Docker Compose by emitting “What’s next” hints via Docker CLI hooks and by introducing an l keyboard shortcut in the compose up navigation menu (gated by Desktop feature flag + user setting).
Changes:
- Introduce a hidden hooks subcommand that returns “Next steps” templates for
docker logs,docker compose logs, anddocker compose up -d. - Add Logs-view keyboard shortcut (
l) to thecompose upnavigation menu when the Desktop LogsTab feature is enabled. - Extend Desktop client integration to check feature flags and Desktop settings (incl. backend socket derivation) and record tracing metrics for availability.
Reviewed changes
Copilot reviewed 10 out of 10 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| pkg/compose/up.go | Computes Desktop/LogsTab availability to gate nav menu behavior + tracing. |
| pkg/compose/desktop.go | Refactors Desktop endpoint detection and adds “feature enabled” helper. |
| internal/tracing/keyboard_metrics.go | Adds logs-view availability to nav menu metrics attributes. |
| internal/desktop/client.go | Adds feature+settings checks and backend socket endpoint derivation. |
| internal/desktop/client_test.go | Adds unit coverage for backend socket endpoint derivation. |
| cmd/main.go | Registers the new hooks subcommand on the root command. |
| cmd/formatter/shortcut.go | Adds l shortcut + handler to open Docker Desktop Logs view. |
| cmd/compose/hooks.go | Implements hidden hooks handler that returns “What’s next” hints. |
| cmd/compose/hooks_test.go | Adds tests for hook request parsing and hint gating. |
| cmd/compose/compose.go | Updates standalone detection to account for hook subcommand. |
Comments suppressed due to low confidence (1)
cmd/main.go:51
- The hook subcommand will still trigger the parent (compose) PersistentPreRunE defined below, so plugin initialization (plugin.PersistentPreRunE/cmdtrace setup) will still run on hook invocations. Overriding PersistentPreRunE on the hook command does not prevent the parent persistent pre-run in Cobra. Consider short-circuiting this wrapper when cmd.Name() == metadata.HookSubcommandName (or similar) so hook execution is fast and does not depend on Docker/CLI initialization.
cmd := commands.RootCommand(cli, backendOptions)
cmd.AddCommand(commands.HooksCommand())
originalPreRunE := cmd.PersistentPreRunE
cmd.PersistentPreRunE = func(cmd *cobra.Command, args []string) error {
// initialize the cli instance
if err := plugin.PersistentPreRunE(cmd, args); err != nil {
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| func HooksCommand() *cobra.Command { | ||
| return &cobra.Command{ | ||
| Use: metadata.HookSubcommandName, | ||
| Hidden: true, | ||
| // Override PersistentPreRunE to prevent the parent's PersistentPreRunE | ||
| // (plugin initialization) from running for hook invocations. | ||
| PersistentPreRunE: func(*cobra.Command, []string) error { return nil }, |
There was a problem hiding this comment.
This comment and the no-op PersistentPreRunE won’t actually prevent the parent command’s PersistentPreRunE from running (Cobra runs parent persistent pre-runs before the subcommand’s). To avoid plugin initialization for hooks, add the gating in the parent PersistentPreRunE (e.g., in cmd/main.go wrapper) or restructure command setup; otherwise hooks will still pay the full init cost.
| func HooksCommand() *cobra.Command { | |
| return &cobra.Command{ | |
| Use: metadata.HookSubcommandName, | |
| Hidden: true, | |
| // Override PersistentPreRunE to prevent the parent's PersistentPreRunE | |
| // (plugin initialization) from running for hook invocations. | |
| PersistentPreRunE: func(*cobra.Command, []string) error { return nil }, | |
| // | |
| // Note: a child command's PersistentPreRunE does not prevent a parent's | |
| // PersistentPreRunE from running in Cobra. Any optimization to skip parent | |
| // plugin initialization for hook invocations must be implemented where the | |
| // parent command is configured. | |
| func HooksCommand() *cobra.Command { | |
| return &cobra.Command{ | |
| Use: metadata.HookSubcommandName, | |
| Hidden: true, |
| // Settings fetches the Docker Desktop application settings. | ||
| func (c *Client) Settings(ctx context.Context) (*SettingsResponse, error) { | ||
| req, err := c.newRequest(ctx, http.MethodGet, "/app/settings", http.NoBody) | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
|
|
||
| resp, err := c.client.Do(req) | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
| defer func() { | ||
| _ = resp.Body.Close() | ||
| }() | ||
|
|
||
| if resp.StatusCode != http.StatusOK { | ||
| return nil, fmt.Errorf("unexpected status code: %d", resp.StatusCode) | ||
| } | ||
|
|
||
| var ret SettingsResponse | ||
| if err := json.NewDecoder(resp.Body).Decode(&ret); err != nil { | ||
| return nil, err | ||
| } | ||
| return &ret, nil | ||
| } | ||
|
|
||
| // IsFeatureEnabled checks both the feature flag (GET /features) and the user | ||
| // setting (GET /app/settings) for a given feature. Returns true only when the | ||
| // feature is both rolled out and enabled by the user. Features without a | ||
| // corresponding setting entry are considered enabled if the flag is set. | ||
| func (c *Client) IsFeatureEnabled(ctx context.Context, feature string) (bool, error) { |
There was a problem hiding this comment.
New Settings() and IsFeatureEnabled() logic (feature flag + per-feature settings check, including backendSocketEndpoint derivation) is not covered by tests in this package. Consider adding unit tests that stub the HTTP client/transport (and ideally the backend client creation) to verify: feature absent/disabled, feature enabled with no setting check, and FeatureLogsTab enabled with settings true/false.
Add CLI hooks handler to show "What's next:" hints pointing to the Docker Desktop Logs view after `docker logs`, `docker compose logs`, and `docker compose up -d`. Add `l` keyboard shortcut in the `compose up` navigation menu to open the Logs view, gated on Docker Desktop feature flag and settings. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Guillaume Lours <glours@users.noreply.github.com>
3db809c to
ed28998
Compare
| // hooksHints maps hook root commands to their hint definitions. | ||
| var hooksHints = map[string]hookHint{ | ||
| // standalone "docker logs" (not a compose subcommand) | ||
| "logs": {template: dockerLogsHint}, |
There was a problem hiding this comment.
unclear to me why we provide docker logs hint inside compose. Maybe just because we have no place in docker/cli for this and compose used to be installed by 99% users?
There was a problem hiding this comment.
It's because Compose is in charge of this hint globally, it could trigger the hint display to any other cli plugins too.
What I did
Add CLI hooks handler to show "What's next:" hints pointing to the Docker Desktop Logs view after
docker logs,docker compose logs, anddocker compose up -d.Add
lkeyboard shortcut in thecompose upnavigation menu to open the Logs view, gated on Docker Desktop feature flag and settings.Related issue
N/A
(not mandatory) A picture of a cute animal, if possible in relation to what you did
