github.com/bilus/oya@v0.0.3-0.20190301162104-da4acbd394c6/pkg/project/packs.go (about) 1 package project 2 3 import ( 4 "sort" 5 6 "github.com/bilus/oya/pkg/deptree" 7 "github.com/bilus/oya/pkg/oyafile" 8 "github.com/bilus/oya/pkg/pack" 9 "github.com/bilus/oya/pkg/repo" 10 "github.com/bilus/oya/pkg/types" 11 ) 12 13 func (p *Project) Require(pack pack.Pack) error { 14 raw, err := p.rawOyafileIn(p.RootDir) 15 if err != nil { 16 return err 17 } 18 err = raw.AddRequire(pack) 19 if err != nil { 20 return err 21 } 22 23 p.dependencies = nil // Force reload. 24 return nil 25 } 26 27 func (p *Project) Install(pack pack.Pack) error { 28 return pack.Install(p.installDir) 29 } 30 31 func (p *Project) IsInstalled(pack pack.Pack) (bool, error) { 32 return pack.IsInstalled(p.installDir) 33 } 34 35 // InstallPacks installs packs used by the project. 36 // It works in two steps: 37 // 1. It goes through all Import: directives and updates the Require: section with missing packs in their latest versions. 38 // 2. It installs all packs that haven't been installed. 39 func (p *Project) InstallPacks() error { 40 err := p.updateDependencies() 41 if err != nil { 42 return err 43 } 44 45 deps, err := p.Deps() 46 if err != nil { 47 return err 48 } 49 return deps.ForEach( 50 func(pack pack.Pack) error { 51 _, ok := pack.ReplacementPath() 52 if ok { 53 return nil 54 } 55 installed, err := p.IsInstalled(pack) 56 if err != nil { 57 return err 58 } 59 if installed { 60 return nil 61 } 62 err = p.Install(pack) 63 return err 64 }, 65 ) 66 } 67 68 func (p *Project) FindRequiredPack(importPath types.ImportPath) (pack.Pack, bool, error) { 69 deps, err := p.Deps() 70 if err != nil { 71 return pack.Pack{}, false, err 72 } 73 return deps.Find(importPath) 74 } 75 76 func (p *Project) Deps() (Deps, error) { 77 if p.dependencies != nil { 78 return p.dependencies, nil 79 } 80 81 o, err := p.oyafileIn(p.RootDir) 82 if err != nil { 83 return nil, err 84 } 85 installDirs := []string{ 86 p.installDir, 87 } 88 requires, err := resolvePackReferences(o.Requires) 89 if err != nil { 90 return nil, err 91 } 92 ldr, err := deptree.New(p.RootDir, installDirs, requires) 93 if err != nil { 94 return nil, err 95 } 96 err = ldr.Explode() 97 if err != nil { 98 return nil, err 99 } 100 p.dependencies = ldr 101 return ldr, nil 102 } 103 104 func (p *Project) updateDependencies() error { 105 files, err := p.List(p.RootDir) 106 if err != nil { 107 return err 108 } 109 110 deps, err := p.Deps() 111 if err != nil { 112 return err 113 } 114 115 // Collect all import paths from all Oyafiles. Make them unique. 116 // Also, sort them to make writing reliable tests easier. 117 importPaths := uniqueSortedImportPaths(files) 118 for _, importPath := range importPaths { 119 _, found, err := deps.Find(importPath) 120 if err != nil { 121 return err 122 } 123 if found { 124 continue 125 } 126 127 l, err := repo.Open(importPath) 128 if err != nil { 129 // Import paths can also be relative to the root directory. 130 // BUG(bilus): I don't particularly like it how tihs logic is split. Plus we may be masking some other errors this way 131 if _, ok := err.(repo.ErrNotGithub); ok { 132 continue 133 } 134 return err 135 } 136 137 pack, err := l.LatestVersion() 138 if err != nil { 139 return err 140 } 141 err = p.Require(pack) 142 if err != nil { 143 return err 144 } 145 } 146 return nil 147 } 148 149 func uniqueSortedImportPaths(oyafiles []*oyafile.Oyafile) []types.ImportPath { 150 importPathSet := make(map[types.ImportPath]struct{}) 151 importPaths := make([]types.ImportPath, 0) 152 for _, o := range oyafiles { 153 for _, importPath := range o.Imports { 154 if _, exists := importPathSet[importPath]; !exists { 155 importPaths = append(importPaths, importPath) 156 } 157 importPathSet[importPath] = struct{}{} 158 } 159 } 160 161 sort.Slice(importPaths, func(i, j int) bool { 162 return importPaths[i] < importPaths[j] 163 }) 164 165 return importPaths 166 } 167 168 func resolvePackReferences(references []oyafile.PackReference) ([]pack.Pack, error) { 169 packs := make([]pack.Pack, len(references)) 170 for i, reference := range references { 171 l, err := repo.Open(reference.ImportPath) 172 if err != nil { 173 return nil, err 174 } 175 pack, err := l.Version(reference.Version) 176 if err != nil { 177 return nil, err 178 } 179 if len(reference.ReplacementPath) > 0 { 180 pack = pack.LocalReplacement(reference.ReplacementPath) 181 } 182 packs[i] = pack 183 } 184 return packs, nil 185 }