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  }