github.com/btwiuse/jiri@v0.0.0-20191125065820-53353bcfef54/project/utils.go (about) 1 // Copyright 2017 The Fuchsia 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 project 6 7 import ( 8 "fmt" 9 "io" 10 "io/ioutil" 11 "os" 12 "path/filepath" 13 "runtime" 14 "sort" 15 16 "github.com/btwiuse/jiri" 17 "github.com/btwiuse/jiri/gitutil" 18 "github.com/btwiuse/jiri/osutil" 19 "github.com/btwiuse/jiri/retry" 20 ) 21 22 func isFile(file string) (bool, error) { 23 fileInfo, err := os.Stat(file) 24 if err != nil { 25 if os.IsNotExist(err) { 26 return false, nil 27 } 28 return false, fmtError(err) 29 } 30 return !fileInfo.IsDir(), nil 31 } 32 33 func fmtError(err error) error { 34 if err == nil { 35 return nil 36 } 37 _, file, line, _ := runtime.Caller(1) 38 return fmt.Errorf("%s:%d: %s", filepath.Base(file), line, err) 39 } 40 41 func safeWriteFile(jirix *jiri.X, filename string, data []byte) error { 42 tmp := filename + ".tmp" 43 if err := os.MkdirAll(filepath.Dir(filename), 0755); err != nil { 44 return fmtError(err) 45 } 46 if err := ioutil.WriteFile(tmp, data, 0644); err != nil { 47 return fmtError(err) 48 } 49 return fmtError(osutil.Rename(tmp, filename)) 50 } 51 52 func isPathDir(dir string) bool { 53 if dir != "" { 54 if fi, err := os.Stat(dir); err == nil { 55 return fi.IsDir() 56 } 57 } 58 return false 59 } 60 61 func isEmpty(path string) (bool, error) { 62 dir, err := os.Open(path) 63 if err != nil { 64 return false, fmtError(err) 65 } 66 defer dir.Close() 67 68 if _, err = dir.Readdirnames(1); err != nil && err == io.EOF { 69 return true, nil 70 } else { 71 return false, fmtError(err) 72 } 73 } 74 75 // fmtRevision returns the first 8 chars of a revision hash. 76 func fmtRevision(r string) string { 77 l := 8 78 if len(r) < l { 79 return r 80 } 81 return r[:l] 82 } 83 84 // clone is a wrapper that reattempts a git clone operation on failure. 85 func clone(jirix *jiri.X, repo, path string, opts ...gitutil.CloneOpt) error { 86 msg := fmt.Sprintf("Cloning %s", repo) 87 t := jirix.Logger.TrackTime(msg) 88 defer t.Done() 89 return retry.Function(jirix, func() error { 90 return gitutil.New(jirix).Clone(repo, path, opts...) 91 }, msg, retry.AttemptsOpt(jirix.Attempts)) 92 } 93 94 // fetch is a wrapper that reattempts a git fetch operation on failure. 95 func fetch(jirix *jiri.X, path, remote string, opts ...gitutil.FetchOpt) error { 96 msg := fmt.Sprintf("Fetching for %s", path) 97 t := jirix.Logger.TrackTime(msg) 98 defer t.Done() 99 return retry.Function(jirix, func() error { 100 return gitutil.New(jirix, gitutil.RootDirOpt(path)).Fetch(remote, opts...) 101 }, msg, retry.AttemptsOpt(jirix.Attempts)) 102 } 103 104 type MultiError []error 105 106 func (m MultiError) Error() string { 107 s := []string{} 108 n := 0 109 for _, e := range m { 110 if e != nil { 111 s = append(s, e.Error()) 112 n++ 113 } 114 } 115 sort.Strings(s) 116 switch n { 117 case 0: 118 return "(0 errors)" 119 case 1: 120 return s[0] 121 case 2: 122 return s[0] + " (and 1 other error not shown here)" 123 } 124 return fmt.Sprintf("%s (and %d other errors not shown here)", s[0], n-1) 125 }