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 }