github.com/bilus/oya@v0.0.3-0.20190301162104-da4acbd394c6/pkg/project/list.go (about)

     1  package project
     2  
     3  import (
     4  	"os"
     5  	"path/filepath"
     6  	"strings"
     7  
     8  	"github.com/bilus/oya/pkg/oyafile"
     9  	"github.com/bilus/oya/pkg/raw"
    10  	"github.com/pkg/errors"
    11  	"k8s.io/helm/pkg/ignore"
    12  )
    13  
    14  func (p *Project) Oyafiles() ([]*oyafile.Oyafile, error) {
    15  	return listOyafiles(p.RootDir, p.RootDir)
    16  }
    17  
    18  func (p *Project) List(startDir string) ([]*oyafile.Oyafile, error) {
    19  	return listOyafiles(startDir, p.RootDir)
    20  }
    21  
    22  // TODO: Cleanup, should probably be Project.List.
    23  func listOyafiles(startDir, rootDir string) ([]*oyafile.Oyafile, error) {
    24  	skip := makeSkipFunc(startDir, rootDir)
    25  	ignore, err := makeIgnoreFunc(rootDir)
    26  	if err != nil {
    27  		return nil, errors.Wrapf(err, "error setting up ignores in %v", startDir)
    28  	}
    29  	var oyafiles []*oyafile.Oyafile
    30  	return oyafiles, filepath.Walk(startDir, func(path string, info os.FileInfo, err error) error {
    31  		if err != nil {
    32  			return err
    33  		}
    34  		if !info.IsDir() {
    35  			return nil
    36  		}
    37  		doSkip, err := skip(path)
    38  		if err != nil {
    39  			return errors.Wrapf(err, "error trying to determine if %v should be skipped", path)
    40  		}
    41  		if doSkip {
    42  			return filepath.SkipDir
    43  		}
    44  		oyafile, ok, err := oyafile.LoadFromDir(path, rootDir)
    45  		if err != nil {
    46  			return errors.Wrapf(err, "error loading Oyafile from %v", path)
    47  		}
    48  		if !ok {
    49  			return nil
    50  		}
    51  		doIgnore, err := ignore(oyafile)
    52  		if err != nil {
    53  			return errors.Wrapf(err, "error trying to determine if %v should be ignored", oyafile.Path)
    54  		}
    55  		if doIgnore {
    56  			return filepath.SkipDir
    57  		}
    58  		oyafiles = append(oyafiles, oyafile)
    59  		return nil
    60  	})
    61  }
    62  
    63  // makeSkipFunc returns a function that given a path, returns
    64  // true if the entire subdirectory should be ignored.
    65  // Similar to makeIgnoreFunc but does not parse Oyafile, thus allowing
    66  // for broken Oyafile projects nested under the current project.
    67  func makeSkipFunc(startDir, rootDir string) func(path string) (bool, error) {
    68  	return func(path string) (bool, error) {
    69  		// Exclude projects nested under the current project.
    70  
    71  		raw, ok, err := raw.LoadFromDir(path, rootDir)
    72  		if !ok {
    73  			return false, nil
    74  		}
    75  		if err != nil {
    76  			return false, err
    77  		}
    78  
    79  		isRoot, err := raw.IsRoot()
    80  		if err != nil {
    81  			return false, err
    82  		}
    83  
    84  		// BUG(bilus): Clean up this magic string & logic duplication everywhere.
    85  		_, isProject, err := raw.LookupKey("Project")
    86  		if err != nil {
    87  			return false, err
    88  		}
    89  
    90  		return isProject && !isRoot, nil
    91  	}
    92  }
    93  
    94  // makeIgnoreFunc returns a function that given an oyafile returns true if its containing directory tree should be recursively ignored.
    95  // It uses an array of relative paths under "Ignore:" key in the project's root Oyafile.
    96  // BUG(bilus): We should probably make it more intuitive by supporting Ignore: directives in nested dirs as well as the root dir.
    97  func makeIgnoreFunc(rootDir string) (func(*oyafile.Oyafile) (bool, error), error) {
    98  	o, ok, err := oyafile.LoadFromDir(rootDir, rootDir)
    99  	if err != nil {
   100  		return nil, errors.Wrapf(err, "error looking for Ignore: directive")
   101  	}
   102  	if !ok {
   103  		return nil, errors.Errorf("No oyafile found at %v", rootDir)
   104  	}
   105  	ignore, err := ignore.Parse(strings.NewReader(o.Ignores()))
   106  	if err != nil {
   107  		return nil, errors.Wrapf(err, "Ignore: in %v contains invalid entries", o.Path)
   108  	}
   109  	return func(o *oyafile.Oyafile) (bool, error) {
   110  		fi, err := os.Stat(o.Path)
   111  		if err != nil {
   112  			return true, err
   113  		}
   114  
   115  		return ignore.Ignore(o.RelPath(), fi), nil
   116  	}, nil
   117  }