Name the cause of a failed toolchain or module download, not Go's progress line before it (issue 1560, review round 1)
mesh/merge-gate head 3c4deb8bee8b merged into trunk 76bb22c36270 — pass: builds mesh-build-go → g14, novox, shanks; no bus step; every machine composes wit…
mesh/repo-check head 3c4deb8bee8b merged into trunk 76bb22c36270 — pass: its merge-check.sh passed, each part in its toolchain: merge-check.sh (go), merge-…
mesh/delivery delivered

This commit is contained in:
2026-10-21 00:59:12 +00:00
parent 4605fcf46d
commit 3c4deb8bee
5 changed files with 28 additions and 5 deletions
@@ -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 {
@@ -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)
}
@@ -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 != "" {
@@ -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)
}
@@ -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)
}
}