github.com/comcast/canticle@v0.0.0-20161108184242-c53cface56e8/canticles/cantdeploader.go (about)

     1  package canticles
     2  
     3  import (
     4  	"fmt"
     5  	"sync"
     6  )
     7  
     8  // A CantDepReader should return the CanticleDependencies of a
     9  // package. Usually stored in the Canticle file.
    10  type CantDepReader interface {
    11  	CanticleDependencies(pkg string) ([]*CanticleDependency, error)
    12  }
    13  
    14  // A CanticleDepLoader is used to fetch the dependencies of a for a
    15  // set of CanticleDependencies. It uses a reader for fetchpath to read
    16  // the dependencies in a path and a resolver to resolve the vcs for
    17  // each path and actually fetch the dep. Update can be set to udpate
    18  // branches for a dep.
    19  type CanticleDepLoader struct {
    20  	Reader   CantDepReader
    21  	Resolver RepoResolver
    22  	Gopath   string
    23  	Update   bool
    24  	updated  map[string]string
    25  	Limit    int
    26  }
    27  
    28  // FetchPath fetches the dependencies in a Canticle file at path. It
    29  // will return an array of errors encountered while fetching those
    30  // deps.
    31  func (cdl *CanticleDepLoader) FetchPath(path string) []error {
    32  	LogVerbose("Reading %s canticle deps", path)
    33  	pkg, err := PackageName(cdl.Gopath, path)
    34  	if err != nil {
    35  		return []error{err}
    36  	}
    37  	cdeps, err := cdl.Reader.CanticleDependencies(pkg)
    38  	if err != nil {
    39  		return []error{fmt.Errorf("cant fetch package %s couldn't read cant file %s", pkg, err.Error())}
    40  	}
    41  	LogVerbose("Read package canticle %s deps", pkg)
    42  	return cdl.FetchDeps(cdeps...)
    43  }
    44  
    45  type update struct {
    46  	cdep *CanticleDependency
    47  	rev  string
    48  	err  error
    49  }
    50  
    51  // FetchDeps will fetch all of the cdeps passed to it in parallel and
    52  // return an array of encountered errors.
    53  func (cdl *CanticleDepLoader) FetchDeps(cdeps ...*CanticleDependency) []error {
    54  	cdl.updated = make(map[string]string, len(cdeps))
    55  	results := make(chan update, len(cdeps))
    56  	fetch := make(chan *CanticleDependency)
    57  	limit := cdl.Limit
    58  	if limit == 0 {
    59  		limit = len(cdeps)
    60  	}
    61  	var wg sync.WaitGroup
    62  	var errors []error
    63  	for i := 0; i < limit; i++ {
    64  		wg.Add(1)
    65  		go func() {
    66  			for cdep := range fetch {
    67  				rev, err := FetchDep(cdl.Resolver, cdep, cdl.Update)
    68  				results <- update{cdep, rev, err}
    69  			}
    70  			wg.Done()
    71  		}()
    72  	}
    73  	for _, cdep := range cdeps {
    74  		fetch <- cdep
    75  	}
    76  	close(fetch)
    77  	go func() {
    78  		wg.Wait()
    79  		close(results)
    80  	}()
    81  	for result := range results {
    82  		if result.err != nil {
    83  			errors = append(errors, result.err)
    84  		}
    85  		if result.rev != "" {
    86  			cdl.updated[result.cdep.Root] = result.rev
    87  		}
    88  	}
    89  	return errors
    90  }
    91  
    92  // Updated returns a map of repo roots that where updated by the last
    93  // fetch deps/fetchpath call and the resulting info from the update.
    94  func (cdl *CanticleDepLoader) Updated() map[string]string {
    95  	return cdl.updated
    96  }
    97  
    98  // FetchDep fetchs a single canticle dep using the resolver. If update
    99  // is true it will update the vcs branch to cdep.Revision. If not
   100  // updated the rev string will be the empty string.
   101  func FetchDep(resolver RepoResolver, cdep *CanticleDependency, update bool) (string, error) {
   102  	LogInfo("Resolving repo for cdep %+v", cdep)
   103  	vcs, err := resolver.ResolveRepo(cdep.Root, cdep)
   104  	if err != nil {
   105  		return "", fmt.Errorf("cant create vcs for %v because %s", cdep, err.Error())
   106  	}
   107  	LogInfo("Fetching cdep %+v", cdep)
   108  	if err := vcs.Create(cdep.Revision); err != nil {
   109  		return "", fmt.Errorf("cant fetch repo %s because %s", cdep.Root, err.Error())
   110  	}
   111  	if update {
   112  		LogVerbose("Updating cdep %+v", cdep)
   113  		updated, res, err := vcs.UpdateBranch(cdep.Revision)
   114  		if !updated {
   115  			res = ""
   116  		}
   117  		if err != nil {
   118  			return res, fmt.Errorf("cant update repo %s because %s", cdep.Root, err.Error())
   119  		}
   120  		return res, nil
   121  	}
   122  
   123  	return "", nil
   124  }