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

     1  package changeset
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"path/filepath"
     7  	"strings"
     8  
     9  	"github.com/bilus/oya/pkg/oyafile"
    10  	"github.com/bilus/oya/pkg/task"
    11  	"github.com/bilus/oya/pkg/template"
    12  	log "github.com/sirupsen/logrus"
    13  )
    14  
    15  // Calculate gets changeset from each Oyafile by invoking their Changeset: tasks
    16  // and parsing the output.
    17  func Calculate(candidates []*oyafile.Oyafile) ([]*oyafile.Oyafile, error) {
    18  	if len(candidates) == 0 {
    19  		return candidates, nil
    20  	}
    21  
    22  	var changeset []*oyafile.Oyafile
    23  	for _, candidate := range candidates {
    24  		oyafiles, err := calculateChangeset(candidate)
    25  		if err != nil {
    26  			return nil, err
    27  		}
    28  		changeset = append(changeset, oyafiles...)
    29  	}
    30  
    31  	return unique(changeset), nil
    32  }
    33  
    34  func execChangesetTask(workDir string, changesetTask task.Task) ([]string, error) {
    35  	stdout := bytes.NewBuffer(nil)
    36  	stderr := bytes.NewBuffer(nil)
    37  
    38  	err := changesetTask.Exec(workDir, template.Scope{}, stdout, stderr)
    39  	if err != nil {
    40  		return nil, err
    41  	}
    42  	// TODO: We shouldn't be ignoring stderr.
    43  	return parseChangeset(stdout.String())
    44  }
    45  
    46  func calculateChangeset(currOyafile *oyafile.Oyafile) ([]*oyafile.Oyafile, error) {
    47  	changesetTask, ok := currOyafile.Tasks.LookupTask("Changeset")
    48  	if !ok {
    49  		return nil, nil
    50  	}
    51  
    52  	dirs, err := execChangesetTask(currOyafile.Dir, changesetTask)
    53  	if err != nil {
    54  		return nil, err
    55  	}
    56  
    57  	oyafiles := make([]*oyafile.Oyafile, 0, len(dirs))
    58  	for _, dir := range dirs {
    59  		fullPath := filepath.Join(currOyafile.Dir, dir)
    60  		o, exists, err := oyafile.LoadFromDir(fullPath, currOyafile.RootDir)
    61  		if !exists {
    62  			// TODO: Warning that changeset contains paths without Oyafiles?
    63  			log.Printf("Path %v in changeset does not exist", fullPath)
    64  			continue
    65  		}
    66  		if err != nil {
    67  			return nil, err
    68  		}
    69  		oyafiles = append(oyafiles, o)
    70  	}
    71  	return oyafiles, nil
    72  }
    73  
    74  func parseChangeset(changeset string) ([]string, error) {
    75  	dirs := make([]string, 0)
    76  
    77  	// TODO: Ignores stderr for the time being.
    78  	changes := strings.Split(changeset, "\n")
    79  	for _, change := range changes {
    80  		if len(change) == 0 {
    81  			continue
    82  		}
    83  		if change[0] != '+' || len(change) < 2 {
    84  			log.Debugf("  Error: %v", change)
    85  			return nil, fmt.Errorf("Unexpected changeset entry %q expected \"+path\"", change)
    86  		}
    87  		path := change[1:]
    88  		log.Debugf("  Addition: %v", path)
    89  		// TODO: Check if path is valid.
    90  		dirs = append(dirs, path)
    91  	}
    92  
    93  	return dirs, nil
    94  
    95  }
    96  
    97  func normalizePath(oyafileDir, path string) string {
    98  	if filepath.IsAbs(path) {
    99  		return filepath.Clean(path)
   100  	}
   101  	return filepath.Clean(filepath.Join(oyafileDir, path))
   102  }
   103  
   104  func unique(oyafiles []*oyafile.Oyafile) []*oyafile.Oyafile {
   105  	result := make([]*oyafile.Oyafile, 0, len(oyafiles))
   106  	unique := make(map[string]struct{})
   107  	for _, o := range oyafiles {
   108  		_, ok := unique[o.Dir]
   109  		if ok {
   110  			continue
   111  		}
   112  		unique[o.Dir] = struct{}{}
   113  		result = append(result, o)
   114  	}
   115  
   116  	return result
   117  }