From f23c35645c6f2b01111be6c355f98f80c102e86a Mon Sep 17 00:00:00 2001 From: Thomas Camlong Date: Sat, 1 Aug 2026 23:40:41 +0200 Subject: [PATCH] fix(dev): surface registry pull errors --- tools/homarr-dev/internal/tui/dev.go | 21 +++++++++++++++++---- tools/homarr-dev/internal/tui/dev_test.go | 14 +++++++++++--- 2 files changed, 28 insertions(+), 7 deletions(-) diff --git a/tools/homarr-dev/internal/tui/dev.go b/tools/homarr-dev/internal/tui/dev.go index 9aed6b0a7..5d3ed5b28 100644 --- a/tools/homarr-dev/internal/tui/dev.go +++ b/tools/homarr-dev/internal/tui/dev.go @@ -154,15 +154,21 @@ func startPull(ctx context.Context, cmd *exec.Cmd, events chan<- pullEvent) tea. defer reader.Close() scanner := bufio.NewScanner(reader) scanner.Split(splitDockerProgress) + lastLine := "" for scanner.Scan() { line := strings.TrimSpace(scanner.Text()) - if line != "" && ctx.Err() == nil && len(events) < cap(events)-1 { - events <- pullEvent{line: line} + if line != "" { + lastLine = line + if ctx.Err() == nil && len(events) < cap(events)-1 { + events <- pullEvent{line: line} + } } } err := cmd.Wait() if err == nil { err = scanner.Err() + } else { + err = pullCommandError(err, lastLine) } events <- pullEvent{done: true, err: err} }() @@ -170,6 +176,13 @@ func startPull(ctx context.Context, cmd *exec.Cmd, events chan<- pullEvent) tea. } } +func pullCommandError(err error, lastLine string) error { + if lastLine == "" { + return err + } + return fmt.Errorf("%w: %s", err, lastLine) +} + func waitPull(events <-chan pullEvent) tea.Cmd { return func() tea.Msg { event, ok := <-events @@ -779,8 +792,8 @@ func (m prsModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { } } if row.kind == "remote" { - if row.imageState != "yes" { - m.status = fmt.Sprintf("PR #%d cannot start: image is %s", row.pr.Number, row.imageState) + if row.imageState == "checking" { + m.status = fmt.Sprintf("PR #%d image check is still in progress", row.pr.Number) return m, nil } return m, m.beginPull(*row) diff --git a/tools/homarr-dev/internal/tui/dev_test.go b/tools/homarr-dev/internal/tui/dev_test.go index 893f105da..647b8ca5e 100644 --- a/tools/homarr-dev/internal/tui/dev_test.go +++ b/tools/homarr-dev/internal/tui/dev_test.go @@ -1,6 +1,7 @@ package tui import ( + "errors" "strings" "testing" @@ -83,9 +84,9 @@ func TestRemotePRBuildUsesTemporaryPRWorkflow(t *testing.T) { } } -func TestPRWithoutImageDoesNotLaunch(t *testing.T) { +func TestPRImageCheckInProgressDoesNotLaunch(t *testing.T) { m := newPRsModel() - m.rows = []prRow{{kind: "remote", pr: gh.PR{Number: 6441, Title: "No image"}, imageState: "no"}} + m.rows = []prRow{{kind: "remote", pr: gh.PR{Number: 6441, Title: "Pending image"}, imageState: "checking"}} m.table.SetRows(buildTableRows(m.rows)) updated, cmd := m.Update(tea.KeyPressMsg(tea.Key{Code: tea.KeyEnter})) @@ -93,11 +94,18 @@ func TestPRWithoutImageDoesNotLaunch(t *testing.T) { if cmd != nil { t.Fatal("unavailable image returned a launch command") } - if !strings.Contains(got.status, "cannot start") { + if !strings.Contains(got.status, "still in progress") { t.Fatalf("status = %q, want launch rejection", got.status) } } +func TestPullErrorIncludesLastDockerMessage(t *testing.T) { + err := pullCommandError(errors.New("exit status 1"), "manifest unknown") + if !strings.Contains(err.Error(), "manifest unknown") { + t.Fatalf("pull error = %v", err) + } +} + func TestDevTableRendersLocalImageSource(t *testing.T) { m := newPRsModel() m.rows = []prRow{{