github.com/aclements/go-misc@v0.0.0-20240129233631-2f6ede80790c/findflakes/logs.go (about) 1 // Copyright 2015 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 "encoding/json" 9 "fmt" 10 "io/ioutil" 11 "os" 12 "path/filepath" 13 "strings" 14 "time" 15 16 "golang.org/x/build/types" 17 ) 18 19 type Revision struct { 20 types.BuildRevision 21 Date time.Time 22 23 Builds []*Build 24 25 path string 26 } 27 28 func (r *Revision) String() string { 29 // Use time format from dashboard, plus year. 30 return fmt.Sprintf("%s %s", r.Revision[:7], r.Date.Format("02 Jan 15:04 2006")) 31 } 32 33 func (r *Revision) Subject() string { 34 subject := r.Desc 35 if i := strings.Index(subject, "\n"); i >= 0 { 36 subject = subject[:i] 37 } 38 return subject 39 } 40 41 func (r *Revision) OneLine() string { 42 return fmt.Sprintf("%s %s", r.Revision[:7], r.Subject()) 43 } 44 45 type Build struct { 46 Revision *Revision 47 Builder string 48 Status BuildStatus 49 LogURL string 50 } 51 52 type BuildStatus int 53 54 const ( 55 BuildOK BuildStatus = iota 56 BuildRunning 57 BuildFailed 58 ) 59 60 func (b *Build) LogPath() string { 61 return filepath.Join(b.Revision.path, b.Builder) 62 } 63 64 func (b *Build) ReadLog() ([]byte, error) { 65 return ioutil.ReadFile(b.LogPath()) 66 } 67 68 // LoadRevisions loads all saved build revisions from revDir, which 69 // must be the "rev" directory written by fetchlogs. The returned 70 // revisions are ordered from oldest to newest. 71 func LoadRevisions(revDir string) ([]*Revision, error) { 72 revFiles, err := ioutil.ReadDir(revDir) 73 if err != nil { 74 return nil, err 75 } 76 77 revs := []*Revision{} 78 for _, revFile := range revFiles { 79 if !revFile.IsDir() { 80 continue 81 } 82 83 rev := &Revision{path: filepath.Join(revDir, revFile.Name())} 84 85 // Load revision metadata. 86 var builders []string 87 err1 := readJSONFile(filepath.Join(rev.path, ".rev.json"), &rev.BuildRevision) 88 err2 := readJSONFile(filepath.Join(rev.path, ".builders.json"), &builders) 89 if os.IsNotExist(err1) || os.IsNotExist(err2) { 90 continue 91 } else if err1 != nil { 92 return nil, err1 93 } else if err2 != nil { 94 return nil, err2 95 } 96 97 rev.Date, err = time.Parse(time.RFC3339, rev.BuildRevision.Date) 98 if err != nil { 99 return nil, err 100 } 101 102 rev.Builds = make([]*Build, len(builders)) 103 for i, builder := range builders { 104 var status BuildStatus 105 var logURL string 106 s := rev.Results[i] 107 switch s { 108 case "ok": 109 status = BuildOK 110 case "": 111 status = BuildRunning 112 default: 113 status = BuildFailed 114 logURL = s 115 } 116 rev.Builds[i] = &Build{ 117 Revision: rev, 118 Builder: builder, 119 Status: status, 120 LogURL: logURL, 121 } 122 } 123 124 revs = append(revs, rev) 125 } 126 127 return revs, nil 128 } 129 130 func readJSONFile(path string, v interface{}) error { 131 r, err := os.Open(path) 132 if err != nil { 133 return err 134 } 135 defer r.Close() 136 137 return json.NewDecoder(r).Decode(&v) 138 }