blob: 5d421995fa172dd304f1761028428b2210c7363c [file] [log] [blame]
package action
import (
"os"
"path/filepath"
"sort"
"strings"
"github.com/Masterminds/glide/cfg"
"github.com/Masterminds/glide/dependency"
"github.com/Masterminds/glide/gb"
"github.com/Masterminds/glide/godep"
"github.com/Masterminds/glide/gpm"
"github.com/Masterminds/glide/msg"
gpath "github.com/Masterminds/glide/path"
"github.com/Masterminds/glide/util"
)
// Create creates/initializes a new Glide repository.
//
// This will fail if a glide.yaml already exists.
//
// By default, this will scan the present source code directory for dependencies.
//
// If skipImport is set to true, this will not attempt to import from an existing
// GPM, Godep, or GB project if one should exist. However, it will still attempt
// to read the local source to determine required packages.
func Create(base string, skipImport, nonInteractive bool) {
glidefile := gpath.GlideFile
// Guard against overwrites.
guardYAML(glidefile)
// Guess deps
conf := guessDeps(base, skipImport)
// Write YAML
msg.Info("Writing configuration file (%s)", glidefile)
if err := conf.WriteFile(glidefile); err != nil {
msg.Die("Could not save %s: %s", glidefile, err)
}
var res bool
if !nonInteractive {
msg.Info("Would you like Glide to help you find ways to improve your glide.yaml configuration?")
msg.Info("If you want to revisit this step you can use the config-wizard command at any time.")
msg.Info("Yes (Y) or No (N)?")
res = msg.PromptUntilYorN()
if res {
ConfigWizard(base)
}
}
if !res {
msg.Info("You can now edit the glide.yaml file. Consider:")
msg.Info("--> Using versions and ranges. See https://glide.sh/docs/versions/")
msg.Info("--> Adding additional metadata. See https://glide.sh/docs/glide.yaml/")
msg.Info("--> Running the config-wizard command to improve the versions in your configuration")
}
}
// guardYAML fails if the given file already exists.
//
// This prevents an important file from being overwritten.
func guardYAML(filename string) {
if _, err := os.Stat(filename); err == nil {
msg.Die("Cowardly refusing to overwrite existing YAML.")
}
}
// guessDeps attempts to resolve all of the dependencies for a given project.
//
// base is the directory to start with.
// skipImport will skip running the automatic imports.
//
// FIXME: This function is likely a one-off that has a more standard alternative.
// It's also long and could use a refactor.
func guessDeps(base string, skipImport bool) *cfg.Config {
buildContext, err := util.GetBuildContext()
if err != nil {
msg.Die("Failed to build an import context: %s", err)
}
name := buildContext.PackageName(base)
msg.Info("Generating a YAML configuration file and guessing the dependencies")
config := new(cfg.Config)
// Get the name of the top level package
config.Name = name
// Import by looking at other package managers and looking over the
// entire directory structure.
// Attempt to import from other package managers.
if !skipImport {
guessImportDeps(base, config)
}
importLen := len(config.Imports)
if importLen == 0 {
msg.Info("Scanning code to look for dependencies")
} else {
msg.Info("Scanning code to look for dependencies not found in import")
}
// Resolve dependencies by looking at the tree.
r, err := dependency.NewResolver(base)
if err != nil {
msg.Die("Error creating a dependency resolver: %s", err)
}
// When creating resolve the test dependencies as well as the application ones.
r.ResolveTest = true
h := &dependency.DefaultMissingPackageHandler{Missing: []string{}, Gopath: []string{}}
r.Handler = h
sortable, testSortable, err := r.ResolveLocal(false)
if err != nil {
msg.Die("Error resolving local dependencies: %s", err)
}
sort.Strings(sortable)
sort.Strings(testSortable)
vpath := r.VendorDir
if !strings.HasSuffix(vpath, "/") {
vpath = vpath + string(os.PathSeparator)
}
for _, pa := range sortable {
n := strings.TrimPrefix(pa, vpath)
root, subpkg := util.NormalizeName(n)
if !config.Imports.Has(root) && root != config.Name {
msg.Info("--> Found reference to %s\n", n)
d := &cfg.Dependency{
Name: root,
}
if len(subpkg) > 0 {
d.Subpackages = []string{subpkg}
}
config.Imports = append(config.Imports, d)
} else if config.Imports.Has(root) {
if len(subpkg) > 0 {
subpkg = strings.TrimPrefix(subpkg, "/")
d := config.Imports.Get(root)
if !d.HasSubpackage(subpkg) {
msg.Info("--> Adding sub-package %s to %s\n", subpkg, root)
d.Subpackages = append(d.Subpackages, subpkg)
}
}
}
}
for _, pa := range testSortable {
n := strings.TrimPrefix(pa, vpath)
root, subpkg := util.NormalizeName(n)
if config.Imports.Has(root) && root != config.Name {
msg.Debug("--> Found test reference to %s already listed as an import", n)
} else if !config.DevImports.Has(root) && root != config.Name {
msg.Info("--> Found test reference to %s", n)
d := &cfg.Dependency{
Name: root,
}
if len(subpkg) > 0 {
d.Subpackages = []string{subpkg}
}
config.DevImports = append(config.DevImports, d)
} else if config.DevImports.Has(root) {
if len(subpkg) > 0 {
subpkg = strings.TrimPrefix(subpkg, "/")
d := config.DevImports.Get(root)
if !d.HasSubpackage(subpkg) {
msg.Info("--> Adding test sub-package %s to %s\n", subpkg, root)
d.Subpackages = append(d.Subpackages, subpkg)
}
}
}
}
if len(config.Imports) == importLen && importLen != 0 {
msg.Info("--> Code scanning found no additional imports")
}
return config
}
func guessImportDeps(base string, config *cfg.Config) {
msg.Info("Attempting to import from other package managers (use --skip-import to skip)")
deps := []*cfg.Dependency{}
absBase, err := filepath.Abs(base)
if err != nil {
msg.Die("Failed to resolve location of %s: %s", base, err)
}
if d, ok := guessImportGodep(absBase); ok {
msg.Info("Importing Godep configuration")
msg.Warn("Godep uses commit id versions. Consider using Semantic Versions with Glide")
deps = d
} else if d, ok := guessImportGPM(absBase); ok {
msg.Info("Importing GPM configuration")
deps = d
} else if d, ok := guessImportGB(absBase); ok {
msg.Info("Importing GB configuration")
deps = d
}
for _, i := range deps {
if i.Reference == "" {
msg.Info("--> Found imported reference to %s", i.Name)
} else {
msg.Info("--> Found imported reference to %s at revision %s", i.Name, i.Reference)
}
config.Imports = append(config.Imports, i)
}
}
func guessImportGodep(dir string) ([]*cfg.Dependency, bool) {
d, err := godep.Parse(dir)
if err != nil || len(d) == 0 {
return []*cfg.Dependency{}, false
}
return d, true
}
func guessImportGPM(dir string) ([]*cfg.Dependency, bool) {
d, err := gpm.Parse(dir)
if err != nil || len(d) == 0 {
return []*cfg.Dependency{}, false
}
return d, true
}
func guessImportGB(dir string) ([]*cfg.Dependency, bool) {
d, err := gb.Parse(dir)
if err != nil || len(d) == 0 {
return []*cfg.Dependency{}, false
}
return d, true
}