gopkg.in/tools/godep.v70@v70.0.0-20160520184947-56b9657fec2f/restore.go (about)

     1  package main
     2  
     3  import (
     4  	"errors"
     5  	"go/build"
     6  	"log"
     7  	"os"
     8  	"path/filepath"
     9  
    10  	"github.com/tools/godep/Godeps/_workspace/src/golang.org/x/tools/go/vcs"
    11  )
    12  
    13  var cmdRestore = &Command{
    14  	Name:  "restore",
    15  	Short: "check out listed dependency versions in GOPATH",
    16  	Long: `
    17  Restore checks out the Godeps-specified version of each package in GOPATH.
    18  
    19  NOTE: restore leaves git repositories in a detached state. go1.6+ no longer
    20  checks out the master branch when doing a "go get", see:
    21  https://github.com/golang/go/commit/42206598671a44111c8f726ad33dc7b265bdf669.
    22  
    23  `,
    24  	Run:          runRestore,
    25  	OnlyInGOPATH: true,
    26  }
    27  
    28  // Three phases:
    29  // 1. Download all deps
    30  // 2. Restore all deps (checkout the recorded rev)
    31  // 3. Attempt to load all deps as a simple consistency check
    32  func runRestore(cmd *Command, args []string) {
    33  	if len(build.Default.GOPATH) == 0 {
    34  		log.Println("Error restore requires GOPATH but it is empty.")
    35  		os.Exit(1)
    36  	}
    37  
    38  	var hadError bool
    39  	checkErr := func(s string) {
    40  		if hadError {
    41  			log.Println(s)
    42  			os.Exit(1)
    43  		}
    44  	}
    45  
    46  	g, err := loadDefaultGodepsFile()
    47  	if err != nil {
    48  		log.Fatalln(err)
    49  	}
    50  	for i, dep := range g.Deps {
    51  		verboseln("Downloading dependency (if needed):", dep.ImportPath)
    52  		err := download(&dep)
    53  		if err != nil {
    54  			log.Printf("error downloading dep (%s): %s\n", dep.ImportPath, err)
    55  			hadError = true
    56  		}
    57  		g.Deps[i] = dep
    58  	}
    59  	checkErr("Error downloading some deps. Aborting restore and check.")
    60  	for _, dep := range g.Deps {
    61  		verboseln("Restoring dependency (if needed):", dep.ImportPath)
    62  		err := restore(dep)
    63  		if err != nil {
    64  			log.Printf("error restoring dep (%s): %s\n", dep.ImportPath, err)
    65  			hadError = true
    66  		}
    67  	}
    68  	checkErr("Error restoring some deps. Aborting check.")
    69  	for _, dep := range g.Deps {
    70  		verboseln("Checking dependency:", dep.ImportPath)
    71  		_, err := LoadPackages(dep.ImportPath)
    72  		if err != nil {
    73  			log.Printf("Dep (%s) restored, but was unable to load it with error:\n\t%s\n", dep.ImportPath, err)
    74  			if me, ok := err.(errorMissingDep); ok {
    75  				log.Println("\tThis may be because the dependencies were saved with an older version of godep (< v33).")
    76  				log.Printf("\tTry `go get %s`. Then `godep save` to update deps.\n", me.i)
    77  			}
    78  			hadError = true
    79  		}
    80  	}
    81  	checkErr("Error checking some deps.")
    82  }
    83  
    84  var downloaded = make(map[string]bool)
    85  
    86  // download the given dependency.
    87  // 2 Passes: 1) go get -d <pkg>, 2) git pull (if necessary)
    88  func download(dep *Dependency) error {
    89  
    90  	rr, err := vcs.RepoRootForImportPath(dep.ImportPath, debug)
    91  	if err != nil {
    92  		debugln("Error determining repo root for", dep.ImportPath)
    93  		return err
    94  	}
    95  	ppln("rr", rr)
    96  
    97  	dep.vcs = cmd[rr.VCS]
    98  
    99  	// try to find an existing directory in the GOPATHs
   100  	for _, gp := range filepath.SplitList(build.Default.GOPATH) {
   101  		t := filepath.Join(gp, "src", rr.Root)
   102  		fi, err := os.Stat(t)
   103  		if err != nil {
   104  			continue
   105  		}
   106  		if fi.IsDir() {
   107  			dep.root = t
   108  			break
   109  		}
   110  	}
   111  
   112  	// If none found, just pick the first GOPATH entry (AFAICT that's what go get does)
   113  	if dep.root == "" {
   114  		dep.root = filepath.Join(filepath.SplitList(build.Default.GOPATH)[0], "src", rr.Root)
   115  	}
   116  	ppln("dep", dep)
   117  
   118  	if downloaded[rr.Repo] {
   119  		verboseln("Skipping already downloaded repo", rr.Repo)
   120  		return nil
   121  	}
   122  
   123  	fi, err := os.Stat(dep.root)
   124  	if err != nil {
   125  		if os.IsNotExist(err) {
   126  			if err := os.MkdirAll(filepath.Dir(dep.root), os.ModePerm); err != nil {
   127  				debugln("Error creating base dir of", dep.root)
   128  				return err
   129  			}
   130  			err := rr.VCS.CreateAtRev(dep.root, rr.Repo, dep.Rev)
   131  			debugln("CreatedAtRev", dep.root, rr.Repo, dep.Rev)
   132  			if err != nil {
   133  				debugln("CreateAtRev error", err)
   134  				return err
   135  			}
   136  			downloaded[rr.Repo] = true
   137  			return nil
   138  		}
   139  		debugln("Error checking repo root for", dep.ImportPath, "at", dep.root, ":", err)
   140  		return err
   141  	}
   142  
   143  	if !fi.IsDir() {
   144  		return errors.New("repo root src dir exists, but isn't a directory for " + dep.ImportPath + " at " + dep.root)
   145  	}
   146  
   147  	if !dep.vcs.exists(dep.root, dep.Rev) {
   148  		debugln("Updating existing", dep.root)
   149  		if dep.vcs == vcsGit {
   150  			detached, err := gitDetached(dep.root)
   151  			if err != nil {
   152  				return err
   153  			}
   154  			if detached {
   155  				db, err := gitDefaultBranch(dep.root)
   156  				if err != nil {
   157  					return err
   158  				}
   159  				if err := gitCheckout(dep.root, db); err != nil {
   160  					return err
   161  				}
   162  			}
   163  		}
   164  
   165  		dep.vcs.vcs.Download(dep.root)
   166  		downloaded[rr.Repo] = true
   167  	}
   168  
   169  	debugln("Nothing to download")
   170  	return nil
   171  }
   172  
   173  var restored = make(map[string]string) // dep.root -> dep.Rev
   174  
   175  // restore checks out the given revision.
   176  func restore(dep Dependency) error {
   177  	rev, ok := restored[dep.root]
   178  	debugln(rev)
   179  	debugln(ok)
   180  	debugln(dep.root)
   181  	if ok {
   182  		if rev != dep.Rev {
   183  			return errors.New("Wanted to restore rev " + dep.Rev + ", already restored rev " + rev + " for another package in the repo")
   184  		}
   185  		verboseln("Skipping already restored repo")
   186  		return nil
   187  	}
   188  
   189  	debugln("Restoring:", dep.ImportPath, dep.Rev)
   190  	err := dep.vcs.RevSync(dep.root, dep.Rev)
   191  	if err == nil {
   192  		restored[dep.root] = dep.Rev
   193  	}
   194  	return err
   195  }