diff --git a/modules/mesh-build-go/cmd/mesh-build-go/build.go b/modules/mesh-build-go/cmd/mesh-build-go/build.go index d64769c1..1adb0b5c 100644 --- a/modules/mesh-build-go/cmd/mesh-build-go/build.go +++ b/modules/mesh-build-go/cmd/mesh-build-go/build.go @@ -128,7 +128,7 @@ func (m *Module) build(ctx context.Context, args map[string]any) (any, error) { } cmd := []string{"-C", dir, "build", "-ldflags", linker, "-o", filepath.Join(out, binary), "./" + strings.Trim(art.From, "./")} if r := m.Run(ctx, m.buildEnv(dir), "go", cmd...); r.Status != 0 || r.Err != "" { - return nil, fmt.Errorf("go build of %s: %s", art.Name, firstLine(r.Err+"\n"+r.Stderr+"\n"+r.Stdout)) + return nil, fmt.Errorf("go build of %s: %s", art.Name, causeLine(r.Err+"\n"+r.Stderr+"\n"+r.Stdout)) } archive, digest, err := buildstep.Pack(out) if err != nil { diff --git a/modules/mesh-build-go/cmd/mesh-build-go/digest.go b/modules/mesh-build-go/cmd/mesh-build-go/digest.go index 0adff4f4..2158df98 100644 --- a/modules/mesh-build-go/cmd/mesh-build-go/digest.go +++ b/modules/mesh-build-go/cmd/mesh-build-go/digest.go @@ -148,7 +148,7 @@ func (m *Module) listPackages(ctx context.Context, tree, dir string, tags []stri args = append(args, "./...") r := m.Run(ctx, m.goEnvFor(abs), "go", args...) if r.Status != 0 || r.Err != "" { - return nil, fmt.Errorf("go list in %s: %s", dir, firstLine(r.Err+"\n"+r.Stderr+"\n"+r.Stdout)) + return nil, fmt.Errorf("go list in %s: %s", dir, causeLine(r.Err+"\n"+r.Stderr+"\n"+r.Stdout)) } return parsePackages(r.Stdout) } diff --git a/modules/mesh-build-go/cmd/mesh-build-go/fetch.go b/modules/mesh-build-go/cmd/mesh-build-go/fetch.go index 2318a224..59d18db8 100644 --- a/modules/mesh-build-go/cmd/mesh-build-go/fetch.go +++ b/modules/mesh-build-go/cmd/mesh-build-go/fetch.go @@ -89,7 +89,7 @@ func (m *Module) fetch(ctx context.Context, args map[string]any) (any, error) { for _, verb := range []string{"download", "verify"} { r := m.Run(ctx, m.goEnv(), "go", "-C", filepath.Join(tree, d), "mod", verb) if r.Status != 0 || r.Err != "" { - return nil, fmt.Errorf("go mod %s in %s: %s", verb, d, firstLine(r.Err+"\n"+r.Stdout+"\n"+r.Stderr)) + return nil, fmt.Errorf("go mod %s in %s: %s", verb, d, causeLine(r.Err+"\n"+r.Stdout+"\n"+r.Stderr)) } } } @@ -164,6 +164,18 @@ func (m *Module) clock() time.Time { } func (m *Module) now() time.Time { return m.clock() } +// causeLine is the first line of a go command's output that says something other than Go's own progress: `go: +// downloading ...` comes first when Go fetches a toolchain or a module, and the cause of a failure on a later line +// (issue 1560: a refusal read only "go: downloading go1.27.1 (linux/amd64)"). +func causeLine(s string) string { + for _, l := range strings.Split(s, "\n") { + if l = strings.TrimSpace(l); l != "" && !strings.HasPrefix(l, "go: downloading ") { + return l + } + } + return firstLine(s) +} + func firstLine(s string) string { for _, l := range strings.Split(s, "\n") { if l = strings.TrimSpace(l); l != "" { diff --git a/modules/mesh-build-go/cmd/mesh-build-go/probe.go b/modules/mesh-build-go/cmd/mesh-build-go/probe.go index bfc54209..1d96435b 100644 --- a/modules/mesh-build-go/cmd/mesh-build-go/probe.go +++ b/modules/mesh-build-go/cmd/mesh-build-go/probe.go @@ -55,7 +55,7 @@ func probeGo(ctx context.Context, run forgekey.Runner, home, cache string) (stri r := run(ctx, append(baseEnv(home), probeEnv(cache)...), "go", "version") if r.Status != 0 || r.Err != "" { return "", fmt.Errorf("`go version` with GOTOOLCHAIN=%s failed (the recipe builds with %s): %s", RecipeGo, RecipeGo, - firstLine(r.Err+"\n"+r.Stderr)) + causeLine(r.Err+"\n"+r.Stderr)) } return parseGoVersion(r.Stdout) } diff --git a/modules/mesh-build-go/cmd/mesh-build-go/toolchain_probe_test.go b/modules/mesh-build-go/cmd/mesh-build-go/toolchain_probe_test.go index ac21510a..f2b0a4d5 100644 --- a/modules/mesh-build-go/cmd/mesh-build-go/toolchain_probe_test.go +++ b/modules/mesh-build-go/cmd/mesh-build-go/toolchain_probe_test.go @@ -77,7 +77,7 @@ func TestAFailedFetchOrAnotherVersionMakesHealthUnhealthyNamingBoth(t *testing.T ran forgekey.Ran want []string }{ - {"fetch fails", forgekey.Ran{Status: 1, Stderr: "go: download go1.27.1 for linux/amd64: dial tcp: no route to host\n"}, + {"fetch fails", forgekey.Ran{Status: 1, Stderr: "go: downloading go1.27.1 (linux/amd64)\ngo: download go1.27.1 for linux/amd64: dial tcp: no route to host\n"}, []string{"go1.27.1", "no route to host"}}, {"other version", forgekey.Ran{Stdout: "go version go1.27.1-X:nodwarf5 linux/amd64\n"}, []string{"go1.27.1", "go1.27.1-X:nodwarf5"}}, @@ -122,3 +122,14 @@ func TestTheProbeIsBoundLongOnlyWhileTheReleaseIsMissing(t *testing.T) { t.Errorf("cached release: bound %v", got) } } + +// 1560 (review): Go's progress line is not the cause: the refusal names the line after it. +func TestCauseLineSkipsGosDownloadingLine(t *testing.T) { + got := causeLine("go: downloading go1.27.1 (linux/amd64)\ngo: download go1.27.1: checksum mismatch\n") + if got != "go: download go1.27.1: checksum mismatch" { + t.Errorf("causeLine = %q", got) + } + if got := causeLine("go: downloading go1.27.1 (linux/amd64)\n"); got != "go: downloading go1.27.1 (linux/amd64)" { + t.Errorf("only a progress line: %q", got) + } +}