github.com/xkeyideal/glide@v0.0.0-20171121052037-a806f0aaeda0/action/create.go (about)

     1  package action
     2  
     3  import (
     4  	"os"
     5  	"path/filepath"
     6  	"sort"
     7  	"strings"
     8  
     9  	"github.com/xkeyideal/glide/cfg"
    10  	"github.com/xkeyideal/glide/dependency"
    11  	"github.com/xkeyideal/glide/gb"
    12  	"github.com/xkeyideal/glide/godep"
    13  	"github.com/xkeyideal/glide/gpm"
    14  	"github.com/xkeyideal/glide/msg"
    15  	gpath "github.com/xkeyideal/glide/path"
    16  	"github.com/xkeyideal/glide/util"
    17  )
    18  
    19  // Create creates/initializes a new Glide repository.
    20  //
    21  // This will fail if a glide.yaml already exists.
    22  //
    23  // By default, this will scan the present source code directory for dependencies.
    24  //
    25  // If skipImport is set to true, this will not attempt to import from an existing
    26  // GPM, Godep, or GB project if one should exist. However, it will still attempt
    27  // to read the local source to determine required packages.
    28  func Create(base string, skipImport, nonInteractive bool) {
    29  	glidefile := gpath.GlideFile
    30  	// Guard against overwrites.
    31  	guardYAML(glidefile)
    32  
    33  	// Guess deps
    34  	conf := guessDeps(base, skipImport)
    35  	// Write YAML
    36  	msg.Info("Writing configuration file (%s)", glidefile)
    37  	if err := conf.WriteFile(glidefile); err != nil {
    38  		msg.Die("Could not save %s: %s", glidefile, err)
    39  	}
    40  
    41  	var res bool
    42  	if !nonInteractive {
    43  		msg.Info("Would you like Glide to help you find ways to improve your glide.yaml configuration?")
    44  		msg.Info("If you want to revisit this step you can use the config-wizard command at any time.")
    45  		msg.Info("Yes (Y) or No (N)?")
    46  		res = msg.PromptUntilYorN()
    47  		if res {
    48  			ConfigWizard(base)
    49  		}
    50  	}
    51  
    52  	if !res {
    53  		msg.Info("You can now edit the glide.yaml file. Consider:")
    54  		msg.Info("--> Using versions and ranges. See https://glide.sh/docs/versions/")
    55  		msg.Info("--> Adding additional metadata. See https://glide.sh/docs/glide.yaml/")
    56  		msg.Info("--> Running the config-wizard command to improve the versions in your configuration")
    57  	}
    58  }
    59  
    60  // guardYAML fails if the given file already exists.
    61  //
    62  // This prevents an important file from being overwritten.
    63  func guardYAML(filename string) {
    64  	if _, err := os.Stat(filename); err == nil {
    65  		msg.Die("Cowardly refusing to overwrite existing YAML.")
    66  	}
    67  }
    68  
    69  // guessDeps attempts to resolve all of the dependencies for a given project.
    70  //
    71  // base is the directory to start with.
    72  // skipImport will skip running the automatic imports.
    73  //
    74  // FIXME: This function is likely a one-off that has a more standard alternative.
    75  // It's also long and could use a refactor.
    76  func guessDeps(base string, skipImport bool) *cfg.Config {
    77  	buildContext, err := util.GetBuildContext()
    78  	if err != nil {
    79  		msg.Die("Failed to build an import context: %s", err)
    80  	}
    81  	name := buildContext.PackageName(base)
    82  
    83  	msg.Info("Generating a YAML configuration file and guessing the dependencies")
    84  
    85  	config := new(cfg.Config)
    86  
    87  	// Get the name of the top level package
    88  	config.Name = name
    89  
    90  	// Import by looking at other package managers and looking over the
    91  	// entire directory structure.
    92  
    93  	// Attempt to import from other package managers.
    94  	if !skipImport {
    95  		guessImportDeps(base, config)
    96  	}
    97  
    98  	importLen := len(config.Imports)
    99  	if importLen == 0 {
   100  		msg.Info("Scanning code to look for dependencies")
   101  	} else {
   102  		msg.Info("Scanning code to look for dependencies not found in import")
   103  	}
   104  
   105  	// Resolve dependencies by looking at the tree.
   106  	r, err := dependency.NewResolver(base)
   107  	if err != nil {
   108  		msg.Die("Error creating a dependency resolver: %s", err)
   109  	}
   110  
   111  	// When creating resolve the test dependencies as well as the application ones.
   112  	r.ResolveTest = true
   113  
   114  	h := &dependency.DefaultMissingPackageHandler{Missing: []string{}, Gopath: []string{}}
   115  	r.Handler = h
   116  
   117  	sortable, testSortable, err := r.ResolveLocal(false)
   118  	if err != nil {
   119  		msg.Die("Error resolving local dependencies: %s", err)
   120  	}
   121  
   122  	sort.Strings(sortable)
   123  	sort.Strings(testSortable)
   124  
   125  	vpath := r.VendorDir
   126  	if !strings.HasSuffix(vpath, "/") {
   127  		vpath = vpath + string(os.PathSeparator)
   128  	}
   129  
   130  	for _, pa := range sortable {
   131  		n := strings.TrimPrefix(pa, vpath)
   132  		root, subpkg := util.NormalizeName(n)
   133  
   134  		if !config.Imports.Has(root) && root != config.Name {
   135  			msg.Info("--> Found reference to %s\n", n)
   136  			d := &cfg.Dependency{
   137  				Name: root,
   138  			}
   139  			if len(subpkg) > 0 {
   140  				d.Subpackages = []string{subpkg}
   141  			}
   142  			config.Imports = append(config.Imports, d)
   143  		} else if config.Imports.Has(root) {
   144  			if len(subpkg) > 0 {
   145  				subpkg = strings.TrimPrefix(subpkg, "/")
   146  				d := config.Imports.Get(root)
   147  				if !d.HasSubpackage(subpkg) {
   148  					msg.Info("--> Adding sub-package %s to %s\n", subpkg, root)
   149  					d.Subpackages = append(d.Subpackages, subpkg)
   150  				}
   151  			}
   152  		}
   153  	}
   154  
   155  	for _, pa := range testSortable {
   156  		n := strings.TrimPrefix(pa, vpath)
   157  		root, subpkg := util.NormalizeName(n)
   158  
   159  		if config.Imports.Has(root) && root != config.Name {
   160  			msg.Debug("--> Found test reference to %s already listed as an import", n)
   161  		} else if !config.DevImports.Has(root) && root != config.Name {
   162  			msg.Info("--> Found test reference to %s", n)
   163  			d := &cfg.Dependency{
   164  				Name: root,
   165  			}
   166  			if len(subpkg) > 0 {
   167  				d.Subpackages = []string{subpkg}
   168  			}
   169  			config.DevImports = append(config.DevImports, d)
   170  		} else if config.DevImports.Has(root) {
   171  			if len(subpkg) > 0 {
   172  				subpkg = strings.TrimPrefix(subpkg, "/")
   173  				d := config.DevImports.Get(root)
   174  				if !d.HasSubpackage(subpkg) {
   175  					msg.Info("--> Adding test sub-package %s to %s\n", subpkg, root)
   176  					d.Subpackages = append(d.Subpackages, subpkg)
   177  				}
   178  			}
   179  		}
   180  	}
   181  
   182  	if len(config.Imports) == importLen && importLen != 0 {
   183  		msg.Info("--> Code scanning found no additional imports")
   184  	}
   185  
   186  	return config
   187  }
   188  
   189  func guessImportDeps(base string, config *cfg.Config) {
   190  	msg.Info("Attempting to import from other package managers (use --skip-import to skip)")
   191  	deps := []*cfg.Dependency{}
   192  	absBase, err := filepath.Abs(base)
   193  	if err != nil {
   194  		msg.Die("Failed to resolve location of %s: %s", base, err)
   195  	}
   196  
   197  	if d, ok := guessImportGodep(absBase); ok {
   198  		msg.Info("Importing Godep configuration")
   199  		msg.Warn("Godep uses commit id versions. Consider using Semantic Versions with Glide")
   200  		deps = d
   201  	} else if d, ok := guessImportGPM(absBase); ok {
   202  		msg.Info("Importing GPM configuration")
   203  		deps = d
   204  	} else if d, ok := guessImportGB(absBase); ok {
   205  		msg.Info("Importing GB configuration")
   206  		deps = d
   207  	}
   208  
   209  	for _, i := range deps {
   210  		if i.Reference == "" {
   211  			msg.Info("--> Found imported reference to %s", i.Name)
   212  		} else {
   213  			msg.Info("--> Found imported reference to %s at revision %s", i.Name, i.Reference)
   214  		}
   215  
   216  		config.Imports = append(config.Imports, i)
   217  	}
   218  }
   219  
   220  func guessImportGodep(dir string) ([]*cfg.Dependency, bool) {
   221  	d, err := godep.Parse(dir)
   222  	if err != nil || len(d) == 0 {
   223  		return []*cfg.Dependency{}, false
   224  	}
   225  
   226  	return d, true
   227  }
   228  
   229  func guessImportGPM(dir string) ([]*cfg.Dependency, bool) {
   230  	d, err := gpm.Parse(dir)
   231  	if err != nil || len(d) == 0 {
   232  		return []*cfg.Dependency{}, false
   233  	}
   234  
   235  	return d, true
   236  }
   237  
   238  func guessImportGB(dir string) ([]*cfg.Dependency, bool) {
   239  	d, err := gb.Parse(dir)
   240  	if err != nil || len(d) == 0 {
   241  		return []*cfg.Dependency{}, false
   242  	}
   243  
   244  	return d, true
   245  }