-
Notifications
You must be signed in to change notification settings - Fork 2
Show container start guidance on more commands #168
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -25,9 +25,11 @@ func newStatusCmd(cfg *env.Env, tel *telemetry.Client) *cobra.Command { | |
| RunE: commandWithTelemetry("status", tel, func(cmd *cobra.Command, args []string) error { | ||
| rt, err := runtime.NewDockerRuntime(cfg.DockerHost) | ||
| if err != nil { | ||
| return dockerNotAvailableError(err) | ||
| } | ||
gtsiolis marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| if err := checkRuntimeHealth(cmd.Context(), rt); err != nil { | ||
| return err | ||
| } | ||
|
Comment on lines
26
to
32
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Inconsistent error handling between commands and duplicate logic.
Additionally, the error emitted here (lines 28-35) differs from Consider:
🔧 One possible approach: reuse EmitUnhealthyError pattern rt, err := runtime.NewDockerRuntime(cfg.DockerHost)
if err != nil {
- output.EmitError(output.NewPlainSink(os.Stdout), output.ErrorEvent{
- Title: "Docker is not available",
- Summary: err.Error(),
- Actions: []output.ErrorAction{
- {Label: "See help:", Value: "lstk -h"},
- {Label: "Install Docker:", Value: "https://docs.docker.com/get-docker/"},
- },
- })
- return output.NewSilentError(err)
+ // Create a minimal runtime just for error emission, or inline the error event
+ sink := output.NewPlainSink(os.Stdout)
+ output.EmitError(sink, output.ErrorEvent{
+ Title: "Docker is not available",
+ Summary: fmt.Sprintf("cannot initialize Docker client: %v", err),
+ Actions: dockerInstallActions(), // shared helper with OS-specific actions
+ })
+ return output.NewSilentError(err)
}🤖 Prompt for AI Agents |
||
|
|
||
| appCfg, err := config.Get() | ||
| if err != nil { | ||
| return fmt.Errorf("failed to get config: %w", err) | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4,6 +4,16 @@ import ( | |
| "fmt" | ||
| "strings" | ||
| "time" | ||
|
|
||
| "github.com/charmbracelet/lipgloss" | ||
| ) | ||
|
|
||
| var ( | ||
| errorTitleStyle = lipgloss.NewStyle().Bold(true).Foreground(lipgloss.Color("#C33820")) | ||
| errorSecondaryStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("241")) | ||
| errorActionStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("69")) | ||
| errorValueStyle = lipgloss.NewStyle().Bold(true) | ||
| errorMutedStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("245")) | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. issue: do we really mean to redeclare all these, since they already exist in |
||
| ) | ||
|
|
||
| // FormatEventLine converts an output event into a single display line. | ||
|
|
@@ -130,21 +140,27 @@ func formatMessageEvent(e MessageEvent) string { | |
|
|
||
| func formatErrorEvent(e ErrorEvent) string { | ||
| var sb strings.Builder | ||
| sb.WriteString("Error: ") | ||
| sb.WriteString(e.Title) | ||
| sb.WriteString(errorTitleStyle.Render("✗ " + e.Title)) | ||
| if e.Summary != "" { | ||
| sb.WriteString("\n ") | ||
| sb.WriteString("\n") | ||
| sb.WriteString(errorSecondaryStyle.Render("> ")) | ||
| sb.WriteString(e.Summary) | ||
| } | ||
| if e.Detail != "" { | ||
| sb.WriteString("\n ") | ||
| sb.WriteString(e.Detail) | ||
| sb.WriteString(errorMutedStyle.Render(e.Detail)) | ||
| } | ||
| for _, action := range e.Actions { | ||
| sb.WriteString("\n " + ErrorActionPrefix) | ||
| sb.WriteString(action.Label) | ||
| sb.WriteString(" ") | ||
| sb.WriteString(action.Value) | ||
| if len(e.Actions) > 0 { | ||
| sb.WriteString("\n") | ||
| for i, action := range e.Actions { | ||
| sb.WriteString("\n") | ||
| if i > 0 { | ||
| sb.WriteString(errorMutedStyle.Render(ErrorActionPrefix + action.Label + " " + action.Value)) | ||
| } else { | ||
| sb.WriteString(errorActionStyle.Render(ErrorActionPrefix+action.Label+" ")) | ||
| sb.WriteString(errorValueStyle.Render(action.Value)) | ||
| } | ||
| } | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. question: I would like to hear what @silv-io says here. We were previously not doing any styling in non-interactive mode.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The plain output should remain styling free IMO. If we want a non-interactive styled output I think that should be a separate sink
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why do you think so?
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the trigger word there is "most" for me. I don't want to force color onto systems where it might not be rendered well. If we add color styling to the plain output, we should at the least check that it will handle
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think for now we could just remove colors/styling from plain sink and revisit later if we need a third kind of sink. |
||
| } | ||
|
Comment on lines
+153
to
164
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
# Check if there are any tests covering multiple ErrorActions
rg -n 'Actions:.*ErrorAction.*ErrorAction' --type go
# Also check for tests with Actions array having more than one element
rg -n 'Actions: \[\]output\.ErrorAction\{' -A5 --type go | head -50Repository: localstack/lstk Length of output: 1684 🏁 Script executed: cat -n internal/ui/components/error_display_test.go | head -100Repository: localstack/lstk Length of output: 3811 🏁 Script executed: cat -n internal/output/plain_format.go | sed -n '153,164p'Repository: localstack/lstk Length of output: 524 🏁 Script executed: rg -n 'errorActionStyle\|errorValueStyle\|errorMutedStyle' --type go -B2 -A2Repository: localstack/lstk Length of output: 41 🏁 Script executed: rg -n 'errorMutedStyle|errorActionStyle|errorValueStyle' -B3 -A3Repository: localstack/lstk Length of output: 1930 🏁 Script executed: fd 'error_display.go|plain_format.go' --type fRepository: localstack/lstk Length of output: 130 🏁 Script executed: cat -n internal/output/plain_format.go | sed -n '1,50p'Repository: localstack/lstk Length of output: 1700 Add test coverage to validate the visual styling differentiation for multiple actions. The code implements distinct styling: the first action renders with Add a test assertion that validates the rendered output distinguishes the first action visually from subsequent ones to document this intentional design. 🤖 Prompt for AI Agents |
||
| return sb.String() | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,64 @@ | ||
| package integration_test | ||
|
|
||
| import ( | ||
| "runtime" | ||
| "testing" | ||
|
|
||
| "github.com/localstack/lstk/test/integration/env" | ||
| "github.com/stretchr/testify/assert" | ||
| "github.com/stretchr/testify/require" | ||
| ) | ||
|
|
||
| // unhealthyDockerEnv returns an environment with DOCKER_HOST pointing to a | ||
| // non-existent Unix socket so that the Docker health check fails. | ||
| func unhealthyDockerEnv() env.Environ { | ||
| return env.With(env.Key("DOCKER_HOST"), "unix:///var/run/docker-does-not-exist.sock") | ||
| } | ||
|
|
||
| func TestStartShowsDockerErrorWhenUnhealthy(t *testing.T) { | ||
| if runtime.GOOS == "windows" { | ||
| t.Skip("Unix socket test") | ||
| } | ||
|
|
||
| stdout, _, err := runLstk(t, testContext(t), "", unhealthyDockerEnv(), "start") | ||
| require.Error(t, err) | ||
| requireExitCode(t, 1, err) | ||
| assert.Contains(t, stdout, "Docker is not available") | ||
| assert.Contains(t, stdout, "Install Docker:") | ||
| } | ||
|
|
||
| func TestStopShowsDockerErrorWhenUnhealthy(t *testing.T) { | ||
| if runtime.GOOS == "windows" { | ||
| t.Skip("Unix socket test") | ||
| } | ||
|
|
||
| stdout, _, err := runLstk(t, testContext(t), "", unhealthyDockerEnv(), "stop") | ||
| require.Error(t, err) | ||
| requireExitCode(t, 1, err) | ||
| assert.Contains(t, stdout, "Docker is not available") | ||
| assert.Contains(t, stdout, "Install Docker:") | ||
| } | ||
|
|
||
| func TestStatusShowsDockerErrorWhenUnhealthy(t *testing.T) { | ||
| if runtime.GOOS == "windows" { | ||
| t.Skip("Unix socket test") | ||
| } | ||
|
|
||
| stdout, _, err := runLstk(t, testContext(t), "", unhealthyDockerEnv(), "status") | ||
| require.Error(t, err) | ||
| requireExitCode(t, 1, err) | ||
| assert.Contains(t, stdout, "Docker is not available") | ||
| assert.Contains(t, stdout, "Install Docker:") | ||
| } | ||
|
|
||
| func TestLogsShowsDockerErrorWhenUnhealthy(t *testing.T) { | ||
| if runtime.GOOS == "windows" { | ||
| t.Skip("Unix socket test") | ||
| } | ||
|
|
||
| stdout, _, err := runLstk(t, testContext(t), "", unhealthyDockerEnv(), "logs") | ||
| require.Error(t, err) | ||
| requireExitCode(t, 1, err) | ||
| assert.Contains(t, stdout, "Docker is not available") | ||
| assert.Contains(t, stdout, "Install Docker:") | ||
| } |
Uh oh!
There was an error while loading. Please reload this page.