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 }