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

     1  package project
     2  
     3  import (
     4  	"io"
     5  	"path/filepath"
     6  
     7  	"github.com/bilus/oya/pkg/oyafile"
     8  	"github.com/bilus/oya/pkg/raw"
     9  	"github.com/bilus/oya/pkg/task"
    10  	"github.com/bilus/oya/pkg/template"
    11  	"github.com/pkg/errors"
    12  	log "github.com/sirupsen/logrus"
    13  )
    14  
    15  // TODO: Duplicated in oyafile module.
    16  type Project struct {
    17  	RootDir      string
    18  	installDir   string
    19  	dependencies Deps
    20  }
    21  
    22  func Detect(workDir, installDir string) (*Project, error) {
    23  	detectedRootDir, found, err := detectRoot(workDir)
    24  	if err != nil {
    25  		return nil, err
    26  	}
    27  	if !found {
    28  		return nil, ErrNoProject{Path: workDir}
    29  	}
    30  	return &Project{
    31  		RootDir:      detectedRootDir,
    32  		installDir:   installDir,
    33  		dependencies: nil, // lazily-loaded in Deps()
    34  	}, nil
    35  }
    36  
    37  func (p *Project) Run(workDir string, taskName task.Name, recurse, useChangeset bool, scope template.Scope, stdout, stderr io.Writer) error {
    38  	log.Debugf("Task %q at %v", taskName, workDir)
    39  
    40  	targets, err := p.RunTargets(workDir, recurse, useChangeset)
    41  	if err != nil {
    42  		return err
    43  	}
    44  
    45  	if len(targets) == 0 {
    46  		return nil
    47  	}
    48  
    49  	dependencies, err := p.Deps()
    50  	if err != nil {
    51  		return err
    52  	}
    53  
    54  	foundAtLeastOneTask := false
    55  	for _, o := range targets {
    56  		err = o.Build(dependencies)
    57  		if err != nil {
    58  			return errors.Wrapf(err, "error in %v", o.Path)
    59  		}
    60  		found, err := o.RunTask(taskName, scope, stdout, stderr)
    61  		if err != nil {
    62  			return errors.Wrapf(err, "error in %v", o.Path)
    63  		}
    64  		if found {
    65  			foundAtLeastOneTask = found
    66  		}
    67  	}
    68  	if !foundAtLeastOneTask {
    69  		return ErrNoTask{
    70  			Task: taskName,
    71  		}
    72  	}
    73  	return nil
    74  }
    75  
    76  func (p *Project) RunTargets(workDir string, recurse, useChangeset bool) ([]*oyafile.Oyafile, error) {
    77  	if useChangeset {
    78  		changes, err := p.Changeset(workDir)
    79  		if err != nil {
    80  			return nil, err
    81  		}
    82  
    83  		if len(changes) == 0 {
    84  			return nil, nil
    85  		}
    86  
    87  		if !recurse {
    88  			return p.oneTargetIn(workDir)
    89  		} else {
    90  			return changes, nil
    91  		}
    92  	} else {
    93  		if !recurse {
    94  			return p.oneTargetIn(workDir)
    95  		} else {
    96  			return p.List(workDir)
    97  		}
    98  	}
    99  }
   100  
   101  func (p *Project) oneTargetIn(dir string) ([]*oyafile.Oyafile, error) {
   102  	o, err := p.oyafileIn(dir)
   103  	if err != nil {
   104  		return nil, err
   105  	}
   106  	return []*oyafile.Oyafile{o}, nil
   107  }
   108  
   109  func (p *Project) oyafileIn(dir string) (*oyafile.Oyafile, error) {
   110  	o, found, err := oyafile.LoadFromDir(dir, p.RootDir)
   111  	if err != nil {
   112  		return nil, err
   113  	}
   114  	if !found {
   115  		return nil, ErrNoOyafile{Path: dir}
   116  	}
   117  	return o, nil
   118  }
   119  
   120  func (p *Project) rawOyafileIn(dir string) (*raw.Oyafile, error) {
   121  	o, found, err := raw.LoadFromDir(dir, p.RootDir)
   122  	if err != nil {
   123  		return nil, err
   124  	}
   125  	if !found {
   126  		return nil, ErrNoOyafile{Path: dir}
   127  	}
   128  
   129  	return o, nil
   130  }
   131  
   132  func (p *Project) Oyafile(oyafilePath string) (*oyafile.Oyafile, bool, error) {
   133  	return oyafile.Load(oyafilePath, p.RootDir)
   134  }
   135  
   136  func (p *Project) Values() (template.Scope, error) {
   137  	oyafilePath := filepath.Join(p.RootDir, "Oyafile")
   138  	o, found, err := p.Oyafile(oyafilePath)
   139  	if err != nil {
   140  		return template.Scope{}, err
   141  	}
   142  	if !found {
   143  		return template.Scope{}, ErrNoOyafile{Path: oyafilePath}
   144  	}
   145  	return template.Scope{
   146  		"Project": o.Project,
   147  	}, nil
   148  }
   149  
   150  // detectRoot attempts to detect the root project directory marked by
   151  // root Oyafile, i.e. one containing Project: directive.
   152  // It walks the directory tree, starting from startDir, going upwards,
   153  // looking for root.
   154  func detectRoot(startDir string) (string, bool, error) {
   155  	path := startDir
   156  	maxParts := 256
   157  	for i := 0; i < maxParts; i++ {
   158  		raw, found, err := raw.LoadFromDir(path, path) // "Guess" path is the root dir.
   159  		if err == nil && found {
   160  			isRoot, err := raw.IsRoot()
   161  			if err != nil {
   162  				return "", false, err
   163  			}
   164  			if isRoot {
   165  				return path, true, nil
   166  			}
   167  		}
   168  
   169  		if path == "/" {
   170  			break
   171  		}
   172  		path = filepath.Dir(path)
   173  	}
   174  
   175  	return "", false, nil
   176  }