github.com/masterminds/glide@v0.13.4-0.20190710143844-b94b39d657d8/repo/installer.go (about) 1 package repo 2 3 import ( 4 "fmt" 5 "io/ioutil" 6 "os" 7 "path/filepath" 8 "runtime" 9 "strings" 10 "sync" 11 "syscall" 12 "time" 13 14 "github.com/Masterminds/glide/cache" 15 "github.com/Masterminds/glide/cfg" 16 "github.com/Masterminds/glide/dependency" 17 "github.com/Masterminds/glide/importer" 18 "github.com/Masterminds/glide/msg" 19 gpath "github.com/Masterminds/glide/path" 20 "github.com/Masterminds/glide/util" 21 "github.com/Masterminds/semver" 22 "github.com/Masterminds/vcs" 23 "github.com/codegangsta/cli" 24 ) 25 26 // Installer provides facilities for installing the repos in a config file. 27 type Installer struct { 28 29 // Force the install when certain normally stopping conditions occur. 30 Force bool 31 32 // Home is the location of cache 33 Home string 34 35 // Vendor contains the path to put the vendor packages 36 Vendor string 37 38 // ResolveAllFiles enables a resolver that will examine the dependencies 39 // of every file of every package, rather than only following imported 40 // packages. 41 ResolveAllFiles bool 42 43 // ResolveTest sets if test dependencies should be resolved. 44 ResolveTest bool 45 46 // Updated tracks the packages that have been remotely fetched. 47 Updated *UpdateTracker 48 } 49 50 // NewInstaller returns an Installer instance ready to use. This is the constructor. 51 func NewInstaller() *Installer { 52 i := &Installer{} 53 i.Updated = NewUpdateTracker() 54 return i 55 } 56 57 // VendorPath returns the path to the location to put vendor packages 58 func (i *Installer) VendorPath() string { 59 if i.Vendor != "" { 60 return i.Vendor 61 } 62 63 vp, err := gpath.Vendor() 64 if err != nil { 65 return filepath.FromSlash("./vendor") 66 } 67 68 return vp 69 } 70 71 // Install installs the dependencies from a Lockfile. 72 func (i *Installer) Install(lock *cfg.Lockfile, conf *cfg.Config) (*cfg.Config, error) { 73 74 // Create a config setup based on the Lockfile data to process with 75 // existing commands. 76 newConf := &cfg.Config{} 77 newConf.Name = conf.Name 78 79 newConf.Imports = make(cfg.Dependencies, len(lock.Imports)) 80 for k, v := range lock.Imports { 81 newConf.Imports[k] = cfg.DependencyFromLock(v) 82 } 83 84 newConf.DevImports = make(cfg.Dependencies, len(lock.DevImports)) 85 for k, v := range lock.DevImports { 86 newConf.DevImports[k] = cfg.DependencyFromLock(v) 87 } 88 89 newConf.DeDupe() 90 91 if len(newConf.Imports) == 0 && len(newConf.DevImports) == 0 { 92 msg.Info("No dependencies found. Nothing installed.") 93 return newConf, nil 94 } 95 96 msg.Info("Downloading dependencies. Please wait...") 97 98 err := LazyConcurrentUpdate(newConf.Imports, i, newConf) 99 if err != nil { 100 return newConf, err 101 } 102 err = LazyConcurrentUpdate(newConf.DevImports, i, newConf) 103 104 return newConf, err 105 } 106 107 // Checkout reads the config file and checks out all dependencies mentioned there. 108 // 109 // This is used when initializing an empty vendor directory, or when updating a 110 // vendor directory based on changed config. 111 func (i *Installer) Checkout(conf *cfg.Config) error { 112 113 msg.Info("Downloading dependencies. Please wait...") 114 115 if err := ConcurrentUpdate(conf.Imports, i, conf); err != nil { 116 return err 117 } 118 119 if i.ResolveTest { 120 return ConcurrentUpdate(conf.DevImports, i, conf) 121 } 122 123 return nil 124 } 125 126 // Update updates all dependencies. 127 // 128 // It begins with the dependencies in the config file, but also resolves 129 // transitive dependencies. The returned lockfile has all of the dependencies 130 // listed, but the version reconciliation has not been done. 131 // 132 // In other words, all versions in the Lockfile will be empty. 133 func (i *Installer) Update(conf *cfg.Config) error { 134 base := "." 135 136 ic := newImportCache() 137 138 m := &MissingPackageHandler{ 139 home: i.Home, 140 force: i.Force, 141 Config: conf, 142 Use: ic, 143 updated: i.Updated, 144 } 145 146 v := &VersionHandler{ 147 Use: ic, 148 Imported: make(map[string]bool), 149 Conflicts: make(map[string]bool), 150 Config: conf, 151 } 152 153 // Update imports 154 res, err := dependency.NewResolver(base) 155 res.ResolveTest = i.ResolveTest 156 if err != nil { 157 msg.Die("Failed to create a resolver: %s", err) 158 } 159 res.Config = conf 160 res.Handler = m 161 res.VersionHandler = v 162 res.ResolveAllFiles = i.ResolveAllFiles 163 msg.Info("Resolving imports") 164 165 imps, timps, err := res.ResolveLocal(false) 166 if err != nil { 167 msg.Die("Failed to resolve local packages: %s", err) 168 } 169 var deps cfg.Dependencies 170 var tdeps cfg.Dependencies 171 for _, v := range imps { 172 n := res.Stripv(v) 173 if conf.HasIgnore(n) { 174 continue 175 } 176 rt, sub := util.NormalizeName(n) 177 if sub == "" { 178 sub = "." 179 } 180 d := deps.Get(rt) 181 if d == nil { 182 nd := &cfg.Dependency{ 183 Name: rt, 184 Subpackages: []string{sub}, 185 } 186 deps = append(deps, nd) 187 } else if !d.HasSubpackage(sub) { 188 d.Subpackages = append(d.Subpackages, sub) 189 } 190 } 191 if i.ResolveTest { 192 for _, v := range timps { 193 n := res.Stripv(v) 194 if conf.HasIgnore(n) { 195 continue 196 } 197 rt, sub := util.NormalizeName(n) 198 if sub == "" { 199 sub = "." 200 } 201 d := deps.Get(rt) 202 if d == nil { 203 d = tdeps.Get(rt) 204 } 205 if d == nil { 206 nd := &cfg.Dependency{ 207 Name: rt, 208 Subpackages: []string{sub}, 209 } 210 tdeps = append(tdeps, nd) 211 } else if !d.HasSubpackage(sub) { 212 d.Subpackages = append(d.Subpackages, sub) 213 } 214 } 215 } 216 217 _, err = allPackages(deps, res, false) 218 if err != nil { 219 msg.Die("Failed to retrieve a list of dependencies: %s", err) 220 } 221 222 if i.ResolveTest { 223 msg.Debug("Resolving test dependencies") 224 _, err = allPackages(tdeps, res, true) 225 if err != nil { 226 msg.Die("Failed to retrieve a list of test dependencies: %s", err) 227 } 228 } 229 230 msg.Info("Downloading dependencies. Please wait...") 231 232 err = ConcurrentUpdate(conf.Imports, i, conf) 233 if err != nil { 234 return err 235 } 236 237 if i.ResolveTest { 238 err = ConcurrentUpdate(conf.DevImports, i, conf) 239 if err != nil { 240 return err 241 } 242 } 243 244 return nil 245 } 246 247 // Export from the cache to the vendor directory 248 func (i *Installer) Export(conf *cfg.Config) error { 249 tempDir, err := ioutil.TempDir(gpath.Tmp, "glide-vendor") 250 if err != nil { 251 return err 252 } 253 defer func() { 254 err = os.RemoveAll(tempDir) 255 if err != nil { 256 msg.Err(err.Error()) 257 } 258 }() 259 260 vp := filepath.Join(tempDir, "vendor") 261 err = os.MkdirAll(vp, 0755) 262 263 msg.Info("Exporting resolved dependencies...") 264 done := make(chan struct{}, concurrentWorkers) 265 in := make(chan *cfg.Dependency, concurrentWorkers) 266 var wg sync.WaitGroup 267 var lock sync.Mutex 268 var returnErr error 269 270 for ii := 0; ii < concurrentWorkers; ii++ { 271 go func(ch <-chan *cfg.Dependency) { 272 for { 273 select { 274 case dep := <-ch: 275 loc := dep.Remote() 276 key, err := cache.Key(loc) 277 if err != nil { 278 msg.Die(err.Error()) 279 } 280 cache.Lock(key) 281 282 cdir := filepath.Join(cache.Location(), "src", key) 283 repo, err := dep.GetRepo(cdir) 284 if err != nil { 285 msg.Die(err.Error()) 286 } 287 msg.Info("--> Exporting %s", dep.Name) 288 if err := repo.ExportDir(filepath.Join(vp, filepath.ToSlash(dep.Name))); err != nil { 289 msg.Err("Export failed for %s: %s\n", dep.Name, err) 290 // Capture the error while making sure the concurrent 291 // operations don't step on each other. 292 lock.Lock() 293 if returnErr == nil { 294 returnErr = err 295 } else { 296 returnErr = cli.NewMultiError(returnErr, err) 297 } 298 lock.Unlock() 299 } 300 cache.Unlock(key) 301 wg.Done() 302 case <-done: 303 return 304 } 305 } 306 }(in) 307 } 308 309 for _, dep := range conf.Imports { 310 if !conf.HasIgnore(dep.Name) { 311 err = os.MkdirAll(filepath.Join(vp, filepath.ToSlash(dep.Name)), 0755) 312 if err != nil { 313 lock.Lock() 314 if returnErr == nil { 315 returnErr = err 316 } else { 317 returnErr = cli.NewMultiError(returnErr, err) 318 } 319 lock.Unlock() 320 } 321 wg.Add(1) 322 in <- dep 323 } 324 } 325 326 if i.ResolveTest { 327 for _, dep := range conf.DevImports { 328 if !conf.HasIgnore(dep.Name) { 329 err = os.MkdirAll(filepath.Join(vp, filepath.ToSlash(dep.Name)), 0755) 330 if err != nil { 331 lock.Lock() 332 if returnErr == nil { 333 returnErr = err 334 } else { 335 returnErr = cli.NewMultiError(returnErr, err) 336 } 337 lock.Unlock() 338 } 339 wg.Add(1) 340 in <- dep 341 } 342 } 343 } 344 345 wg.Wait() 346 347 // Close goroutines setting the version 348 for ii := 0; ii < concurrentWorkers; ii++ { 349 done <- struct{}{} 350 } 351 352 if returnErr != nil { 353 return returnErr 354 } 355 356 msg.Info("Replacing existing vendor dependencies") 357 358 // Check if a .git directory exists under the old vendor dir. If it does, 359 // move it over to the newly-generated vendor dir - the user is probably 360 // submoduling, and it's easy enough not to break their setup. 361 ivg := filepath.Join(i.VendorPath(), ".git") 362 gitInfo, err := os.Stat(ivg) 363 if err == nil { 364 msg.Info("Preserving existing vendor/.git") 365 vpg := filepath.Join(vp, ".git") 366 err = os.Rename(ivg, vpg) 367 368 if terr, ok := err.(*os.LinkError); ok { 369 if gitInfo.IsDir() { 370 err = fixcle(ivg, vpg, terr) 371 } else { 372 // When this is a submodule, .git is just a file. Don't try to copy 373 // it as a directory in this case (see #828). 374 err = gpath.CopyFile(ivg, vpg) 375 } 376 377 if err != nil { 378 msg.Warn("Failed to preserve existing vendor/.git") 379 } 380 } 381 } 382 383 err = gpath.CustomRemoveAll(i.VendorPath()) 384 if err != nil { 385 return err 386 } 387 388 err = gpath.CustomRename(vp, i.VendorPath()) 389 if terr, ok := err.(*os.LinkError); ok { 390 return fixcle(vp, i.VendorPath(), terr) 391 } 392 393 return err 394 395 } 396 397 // fixcle is a helper function that tries to recover from cross-device rename 398 // errors by falling back to copying. 399 func fixcle(from, to string, terr *os.LinkError) error { 400 // When there are different physical devices we cannot rename cross device. 401 // Instead we copy. 402 403 // syscall.EXDEV is the common name for the cross device link error 404 // which has varying output text across different operating systems. 405 if terr.Err == syscall.EXDEV { 406 msg.Debug("Cross link err, trying manual copy: %s", terr) 407 return gpath.CopyDir(from, to) 408 } else if runtime.GOOS == "windows" { 409 // In windows it can drop down to an operating system call that 410 // returns an operating system error with a different number and 411 // message. Checking for that as a fall back. 412 noerr, ok := terr.Err.(syscall.Errno) 413 // 0x11 (ERROR_NOT_SAME_DEVICE) is the windows error. 414 // See https://msdn.microsoft.com/en-us/library/cc231199.aspx 415 if ok && noerr == 0x11 { 416 msg.Debug("Cross link err on Windows, trying manual copy: %s", terr) 417 return gpath.CopyDir(from, to) 418 } 419 } 420 421 return terr 422 } 423 424 // List resolves the complete dependency tree and returns a list of dependencies. 425 func (i *Installer) List(conf *cfg.Config) []*cfg.Dependency { 426 base := "." 427 428 ic := newImportCache() 429 430 v := &VersionHandler{ 431 Use: ic, 432 Imported: make(map[string]bool), 433 Conflicts: make(map[string]bool), 434 Config: conf, 435 } 436 437 // Update imports 438 res, err := dependency.NewResolver(base) 439 if err != nil { 440 msg.Die("Failed to create a resolver: %s", err) 441 } 442 res.Config = conf 443 res.VersionHandler = v 444 res.ResolveAllFiles = i.ResolveAllFiles 445 446 msg.Info("Resolving imports") 447 _, _, err = res.ResolveLocal(false) 448 if err != nil { 449 msg.Die("Failed to resolve local packages: %s", err) 450 } 451 452 _, err = allPackages(conf.Imports, res, false) 453 if err != nil { 454 msg.Die("Failed to retrieve a list of dependencies: %s", err) 455 } 456 457 if len(conf.DevImports) > 0 { 458 msg.Warn("dev imports not resolved.") 459 } 460 461 return conf.Imports 462 } 463 464 // LazyConcurrentUpdate updates only deps that are not already checkout out at the right version. 465 // 466 // This is only safe when updating from a lock file. 467 func LazyConcurrentUpdate(deps []*cfg.Dependency, i *Installer, c *cfg.Config) error { 468 469 newDeps := []*cfg.Dependency{} 470 for _, dep := range deps { 471 472 key, err := cache.Key(dep.Remote()) 473 if err != nil { 474 newDeps = append(newDeps, dep) 475 continue 476 } 477 destPath := filepath.Join(cache.Location(), "src", key) 478 479 // Get a VCS object for this directory 480 repo, err := dep.GetRepo(destPath) 481 if err != nil { 482 newDeps = append(newDeps, dep) 483 continue 484 } 485 486 ver, err := repo.Version() 487 if err != nil { 488 newDeps = append(newDeps, dep) 489 continue 490 } 491 if dep.Reference != "" { 492 ci, err := repo.CommitInfo(dep.Reference) 493 if err == nil && ci.Commit == dep.Reference { 494 msg.Info("--> Found desired version locally %s %s!", dep.Name, dep.Reference) 495 continue 496 } 497 } 498 499 msg.Debug("--> Queue %s for update (%s != %s).", dep.Name, ver, dep.Reference) 500 newDeps = append(newDeps, dep) 501 } 502 if len(newDeps) > 0 { 503 return ConcurrentUpdate(newDeps, i, c) 504 } 505 506 return nil 507 } 508 509 // ConcurrentUpdate takes a list of dependencies and updates in parallel. 510 func ConcurrentUpdate(deps []*cfg.Dependency, i *Installer, c *cfg.Config) error { 511 done := make(chan struct{}, concurrentWorkers) 512 in := make(chan *cfg.Dependency, concurrentWorkers) 513 var wg sync.WaitGroup 514 var lock sync.Mutex 515 var returnErr error 516 517 for ii := 0; ii < concurrentWorkers; ii++ { 518 go func(ch <-chan *cfg.Dependency) { 519 for { 520 select { 521 case dep := <-ch: 522 loc := dep.Remote() 523 key, err := cache.Key(loc) 524 if err != nil { 525 msg.Die(err.Error()) 526 } 527 cache.Lock(key) 528 if err := VcsUpdate(dep, i.Force, i.Updated); err != nil { 529 msg.Err("Update failed for %s: %s\n", dep.Name, err) 530 // Capture the error while making sure the concurrent 531 // operations don't step on each other. 532 lock.Lock() 533 if returnErr == nil { 534 returnErr = err 535 } else { 536 returnErr = cli.NewMultiError(returnErr, err) 537 } 538 lock.Unlock() 539 } 540 cache.Unlock(key) 541 wg.Done() 542 case <-done: 543 return 544 } 545 } 546 }(in) 547 } 548 549 for _, dep := range deps { 550 if !c.HasIgnore(dep.Name) { 551 wg.Add(1) 552 in <- dep 553 } 554 } 555 556 wg.Wait() 557 558 // Close goroutines setting the version 559 for ii := 0; ii < concurrentWorkers; ii++ { 560 done <- struct{}{} 561 } 562 563 return returnErr 564 } 565 566 // allPackages gets a list of all packages required to satisfy the given deps. 567 func allPackages(deps []*cfg.Dependency, res *dependency.Resolver, addTest bool) ([]string, error) { 568 if len(deps) == 0 { 569 return []string{}, nil 570 } 571 572 vdir, err := gpath.Vendor() 573 if err != nil { 574 return []string{}, err 575 } 576 vdir += string(os.PathSeparator) 577 ll, err := res.ResolveAll(deps, addTest) 578 if err != nil { 579 return []string{}, err 580 } 581 582 for i := 0; i < len(ll); i++ { 583 ll[i] = strings.TrimPrefix(ll[i], vdir) 584 } 585 return ll, nil 586 } 587 588 // MissingPackageHandler is a dependency.MissingPackageHandler. 589 // 590 // When a package is not found, this attempts to resolve and fetch. 591 // 592 // When a package is found on the GOPATH, this notifies the user. 593 type MissingPackageHandler struct { 594 home string 595 force bool 596 Config *cfg.Config 597 Use *importCache 598 updated *UpdateTracker 599 } 600 601 // NotFound attempts to retrieve a package when not found in the local cache 602 // folder. It will attempt to get it from the remote location info. 603 func (m *MissingPackageHandler) NotFound(pkg string, addTest bool) (bool, error) { 604 err := m.fetchToCache(pkg, addTest) 605 if err != nil { 606 return false, err 607 } 608 609 return true, err 610 } 611 612 // OnGopath will either copy a package, already found in the GOPATH, to the 613 // vendor/ directory or download it from the internet. This is dependent if 614 // useGopath on the installer is set to true to copy from the GOPATH. 615 func (m *MissingPackageHandler) OnGopath(pkg string, addTest bool) (bool, error) { 616 617 err := m.fetchToCache(pkg, addTest) 618 if err != nil { 619 return false, err 620 } 621 622 return true, err 623 } 624 625 // InVendor updates a package in the vendor/ directory to make sure the latest 626 // is available. 627 func (m *MissingPackageHandler) InVendor(pkg string, addTest bool) error { 628 return m.fetchToCache(pkg, addTest) 629 } 630 631 // PkgPath resolves the location on the filesystem where the package should be. 632 // This handles making sure to use the cache location. 633 func (m *MissingPackageHandler) PkgPath(pkg string) string { 634 root, sub := util.NormalizeName(pkg) 635 636 // For the parent applications source skip the cache. 637 if root == m.Config.Name { 638 pth := gpath.Basepath() 639 return filepath.Join(pth, filepath.FromSlash(sub)) 640 } 641 642 d := m.Config.Imports.Get(root) 643 if d == nil { 644 d = m.Config.DevImports.Get(root) 645 } 646 647 if d == nil { 648 d, _ = m.Use.Get(root) 649 650 if d == nil { 651 d = &cfg.Dependency{Name: root} 652 } 653 } 654 655 key, err := cache.Key(d.Remote()) 656 if err != nil { 657 msg.Die("Error generating cache key for %s", d.Name) 658 } 659 660 return filepath.Join(cache.Location(), "src", key, filepath.FromSlash(sub)) 661 } 662 663 func (m *MissingPackageHandler) fetchToCache(pkg string, addTest bool) error { 664 root := util.GetRootFromPackage(pkg) 665 // Skip any references to the root package. 666 if root == m.Config.Name { 667 return nil 668 } 669 670 d := m.Config.Imports.Get(root) 671 if d == nil && addTest { 672 d = m.Config.DevImports.Get(root) 673 } 674 675 // If the dependency is nil it means the Config doesn't yet know about it. 676 if d == nil { 677 d, _ = m.Use.Get(root) 678 // We don't know about this dependency so we create a basic instance. 679 if d == nil { 680 d = &cfg.Dependency{Name: root} 681 } 682 683 if addTest { 684 m.Config.DevImports = append(m.Config.DevImports, d) 685 } else { 686 m.Config.Imports = append(m.Config.Imports, d) 687 } 688 } 689 690 return VcsUpdate(d, m.force, m.updated) 691 } 692 693 // VersionHandler handles setting the proper version in the VCS. 694 type VersionHandler struct { 695 696 // If Try to use the version here if we have one. This is a cache and will 697 // change over the course of setting versions. 698 Use *importCache 699 700 // Cache if importing scan has already occurred here. 701 Imported map[string]bool 702 703 Config *cfg.Config 704 705 // There's a problem where many sub-packages have been asked to set a version 706 // and you can end up with numerous conflict messages that are exactly the 707 // same. We are keeping track to only display them once. 708 // the parent pac 709 Conflicts map[string]bool 710 } 711 712 // Process imports dependencies for a package 713 func (d *VersionHandler) Process(pkg string) (e error) { 714 root := util.GetRootFromPackage(pkg) 715 716 // Skip any references to the root package. 717 if root == d.Config.Name { 718 return nil 719 } 720 721 // We have not tried to import, yet. 722 // Should we look in places other than the root of the project? 723 if d.Imported[root] == false { 724 d.Imported[root] = true 725 p := d.pkgPath(root) 726 f, deps, err := importer.Import(p) 727 if f && err == nil { 728 for _, dep := range deps { 729 730 // The fist one wins. Would something smater than this be better? 731 exists, _ := d.Use.Get(dep.Name) 732 if exists == nil && (dep.Reference != "" || dep.Repository != "") { 733 d.Use.Add(dep.Name, dep, root) 734 } 735 } 736 } else if err != nil { 737 msg.Err("Unable to import from %s. Err: %s", root, err) 738 e = err 739 } 740 } 741 742 return 743 } 744 745 // SetVersion sets the version for a package. If that package version is already 746 // set it handles the case by: 747 // - keeping the already set version 748 // - proviting messaging about the version conflict 749 // TODO(mattfarina): The way version setting happens can be improved. Currently not optimal. 750 func (d *VersionHandler) SetVersion(pkg string, addTest bool) (e error) { 751 root := util.GetRootFromPackage(pkg) 752 753 // Skip any references to the root package. 754 if root == d.Config.Name { 755 return nil 756 } 757 758 v := d.Config.Imports.Get(root) 759 if addTest { 760 if v == nil { 761 v = d.Config.DevImports.Get(root) 762 } else if d.Config.DevImports.Has(root) { 763 // Both imports and test imports lists the same dependency. 764 // There are import chains (because the import tree is resolved 765 // before the test tree) that can cause this. 766 tempD := d.Config.DevImports.Get(root) 767 if tempD.Reference != v.Reference { 768 msg.Warn("Using import %s (version %s) for test instead of testImport (version %s).", v.Name, v.Reference, tempD.Reference) 769 } 770 // TODO(mattfarina): Note repo difference in a warning. 771 } 772 } 773 774 dep, req := d.Use.Get(root) 775 if dep != nil && v != nil { 776 if v.Reference == "" && dep.Reference != "" { 777 v.Reference = dep.Reference 778 // Clear the pin, if set, so the new version can be used. 779 v.Pin = "" 780 dep = v 781 } else if v.Reference != "" && dep.Reference != "" && v.Reference != dep.Reference { 782 dest := d.pkgPath(pkg) 783 dep = determineDependency(v, dep, dest, req) 784 } else { 785 dep = v 786 } 787 788 } else if v != nil { 789 dep = v 790 } else if dep != nil { 791 // We've got an imported dependency to use and don't already have a 792 // record of it. Append it to the Imports. 793 if addTest { 794 d.Config.DevImports = append(d.Config.DevImports, dep) 795 } else { 796 d.Config.Imports = append(d.Config.Imports, dep) 797 } 798 } else { 799 // If we've gotten here we don't have any depenency objects. 800 r, sp := util.NormalizeName(pkg) 801 dep = &cfg.Dependency{ 802 Name: r, 803 } 804 if sp != "" { 805 dep.Subpackages = []string{sp} 806 } 807 if addTest { 808 d.Config.DevImports = append(d.Config.DevImports, dep) 809 } else { 810 d.Config.Imports = append(d.Config.Imports, dep) 811 } 812 } 813 814 err := VcsVersion(dep) 815 if err != nil { 816 msg.Warn("Unable to set version on %s to %s. Err: %s", root, dep.Reference, err) 817 e = err 818 } 819 820 return 821 } 822 823 func (d *VersionHandler) pkgPath(pkg string) string { 824 root, sub := util.NormalizeName(pkg) 825 826 // For the parent applications source skip the cache. 827 if root == d.Config.Name { 828 pth := gpath.Basepath() 829 return filepath.Join(pth, filepath.FromSlash(sub)) 830 } 831 832 dep := d.Config.Imports.Get(root) 833 if dep == nil { 834 dep = d.Config.DevImports.Get(root) 835 } 836 837 if dep == nil { 838 dep, _ = d.Use.Get(root) 839 840 if dep == nil { 841 dep = &cfg.Dependency{Name: root} 842 } 843 } 844 845 key, err := cache.Key(dep.Remote()) 846 if err != nil { 847 msg.Die("Error generating cache key for %s", dep.Name) 848 } 849 850 return filepath.Join(cache.Location(), "src", key, filepath.FromSlash(sub)) 851 } 852 853 func determineDependency(v, dep *cfg.Dependency, dest, req string) *cfg.Dependency { 854 repo, err := v.GetRepo(dest) 855 if err != nil { 856 singleWarn("Unable to access repo for %s\n", v.Name) 857 singleInfo("Keeping %s %s", v.Name, v.Reference) 858 return v 859 } 860 861 vIsRef := repo.IsReference(v.Reference) 862 depIsRef := repo.IsReference(dep.Reference) 863 864 // Both are references and they are different ones. 865 if vIsRef && depIsRef { 866 singleWarn("Conflict: %s rev is currently %s, but %s wants %s\n", v.Name, v.Reference, req, dep.Reference) 867 868 displayCommitInfo(repo, v) 869 displayCommitInfo(repo, dep) 870 871 singleInfo("Keeping %s %s", v.Name, v.Reference) 872 return v 873 } else if vIsRef { 874 // The current one is a reference and the suggestion is a SemVer constraint. 875 con, err := semver.NewConstraint(dep.Reference) 876 if err != nil { 877 singleWarn("Version issue for %s: '%s' is neither a reference or semantic version constraint\n", dep.Name, dep.Reference) 878 singleInfo("Keeping %s %s", v.Name, v.Reference) 879 return v 880 } 881 882 ver, err := semver.NewVersion(v.Reference) 883 if err != nil { 884 // The existing version is not a semantic version. 885 singleWarn("Conflict: %s version is %s, but also asked for %s\n", v.Name, v.Reference, dep.Reference) 886 displayCommitInfo(repo, v) 887 singleInfo("Keeping %s %s", v.Name, v.Reference) 888 return v 889 } 890 891 if con.Check(ver) { 892 singleInfo("Keeping %s %s because it fits constraint '%s'", v.Name, v.Reference, dep.Reference) 893 return v 894 } 895 singleWarn("Conflict: %s version is %s but does not meet constraint '%s'\n", v.Name, v.Reference, dep.Reference) 896 singleInfo("Keeping %s %s", v.Name, v.Reference) 897 return v 898 } else if depIsRef { 899 900 con, err := semver.NewConstraint(v.Reference) 901 if err != nil { 902 singleWarn("Version issue for %s: '%s' is neither a reference or semantic version constraint\n", v.Name, v.Reference) 903 singleInfo("Keeping %s %s", v.Name, v.Reference) 904 return v 905 } 906 907 ver, err := semver.NewVersion(dep.Reference) 908 if err != nil { 909 singleWarn("Conflict: %s version is %s, but also asked for %s\n", v.Name, v.Reference, dep.Reference) 910 displayCommitInfo(repo, dep) 911 singleInfo("Keeping %s %s", v.Name, v.Reference) 912 return v 913 } 914 915 if con.Check(ver) { 916 v.Reference = dep.Reference 917 singleInfo("Using %s %s because it fits constraint '%s'", v.Name, v.Reference, v.Reference) 918 return v 919 } 920 singleWarn("Conflict: %s semantic version constraint is %s but '%s' does not meet the constraint\n", v.Name, v.Reference, v.Reference) 921 singleInfo("Keeping %s %s", v.Name, v.Reference) 922 return v 923 } 924 // Neither is a vcs reference and both could be semantic version 925 // constraints that are different. 926 927 _, err = semver.NewConstraint(dep.Reference) 928 if err != nil { 929 // dd.Reference is not a reference or a valid constraint. 930 singleWarn("Version %s %s is not a reference or valid semantic version constraint\n", dep.Name, dep.Reference) 931 singleInfo("Keeping %s %s", v.Name, v.Reference) 932 return v 933 } 934 935 _, err = semver.NewConstraint(v.Reference) 936 if err != nil { 937 // existing.Reference is not a reference or a valid constraint. 938 // We really should never end up here. 939 singleWarn("Version %s %s is not a reference or valid semantic version constraint\n", v.Name, v.Reference) 940 941 v.Reference = dep.Reference 942 v.Pin = "" 943 singleInfo("Using %s %s because it is a valid version", v.Name, v.Reference) 944 return v 945 } 946 947 // Both versions are constraints. Try to merge them. 948 // If either comparison has an || skip merging. That's complicated. 949 ddor := strings.Index(dep.Reference, "||") 950 eor := strings.Index(v.Reference, "||") 951 if ddor == -1 && eor == -1 { 952 // Add the comparisons together. 953 newRef := v.Reference + ", " + dep.Reference 954 v.Reference = newRef 955 v.Pin = "" 956 singleInfo("Combining %s semantic version constraints %s and %s", v.Name, v.Reference, dep.Reference) 957 return v 958 } 959 singleWarn("Conflict: %s version is %s, but also asked for %s\n", v.Name, v.Reference, dep.Reference) 960 singleInfo("Keeping %s %s", v.Name, v.Reference) 961 return v 962 } 963 964 var warningMessage = make(map[string]bool) 965 var infoMessage = make(map[string]bool) 966 967 func singleWarn(ft string, v ...interface{}) { 968 m := fmt.Sprintf(ft, v...) 969 _, f := warningMessage[m] 970 if !f { 971 msg.Warn(m) 972 warningMessage[m] = true 973 } 974 } 975 976 func singleInfo(ft string, v ...interface{}) { 977 m := fmt.Sprintf(ft, v...) 978 _, f := infoMessage[m] 979 if !f { 980 msg.Info(m) 981 infoMessage[m] = true 982 } 983 } 984 985 type importCache struct { 986 cache map[string]*cfg.Dependency 987 from map[string]string 988 } 989 990 func newImportCache() *importCache { 991 return &importCache{ 992 cache: make(map[string]*cfg.Dependency), 993 from: make(map[string]string), 994 } 995 } 996 997 func (i *importCache) Get(name string) (*cfg.Dependency, string) { 998 d, f := i.cache[name] 999 if f { 1000 return d, i.from[name] 1001 } 1002 1003 return nil, "" 1004 } 1005 1006 func (i *importCache) Add(name string, dep *cfg.Dependency, root string) { 1007 i.cache[name] = dep 1008 i.from[name] = root 1009 } 1010 1011 var displayCommitInfoPrefix = msg.Default.Color(msg.Green, "[INFO] ") 1012 var displayCommitInfoTemplate = "%s reference %s:\n" + 1013 displayCommitInfoPrefix + "- author: %s\n" + 1014 displayCommitInfoPrefix + "- commit date: %s\n" + 1015 displayCommitInfoPrefix + "- subject (first line): %s\n" 1016 1017 func displayCommitInfo(repo vcs.Repo, dep *cfg.Dependency) { 1018 c, err := repo.CommitInfo(dep.Reference) 1019 ref := dep.Reference 1020 1021 if err == nil { 1022 tgs, err2 := repo.TagsFromCommit(c.Commit) 1023 if err2 == nil && len(tgs) > 0 { 1024 if tgs[0] != dep.Reference { 1025 ref = ref + " (" + tgs[0] + ")" 1026 } 1027 } 1028 singleInfo(displayCommitInfoTemplate, dep.Name, ref, c.Author, c.Date.Format(time.RFC1123Z), commitSubjectFirstLine(c.Message)) 1029 } 1030 } 1031 1032 func commitSubjectFirstLine(sub string) string { 1033 lines := strings.Split(sub, "\n") 1034 if len(lines) <= 1 { 1035 return sub 1036 } 1037 1038 return lines[0] 1039 }