github.com/golang/dep@v0.5.4/cmd/dep/init.go (about)

     1  // Copyright 2016 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package main
     6  
     7  import (
     8  	"context"
     9  	"flag"
    10  	"log"
    11  	"os"
    12  	"path/filepath"
    13  	"time"
    14  
    15  	"github.com/golang/dep"
    16  	"github.com/golang/dep/gps"
    17  	"github.com/golang/dep/internal/fs"
    18  	"github.com/pkg/errors"
    19  )
    20  
    21  const initShortHelp = `Set up a new Go project, or migrate an existing one`
    22  const initLongHelp = `
    23  Initialize the project at filepath root by parsing its dependencies, writing
    24  manifest and lock files, and vendoring the dependencies. If root isn't
    25  specified, use the current directory.
    26  
    27  When configuration for another dependency management tool is detected, it is
    28  imported into the initial manifest and lock. Use the -skip-tools flag to
    29  disable this behavior. The following external tools are supported:
    30  glide, godep, vndr, govend, gb, gvt, govendor, glock.
    31  
    32  Any dependencies that are not constrained by external configuration use the
    33  GOPATH analysis below.
    34  
    35  By default, the dependencies are resolved over the network. A version will be
    36  selected from the versions available from the upstream source per the following
    37  algorithm:
    38  
    39   - Tags conforming to semver (sorted by semver rules)
    40   - Default branch(es) (sorted lexicographically)
    41   - Non-semver tags (sorted lexicographically)
    42  
    43  An alternate mode can be activated by passing -gopath. In this mode, the version
    44  of each dependency will reflect the current state of the GOPATH. If a dependency
    45  doesn't exist in the GOPATH, a version will be selected based on the above
    46  network version selection algorithm.
    47  
    48  A Gopkg.toml file will be written with inferred version constraints for all
    49  direct dependencies. Gopkg.lock will be written with precise versions, and
    50  vendor/ will be populated with the precise versions written to Gopkg.lock.
    51  `
    52  
    53  func (cmd *initCommand) Name() string      { return "init" }
    54  func (cmd *initCommand) Args() string      { return "[root]" }
    55  func (cmd *initCommand) ShortHelp() string { return initShortHelp }
    56  func (cmd *initCommand) LongHelp() string  { return initLongHelp }
    57  func (cmd *initCommand) Hidden() bool      { return false }
    58  
    59  func (cmd *initCommand) Register(fs *flag.FlagSet) {
    60  	fs.BoolVar(&cmd.noExamples, "no-examples", false, "don't include example in Gopkg.toml")
    61  	fs.BoolVar(&cmd.skipTools, "skip-tools", false, "skip importing configuration from other dependency managers")
    62  	fs.BoolVar(&cmd.gopath, "gopath", false, "search in GOPATH for dependencies")
    63  }
    64  
    65  type initCommand struct {
    66  	noExamples bool
    67  	skipTools  bool
    68  	gopath     bool
    69  }
    70  
    71  func (cmd *initCommand) Run(ctx *dep.Ctx, args []string) error {
    72  	if len(args) > 1 {
    73  		return errors.Errorf("too many args (%d)", len(args))
    74  	}
    75  
    76  	var root string
    77  	if len(args) == 0 {
    78  		root = ctx.WorkingDir
    79  	} else {
    80  		root = args[0]
    81  		if !filepath.IsAbs(args[0]) {
    82  			root = filepath.Join(ctx.WorkingDir, args[0])
    83  		}
    84  		if err := os.MkdirAll(root, os.FileMode(0777)); err != nil {
    85  			return errors.Wrapf(err, "init failed: unable to create a directory at %s", root)
    86  		}
    87  	}
    88  
    89  	p, err := cmd.establishProjectAt(root, ctx)
    90  	if err != nil {
    91  		return err
    92  	}
    93  
    94  	sm, err := ctx.SourceManager()
    95  	if err != nil {
    96  		return errors.Wrap(err, "init failed: unable to create a source manager")
    97  	}
    98  	sm.UseDefaultSignalHandling()
    99  	defer sm.Release()
   100  
   101  	if ctx.Verbose {
   102  		ctx.Out.Println("Getting direct dependencies...")
   103  	}
   104  
   105  	directDeps, err := p.GetDirectDependencyNames(sm)
   106  	if err != nil {
   107  		return errors.Wrap(err, "init failed: unable to determine direct dependencies")
   108  	}
   109  	if ctx.Verbose {
   110  		ctx.Out.Printf("Checked %d directories for packages.\nFound %d direct dependencies.\n", len(p.RootPackageTree.Packages), len(directDeps))
   111  	}
   112  
   113  	// Initialize with imported data, then fill in the gaps using the GOPATH
   114  	rootAnalyzer := newRootAnalyzer(cmd.skipTools, ctx, directDeps, sm)
   115  	p.Manifest, p.Lock, err = rootAnalyzer.InitializeRootManifestAndLock(root, p.ImportRoot)
   116  	if err != nil {
   117  		return errors.Wrap(err, "init failed: unable to prepare an initial manifest and lock for the solver")
   118  	}
   119  
   120  	// Set default prune options for go-tests and unused-packages
   121  	p.Manifest.PruneOptions.DefaultOptions = gps.PruneNestedVendorDirs | gps.PruneGoTestFiles | gps.PruneUnusedPackages
   122  
   123  	if cmd.gopath {
   124  		gs := newGopathScanner(ctx, directDeps, sm)
   125  		err = gs.InitializeRootManifestAndLock(p.Manifest, p.Lock)
   126  		if err != nil {
   127  			return errors.Wrap(err, "init failed: unable to scan the GOPATH for dependencies")
   128  		}
   129  	}
   130  
   131  	rootAnalyzer.skipTools = importDuringSolve()
   132  	copyLock := *p.Lock // Copy lock before solving. Use this to separate new lock projects from solved lock
   133  
   134  	params := gps.SolveParameters{
   135  		RootDir:         root,
   136  		RootPackageTree: p.RootPackageTree,
   137  		Manifest:        p.Manifest,
   138  		Lock:            p.Lock,
   139  		ProjectAnalyzer: rootAnalyzer,
   140  	}
   141  
   142  	if ctx.Verbose {
   143  		params.TraceLogger = ctx.Err
   144  	}
   145  
   146  	if err := ctx.ValidateParams(sm, params); err != nil {
   147  		return errors.Wrapf(err, "init failed: validation of solve parameters failed")
   148  	}
   149  
   150  	s, err := gps.Prepare(params, sm)
   151  	if err != nil {
   152  		return errors.Wrap(err, "init failed: unable to prepare the solver")
   153  	}
   154  
   155  	soln, err := s.Solve(context.TODO())
   156  	if err != nil {
   157  		err = handleAllTheFailuresOfTheWorld(err)
   158  		return errors.Wrap(err, "init failed: unable to solve the dependency graph")
   159  	}
   160  	p.Lock = dep.LockFromSolution(soln, p.Manifest.PruneOptions)
   161  
   162  	rootAnalyzer.FinalizeRootManifestAndLock(p.Manifest, p.Lock, copyLock)
   163  
   164  	// Pass timestamp (yyyyMMddHHmmss format) as suffix to backup name.
   165  	vendorbak, err := dep.BackupVendor(filepath.Join(root, "vendor"), time.Now().Format("20060102150405"))
   166  	if err != nil {
   167  		return errors.Wrap(err, "init failed: first backup vendor/, delete it, and then retry the previous command: failed to backup existing vendor directory")
   168  	}
   169  	if vendorbak != "" {
   170  		ctx.Err.Printf("Old vendor backed up to %v", vendorbak)
   171  	}
   172  
   173  	sw, err := dep.NewSafeWriter(p.Manifest, nil, p.Lock, dep.VendorAlways, p.Manifest.PruneOptions, nil)
   174  	if err != nil {
   175  		return errors.Wrap(err, "init failed: unable to create a SafeWriter")
   176  	}
   177  
   178  	var logger *log.Logger
   179  	if ctx.Verbose {
   180  		logger = ctx.Err
   181  	}
   182  	if err := sw.Write(root, sm, !cmd.noExamples, logger); err != nil {
   183  		return errors.Wrap(err, "init failed: unable to write the manifest, lock and vendor directory to disk")
   184  	}
   185  
   186  	return nil
   187  }
   188  
   189  // establishProjectAt attempts to set up the provided path as the root for the
   190  // project to be created.
   191  //
   192  // It checks for being within a GOPATH, that there is no pre-existing manifest
   193  // and lock, and that we can successfully infer the root import path from
   194  // GOPATH.
   195  //
   196  // If successful, it returns a dep.Project, ready for further use.
   197  func (cmd *initCommand) establishProjectAt(root string, ctx *dep.Ctx) (*dep.Project, error) {
   198  	var err error
   199  	p := new(dep.Project)
   200  	if err = p.SetRoot(root); err != nil {
   201  		return nil, errors.Wrapf(err, "init failed: unable to set the root project to %s", root)
   202  	}
   203  
   204  	ctx.GOPATH, err = ctx.DetectProjectGOPATH(p)
   205  	if err != nil {
   206  		return nil, errors.Wrapf(err, "init failed: unable to detect the containing GOPATH")
   207  	}
   208  
   209  	mf := filepath.Join(root, dep.ManifestName)
   210  	lf := filepath.Join(root, dep.LockName)
   211  
   212  	mok, err := fs.IsRegular(mf)
   213  	if err != nil {
   214  		return nil, errors.Wrapf(err, "init failed: unable to check for an existing manifest at %s", mf)
   215  	}
   216  	if mok {
   217  		return nil, errors.Errorf("init aborted: manifest already exists at %s", mf)
   218  	}
   219  
   220  	lok, err := fs.IsRegular(lf)
   221  	if err != nil {
   222  		return nil, errors.Wrapf(err, "init failed: unable to check for an existing lock at %s", lf)
   223  	}
   224  	if lok {
   225  		return nil, errors.Errorf("invalid aborted: lock already exists at %s", lf)
   226  	}
   227  
   228  	ip, err := ctx.ImportForAbs(root)
   229  	if err != nil {
   230  		return nil, errors.Wrapf(err, "init failed: unable to determine the import path for the root project %s", root)
   231  	}
   232  	p.ImportRoot = gps.ProjectRoot(ip)
   233  
   234  	return p, nil
   235  }