github.com/loggregator/cli@v6.33.1-0.20180224010324-82334f081791+incompatible/cf/appfiles/app_files.go (about)

     1  package appfiles
     2  
     3  import (
     4  	"crypto/sha1"
     5  	"fmt"
     6  	"io"
     7  	"io/ioutil"
     8  	"os"
     9  	"path/filepath"
    10  	"runtime"
    11  
    12  	"code.cloudfoundry.org/cli/cf/models"
    13  	"code.cloudfoundry.org/gofileutils/fileutils"
    14  )
    15  
    16  const windowsPathPrefix = `\\?\`
    17  
    18  //go:generate counterfeiter . AppFiles
    19  
    20  type AppFiles interface {
    21  	AppFilesInDir(dir string) (appFiles []models.AppFileFields, err error)
    22  	CopyFiles(appFiles []models.AppFileFields, fromDir, toDir string) (err error)
    23  	CountFiles(directory string) int64
    24  	WalkAppFiles(dir string, onEachFile func(string, string) error) (err error)
    25  }
    26  
    27  type ApplicationFiles struct{}
    28  
    29  func (appfiles ApplicationFiles) AppFilesInDir(dir string) ([]models.AppFileFields, error) {
    30  	appFiles := []models.AppFileFields{}
    31  
    32  	fullDirPath, toplevelErr := filepath.Abs(dir)
    33  	if toplevelErr != nil {
    34  		return appFiles, toplevelErr
    35  	}
    36  
    37  	toplevelErr = appfiles.WalkAppFiles(fullDirPath, func(fileName string, fullPath string) error {
    38  		fileInfo, err := os.Lstat(fullPath)
    39  		if err != nil {
    40  			return err
    41  		}
    42  
    43  		appFile := models.AppFileFields{
    44  			Path: filepath.ToSlash(fileName),
    45  			Size: fileInfo.Size(),
    46  		}
    47  
    48  		if fileInfo.IsDir() {
    49  			appFile.Sha1 = "0"
    50  			appFile.Size = 0
    51  		} else {
    52  			sha, err := appfiles.shaFile(fullPath)
    53  			if err != nil {
    54  				return err
    55  			}
    56  			appFile.Sha1 = sha
    57  		}
    58  
    59  		appFiles = append(appFiles, appFile)
    60  
    61  		return nil
    62  	})
    63  
    64  	return appFiles, toplevelErr
    65  }
    66  
    67  func (appfiles ApplicationFiles) shaFile(fullPath string) (string, error) {
    68  	hash := sha1.New()
    69  	file, err := os.Open(fullPath)
    70  	if err != nil {
    71  		return "", err
    72  	}
    73  	defer file.Close()
    74  
    75  	_, err = io.Copy(hash, file)
    76  	if err != nil {
    77  		return "", err
    78  	}
    79  
    80  	return fmt.Sprintf("%x", hash.Sum(nil)), nil
    81  }
    82  
    83  func (appfiles ApplicationFiles) CopyFiles(appFiles []models.AppFileFields, fromDir, toDir string) error {
    84  	for _, file := range appFiles {
    85  		err := func() error {
    86  			fromPath, err := filepath.Abs(filepath.Join(fromDir, file.Path))
    87  			if err != nil {
    88  				return err
    89  			}
    90  
    91  			if runtime.GOOS == "windows" {
    92  				fromPath = windowsPathPrefix + fromPath
    93  			}
    94  
    95  			srcFileInfo, err := os.Stat(fromPath)
    96  			if err != nil {
    97  				return err
    98  			}
    99  
   100  			toPath, err := filepath.Abs(filepath.Join(toDir, file.Path))
   101  			if err != nil {
   102  				return err
   103  			}
   104  
   105  			if runtime.GOOS == "windows" {
   106  				toPath = windowsPathPrefix + toPath
   107  			}
   108  
   109  			if srcFileInfo.IsDir() {
   110  				err = os.MkdirAll(toPath, srcFileInfo.Mode())
   111  				if err != nil {
   112  					return err
   113  				}
   114  				return nil
   115  			}
   116  
   117  			return appfiles.copyFile(fromPath, toPath, srcFileInfo.Mode())
   118  		}()
   119  
   120  		if err != nil {
   121  			return err
   122  		}
   123  	}
   124  
   125  	return nil
   126  }
   127  
   128  func (appfiles ApplicationFiles) copyFile(srcPath string, dstPath string, fileMode os.FileMode) error {
   129  	dst, err := fileutils.Create(dstPath)
   130  	if err != nil {
   131  		return err
   132  	}
   133  	defer dst.Close()
   134  
   135  	if runtime.GOOS != "windows" {
   136  		err = dst.Chmod(fileMode)
   137  		if err != nil {
   138  			return err
   139  		}
   140  	}
   141  
   142  	src, err := os.Open(srcPath)
   143  	if err != nil {
   144  		return err
   145  	}
   146  	defer src.Close()
   147  
   148  	_, err = io.Copy(dst, src)
   149  	if err != nil {
   150  		return err
   151  	}
   152  
   153  	return nil
   154  }
   155  
   156  func (appfiles ApplicationFiles) CountFiles(directory string) int64 {
   157  	var count int64
   158  	appfiles.WalkAppFiles(directory, func(_, _ string) error {
   159  		count++
   160  		return nil
   161  	})
   162  	return count
   163  }
   164  
   165  func (appfiles ApplicationFiles) WalkAppFiles(dir string, onEachFile func(string, string) error) error {
   166  	cfIgnore := loadIgnoreFile(dir)
   167  	walkFunc := func(fullPath string, f os.FileInfo, err error) error {
   168  		fileRelativePath, _ := filepath.Rel(dir, fullPath)
   169  		fileRelativeUnixPath := filepath.ToSlash(fileRelativePath)
   170  
   171  		if err != nil && runtime.GOOS == "windows" {
   172  			f, err = os.Lstat(windowsPathPrefix + fullPath)
   173  			if err != nil {
   174  				return err
   175  			}
   176  			fullPath = windowsPathPrefix + fullPath
   177  		}
   178  
   179  		if fullPath == dir {
   180  			return nil
   181  		}
   182  
   183  		if cfIgnore.FileShouldBeIgnored(fileRelativeUnixPath) {
   184  			if err == nil && f.IsDir() {
   185  				return filepath.SkipDir
   186  			}
   187  			return nil
   188  		}
   189  
   190  		if err != nil {
   191  			return err
   192  		}
   193  
   194  		if !f.Mode().IsRegular() && !f.IsDir() {
   195  			return nil
   196  		}
   197  
   198  		return onEachFile(fileRelativePath, fullPath)
   199  	}
   200  
   201  	return filepath.Walk(dir, walkFunc)
   202  }
   203  
   204  func loadIgnoreFile(dir string) CfIgnore {
   205  	fileContents, err := ioutil.ReadFile(filepath.Join(dir, ".cfignore"))
   206  	if err != nil {
   207  		return NewCfIgnore("")
   208  	}
   209  
   210  	return NewCfIgnore(string(fileContents))
   211  }