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