| // Copyright 2022 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package main |
| |
| import ( |
| _ "embed" |
| "errors" |
| "fmt" |
| "os" |
| "os/exec" |
| "path/filepath" |
| "sort" |
| "strings" |
| ) |
| |
| //go:embed _embed/broken.go |
| var brokenScript []byte |
| |
| // listBrokenBuilders returns the builders that are marked |
| // as broken in golang.org/x/build/dashboard at HEAD. |
| func listBrokenBuilders() (broken []string, err error) { |
| defer func() { |
| if err != nil { |
| err = fmt.Errorf("identifying broken builders: %v", err) |
| } |
| }() |
| |
| // Though this be madness, yet there is method in 't. |
| // |
| // Our goals here are: |
| // |
| // 1. Always use the most up-to-date information about broken builders, even |
| // if the user hasn't recently updated the greplogs binary. |
| // |
| // 2. Avoid the need to massively refactor the builder configuration right |
| // now. (Currently, the Go builders are configured programmatically in the |
| // x/build/dashboard package, not in external configuration files.) |
| // |
| // 3. Avoid the need to redeploy a production x/build/cmd/coordinator or |
| // x/build/devapp to pick up changes. (A user triaging test failures might |
| // not have access to deploy the coordinator, or might not want to disrupt |
| // running tests or active gomotes by pushing it.) |
| // |
| // Goals (2) and (3) imply that we must use x/build/dashboard, not fetch the |
| // list from build.golang.org or dev.golang.org. Since that is a Go package, |
| // we must run it as a Go program in order to evaluate it. |
| // |
| // Goal (1) implies that we must use x/build at HEAD, not (say) at whatever |
| // version of x/build this command was built with. We could perhaps relax that |
| // constraint if we move greplogs itself into x/build and consistently triage |
| // using 'go run golang.org/x/build/cmd/greplogs@HEAD' instead of an installed |
| // 'greplogs'. |
| |
| if os.Getenv("GO111MODULE") == "off" { |
| return nil, errors.New("operation requires GO111MODULE=on or auto") |
| } |
| |
| modDir, err := os.MkdirTemp("", "greplogs") |
| if err != nil { |
| return nil, err |
| } |
| defer func() { |
| removeErr := os.RemoveAll(modDir) |
| if err == nil { |
| err = removeErr |
| } |
| }() |
| |
| runCommand := func(name string, args ...string) ([]byte, error) { |
| cmd := exec.Command(name, args...) |
| cmd.Dir = modDir |
| cmd.Env = append(os.Environ(), |
| "PWD="+modDir, // match cmd.Dir |
| "GOPRIVATE=golang.org/x/build", // avoid proxy cache; see https://go.dev/issue/38065 |
| ) |
| cmd.Stderr = new(strings.Builder) |
| |
| out, err := cmd.Output() |
| if err != nil { |
| return out, fmt.Errorf("%s: %w\nstderr:\n%s", strings.Join(cmd.Args, " "), err, cmd.Stderr) |
| } |
| return out, nil |
| } |
| |
| _, err = runCommand("go", "mod", "init", "github.com/aclements/go-misc/greplogs/_embed") |
| if err != nil { |
| return nil, err |
| } |
| |
| _, err = runCommand("go", "get", "golang.org/x/build/dashboard@HEAD") |
| if err != nil { |
| return nil, err |
| } |
| |
| err = os.WriteFile(filepath.Join(modDir, "broken.go"), brokenScript, 0644) |
| if err != nil { |
| return nil, err |
| } |
| |
| out, err := runCommand("go", "run", "broken.go") |
| if err != nil { |
| return nil, err |
| } |
| |
| broken = strings.Split(strings.TrimSpace(string(out)), "\n") |
| sort.Strings(broken) |
| return broken, nil |
| } |