// VcsExists checks if the directory has a local VCS checkout. func VcsExists(dep *yaml.Dependency, dest string) bool { repo, err := dep.GetRepo(dest) if err != nil { return false } return repo.CheckLocal() }
// VcsLastCommit gets the last commit ID from the given dependency. func VcsLastCommit(dep *yaml.Dependency, vend string) (string, error) { cwd := path.Join(vend, dep.Name) repo, err := dep.GetRepo(cwd) if err != nil { return "", err } if repo.CheckLocal() == false { return "", fmt.Errorf("%s is not a VCS repo\n", dep.Name) } version, err := repo.Version() if err != nil { return "", err } return version, nil }
// VcsVersion set the VCS version for a checkout. func VcsVersion(dep *yaml.Dependency, vend string) error { // If there is no refernece configured there is nothing to set. if dep.Reference == "" { return nil } cwd := path.Join(vend, dep.Name) // When the directory is not empty and has no VCS directory it's // a vendored files situation. empty, err := isDirectoryEmpty(cwd) if err != nil { return err } _, err = v.DetectVcsFromFS(cwd) if empty == false && err == v.ErrCannotDetectVCS { Warn("%s appears to be a vendored package. Unable to set new version. Consider the '--update-vendored' flag.\n", dep.Name) } else { repo, err := dep.GetRepo(cwd) if err != nil { return err } ver := dep.Reference // Referenes in Git can begin with a ^ which is similar to semver. // If there is a ^ prefix we assume it's a semver constraint rather than // part of the git/VCS commit id. if repo.IsReference(ver) && !strings.HasPrefix(ver, "^") { Info("Setting version for %s to %s.\n", dep.Name, ver) } else { // Create the constraing first to make sure it's valid before // working on the repo. constraint, err := semver.NewConstraint(ver) // Make sure the constriant is valid. At this point it's not a valid // reference so if it's not a valid constrint we can exit early. if err != nil { Warn("The reference '%s' is not valid\n", ver) return err } // Get the tags and branches (in that order) refs, err := getAllVcsRefs(repo) if err != nil { return err } // Convert and filter the list to semver.Version instances semvers := getSemVers(refs) // Sort semver list sort.Sort(sort.Reverse(semver.Collection(semvers))) found := false for _, v := range semvers { if constraint.Check(v) { found = true // If the constrint passes get the original reference ver = v.Original() break } } if found { Info("Detected semantic version. Setting version for %s to %s.\n", dep.Name, ver) } else { Warn("Unable to find semantic version for constraint %s %s\n", dep.Name, ver) } } if err := repo.UpdateVersion(ver); err != nil { Error("Failed to set version to %s: %s\n", dep.Reference, err) return err } } return nil }
// VcsUpdate updates to a particular checkout based on the VCS setting. func VcsUpdate(dep *yaml.Dependency, vend, home string, force, cache, cacheGopath, skipGopath bool) error { Info("Fetching updates for %s.\n", dep.Name) if filterArchOs(dep) { Info("%s is not used for %s/%s.\n", dep.Name, runtime.GOOS, runtime.GOARCH) return nil } dest := path.Join(vend, dep.Name) // If destination doesn't exist we need to perform an initial checkout. if _, err := os.Stat(dest); os.IsNotExist(err) { if err = VcsGet(dep, dest, home, cache, cacheGopath, skipGopath); err != nil { Warn("Unable to checkout %s\n", dep.Name) return err } } else { // At this point we have a directory for the package. // When the directory is not empty and has no VCS directory it's // a vendored files situation. empty, err := isDirectoryEmpty(dest) if err != nil { return err } _, err = v.DetectVcsFromFS(dest) if empty == false && err == v.ErrCannotDetectVCS { Warn("%s appears to be a vendored package. Unable to update. Consider the '--update-vendored' flag.\n", dep.Name) } else { repo, err := dep.GetRepo(dest) // Tried to checkout a repo to a path that does not work. Either the // type or endpoint has changed. Force is being passed in so the old // location can be removed and replaced with the new one. // Warning, any changes in the old location will be deleted. // TODO: Put dirty checking in on the existing local checkout. if (err == v.ErrWrongVCS || err == v.ErrWrongRemote) && force == true { var newRemote string if len(dep.Repository) > 0 { newRemote = dep.Repository } else { newRemote = "https://" + dep.Name } Warn("Replacing %s with contents from %s\n", dep.Name, newRemote) rerr := os.RemoveAll(dest) if rerr != nil { return rerr } if err = VcsGet(dep, dest, home, cache, cacheGopath, skipGopath); err != nil { Warn("Unable to checkout %s\n", dep.Name) return err } } else if err != nil { return err } else { // Check if the current version is a tag or commit id. If it is // and that version is already checked out we can skip updating // which is faster than going out to the Internet to perform // an update. if dep.Reference != "" { version, err := repo.Version() if err != nil { return err } ib, err := isBranch(dep.Reference, repo) if err != nil { return err } // If the current version equals the ref and it's not a // branch it's a tag or commit id so we can skip // performing an update. if version == dep.Reference && !ib { Info("%s is already set to version %s. Skipping update.", dep.Name, dep.Reference) return nil } } if err := repo.Update(); err != nil { Warn("Download failed.\n") return err } } } } return nil }
// VcsGet figures out how to fetch a dependency, and then gets it. // // VcsGet installs into the dest. func VcsGet(dep *yaml.Dependency, dest, home string, cache, cacheGopath, skipGopath bool) error { // When not skipping the $GOPATH look in it for a copy of the package if !skipGopath { // Check if the $GOPATH has a viable version to use and if so copy to vendor gps := Gopaths() for _, p := range gps { d := filepath.Join(p, "src", dep.Name) if _, err := os.Stat(d); err == nil { empty, err := isDirectoryEmpty(d) if empty || err != nil { continue } repo, err := dep.GetRepo(d) if err != nil { continue } // Dirty repos have uncomitted changes. if repo.IsDirty() { continue } // Having found a repo we copy it to vendor and update it. Debug("Found %s in GOPATH at %s. Copying to %s", dep.Name, d, dest) err = copyDir(d, dest) if err != nil { return err } // Update the repo in the vendor directory Debug("Updating %s, now in the vendor path at %s", dep.Name, dest) repo, err = dep.GetRepo(dest) if err != nil { return err } err = repo.Update() if err != nil { return err } // If there is no reference set on the dep we try to checkout // the default branch. if dep.Reference == "" { db := defaultBranch(repo, home) if db != "" { err = repo.UpdateVersion(db) if err != nil { Debug("Attempting to set the version on %s to %s failed. Error %s", dep.Name, db, err) } } } return nil } } } // When opting in to cache in the GOPATH attempt to do put a copy there. if cacheGopath { // Since we didn't find an existing copy in the GOPATHs try to clone there. gp := Gopath() if gp != "" { d := filepath.Join(gp, "src", dep.Name) if _, err := os.Stat(d); os.IsNotExist(err) { // Empty directory so we checkout out the code here. Debug("Retrieving %s to %s before copying to vendor", dep.Name, d) repo, err := dep.GetRepo(d) if err != nil { return err } repo.Get() branch := findCurrentBranch(repo) if branch != "" { // we know the default branch so we can store it in the cache var loc string if dep.Repository != "" { loc = dep.Repository } else { loc = "https://" + dep.Name } key, err := cacheCreateKey(loc) if err == nil { Debug("Saving default branch for %s", repo.Remote()) c := cacheRepoInfo{DefaultBranch: branch} saveCacheRepoData(key, c, home) } } Debug("Copying %s from GOPATH at %s to %s", dep.Name, d, dest) err = copyDir(d, dest) if err != nil { return err } return nil } } } // If opting in to caching attempt to put it in the cache folder if cache { // Check if the cache has a viable version and try to use that. var loc string if dep.Repository != "" { loc = dep.Repository } else { loc = "https://" + dep.Name } key, err := cacheCreateKey(loc) if err == nil { d := filepath.Join(home, "cache", "src", key) repo, err := dep.GetRepo(d) if err != nil { return err } // If the directory does not exist this is a first cache. if _, err = os.Stat(d); os.IsNotExist(err) { Debug("Adding %s to the cache for the first time", dep.Name) err = repo.Get() if err != nil { return err } branch := findCurrentBranch(repo) if branch != "" { // we know the default branch so we can store it in the cache var loc string if dep.Repository != "" { loc = dep.Repository } else { loc = "https://" + dep.Name } key, err := cacheCreateKey(loc) if err == nil { Debug("Saving default branch for %s", repo.Remote()) c := cacheRepoInfo{DefaultBranch: branch} err = saveCacheRepoData(key, c, home) if err != nil { Debug("Error saving %s to cache. Error: %s", repo.Remote(), err) } } } } else { Debug("Updating %s in the cache", dep.Name) err = repo.Update() if err != nil { return err } } Debug("Copying %s from the cache to %s", dep.Name, dest) err = copyDir(d, dest) if err != nil { return err } return nil } else { Warn("Cache key generation error: %s", err) } } // If unable to cache pull directly into the vendor/ directory. repo, err := dep.GetRepo(dest) if err != nil { return err } gerr := repo.Get() // Attempt to cache the default branch branch := findCurrentBranch(repo) if branch != "" { // we know the default branch so we can store it in the cache var loc string if dep.Repository != "" { loc = dep.Repository } else { loc = "https://" + dep.Name } key, err := cacheCreateKey(loc) if err == nil { Debug("Saving default branch for %s", repo.Remote()) c := cacheRepoInfo{DefaultBranch: branch} err = saveCacheRepoData(key, c, home) if err != nil { Debug("Error saving %s to cache. Error: %s", repo.Remote(), err) } } } return gerr }