github.com/MealCraft/glide@v0.13.4/action/create.go (about) 1 package action 2 3 import ( 4 "os" 5 "path/filepath" 6 "sort" 7 "strings" 8 9 "github.com/Masterminds/glide/cfg" 10 "github.com/Masterminds/glide/dependency" 11 "github.com/Masterminds/glide/gb" 12 "github.com/Masterminds/glide/godep" 13 "github.com/Masterminds/glide/gpm" 14 "github.com/Masterminds/glide/msg" 15 gpath "github.com/Masterminds/glide/path" 16 "github.com/Masterminds/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 }