golang.org/x/build@v0.0.0-20240506185731-218518f32b70/cmd/greplogs/broken.go (about) 1 // Copyright 2022 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package main 6 7 import ( 8 _ "embed" 9 "errors" 10 "fmt" 11 "os" 12 "os/exec" 13 "path/filepath" 14 "sort" 15 "strings" 16 ) 17 18 //go:embed _embed/broken.go 19 var brokenScript []byte 20 21 // listBrokenBuilders returns the builders that are marked 22 // as broken in golang.org/x/build/dashboard at HEAD. 23 func listBrokenBuilders() (broken []string, err error) { 24 defer func() { 25 if err != nil { 26 err = fmt.Errorf("identifying broken builders: %v", err) 27 } 28 }() 29 30 // Though this be madness, yet there is method in 't. 31 // 32 // Our goals here are: 33 // 34 // 1. Always use the most up-to-date information about broken builders, even 35 // if the user hasn't recently updated the greplogs binary. 36 // 37 // 2. Avoid the need to massively refactor the builder configuration right 38 // now. (Currently, the Go builders are configured programmatically in the 39 // x/build/dashboard package, not in external configuration files.) 40 // 41 // 3. Avoid the need to redeploy a production x/build/cmd/coordinator or 42 // x/build/devapp to pick up changes. (A user triaging test failures might 43 // not have access to deploy the coordinator, or might not want to disrupt 44 // running tests or active gomotes by pushing it.) 45 // 46 // Goals (2) and (3) imply that we must use x/build/dashboard, not fetch the 47 // list from build.golang.org or dev.golang.org. Since that is a Go package, 48 // we must run it as a Go program in order to evaluate it. 49 // 50 // Goal (1) implies that we must use x/build at HEAD, not (say) at whatever 51 // version of x/build this command was built with. We could perhaps relax that 52 // constraint if we move greplogs itself into x/build and consistently triage 53 // using 'go run golang.org/x/build/cmd/greplogs@HEAD' instead of an installed 54 // 'greplogs'. 55 56 if os.Getenv("GO111MODULE") == "off" { 57 return nil, errors.New("operation requires GO111MODULE=on or auto") 58 } 59 60 modDir, err := os.MkdirTemp("", "greplogs") 61 if err != nil { 62 return nil, err 63 } 64 defer func() { 65 removeErr := os.RemoveAll(modDir) 66 if err == nil { 67 err = removeErr 68 } 69 }() 70 71 runCommand := func(name string, args ...string) ([]byte, error) { 72 cmd := exec.Command(name, args...) 73 cmd.Dir = modDir 74 cmd.Env = append(os.Environ(), 75 "PWD="+modDir, // match cmd.Dir 76 "GOPRIVATE=golang.org/x/build", // avoid proxy cache; see https://go.dev/issue/38065 77 ) 78 cmd.Stderr = new(strings.Builder) 79 80 out, err := cmd.Output() 81 if err != nil { 82 return out, fmt.Errorf("%s: %w\nstderr:\n%s", strings.Join(cmd.Args, " "), err, cmd.Stderr) 83 } 84 return out, nil 85 } 86 87 _, err = runCommand("go", "mod", "init", "github.com/aclements/go-misc/greplogs/_embed") 88 if err != nil { 89 return nil, err 90 } 91 92 _, err = runCommand("go", "get", "golang.org/x/build/dashboard@HEAD") 93 if err != nil { 94 return nil, err 95 } 96 97 err = os.WriteFile(filepath.Join(modDir, "broken.go"), brokenScript, 0644) 98 if err != nil { 99 return nil, err 100 } 101 102 out, err := runCommand("go", "run", "broken.go") 103 if err != nil { 104 return nil, err 105 } 106 107 broken = strings.Split(strings.TrimSpace(string(out)), "\n") 108 sort.Strings(broken) 109 return broken, nil 110 }