github.com/anthonymayer/glide@v0.0.0-20160224162501-bff8b50d232e/glide.go (about) 1 // Glide is a command line utility that manages Go project dependencies and 2 // your GOPATH. 3 // 4 // Dependencies are managed via a glide.yaml in the root of a project. The yaml 5 // 6 // Params: 7 // - filename (string): The name of the glide YAML file. Default is glide.yaml. 8 // - project (string): The name of the project. Default is 'main'. 9 // file lets you specify projects, versions (tags, branches, or references), 10 // and even alias one location in as other one. Aliasing is useful when supporting 11 // forks without needing to rewrite the imports in a codebase. 12 // 13 // A glide.yaml file looks like: 14 // 15 // package: github.com/Masterminds/glide 16 // imports: 17 // - package: github.com/Masterminds/cookoo 18 // vcs: git 19 // ref: 1.1.0 20 // subpackages: ** 21 // - package: github.com/kylelemons/go-gypsy 22 // subpackages: yaml 23 // 24 // Glide puts dependencies in a vendor directory. Go utilities require this to 25 // be in your GOPATH. Glide makes this easy. Use the `glide in` command to enter 26 // a shell (your default) with the GOPATH set to the projects vendor directory. 27 // To leave this shell simply exit it. 28 // 29 // If your .bashrc, .zshrc, or other startup shell sets your GOPATH you many need 30 // to optionally set it using something like: 31 // 32 // if [ "" = "${GOPATH}" ]; then 33 // export GOPATH="/some/dir" 34 // fi 35 // 36 // For more information use the `glide help` command or see https://github.com/Masterminds/glide 37 package main 38 39 import ( 40 "path/filepath" 41 42 "github.com/Masterminds/glide/action" 43 "github.com/Masterminds/glide/msg" 44 gpath "github.com/Masterminds/glide/path" 45 "github.com/Masterminds/glide/repo" 46 "github.com/Masterminds/glide/util" 47 48 "github.com/codegangsta/cli" 49 50 "fmt" 51 "os" 52 "os/user" 53 ) 54 55 var version = "dev" 56 57 const usage = `The lightweight vendor package manager for your Go projects. 58 59 Each project should have a 'glide.yaml' file in the project directory. Files 60 look something like this: 61 62 package: github.com/Masterminds/glide 63 imports: 64 - package: github.com/Masterminds/cookoo 65 vcs: git 66 ref: 1.1.0 67 subpackages: ** 68 - package: github.com/kylelemons/go-gypsy 69 subpackages: yaml 70 flatten: true 71 72 NOTE: As of Glide 0.5, the commands 'into', 'gopath', 'status', and 'env' 73 no longer exist. 74 ` 75 76 // VendorDir default vendor directory name 77 var VendorDir = "vendor" 78 79 func main() { 80 app := cli.NewApp() 81 app.Name = "glide" 82 app.Usage = usage 83 app.Version = version 84 app.Flags = []cli.Flag{ 85 cli.StringFlag{ 86 Name: "yaml, y", 87 Value: "glide.yaml", 88 Usage: "Set a YAML configuration file.", 89 }, 90 cli.BoolFlag{ 91 Name: "quiet, q", 92 Usage: "Quiet (no info or debug messages)", 93 }, 94 cli.BoolFlag{ 95 Name: "debug", 96 Usage: "Print Debug messages (verbose)", 97 }, 98 cli.StringFlag{ 99 Name: "home", 100 Value: defaultGlideDir(), 101 Usage: "The location of Glide files", 102 EnvVar: "GLIDE_HOME", 103 }, 104 cli.BoolFlag{ 105 Name: "no-color", 106 Usage: "Turn off colored output for log messages", 107 }, 108 } 109 app.CommandNotFound = func(c *cli.Context, command string) { 110 // TODO: Set some useful env vars. 111 action.Plugin(command, os.Args) 112 } 113 app.Before = startup 114 app.Commands = commands() 115 116 // Detect errors from the Before and After calls and exit on them. 117 if err := app.Run(os.Args); err != nil { 118 msg.Err(err.Error()) 119 os.Exit(1) 120 } 121 122 // If there was a Error message exit non-zero. 123 if msg.HasErrored() { 124 m := msg.Color(msg.Red, "An Error has occurred") 125 msg.Msg(m) 126 os.Exit(2) 127 } 128 } 129 130 func commands() []cli.Command { 131 return []cli.Command{ 132 { 133 Name: "create", 134 ShortName: "init", 135 Usage: "Initialize a new project, creating a glide.yaml file", 136 Description: `This command starts from a project without Glide and 137 sets it up. It generates a glide.yaml file, parsing your codebase to guess 138 the dependencies to include. Once this step is done you may edit the 139 glide.yaml file to update imported dependency properties such as the version 140 or version range to include. 141 142 To fetch the dependencies you may run 'glide install'.`, 143 Flags: []cli.Flag{ 144 cli.BoolFlag{ 145 Name: "skip-import", 146 Usage: "When initializing skip importing from other package managers.", 147 }, 148 }, 149 Action: func(c *cli.Context) { 150 action.Create(".", c.Bool("skip-import")) 151 }, 152 }, 153 { 154 Name: "get", 155 Usage: "Install one or more packages into `vendor/` and add dependency to glide.yaml.", 156 Description: `Gets one or more package (like 'go get') and then adds that file 157 to the glide.yaml file. Multiple package names can be specified on one line. 158 159 $ glide get github.com/Masterminds/cookoo/web 160 161 The above will install the project github.com/Masterminds/cookoo and add 162 the subpackage 'web'. 163 164 If a fetched dependency has a glide.yaml file, configuration from Godep, 165 GPM, or GB Glide that configuration will be used to find the dependencies 166 and versions to fetch. If those are not available the dependent packages will 167 be fetched as either a version specified elsewhere or the latest version. 168 169 When adding a new dependency Glide will perform an update to work out the 170 the versions to use from the dependency tree. This will generate an updated 171 glide.lock file with specific locked versions to use. 172 `, 173 Flags: []cli.Flag{ 174 cli.BoolFlag{ 175 Name: "insecure", 176 Usage: "Use http:// rather than https:// to retrieve pacakges.", 177 }, 178 cli.BoolFlag{ 179 Name: "no-recursive, quick", 180 Usage: "Disable updating dependencies' dependencies.", 181 }, 182 cli.BoolFlag{ 183 Name: "force", 184 Usage: "If there was a change in the repo or VCS switch to new one. Warning, changes will be lost.", 185 }, 186 cli.BoolFlag{ 187 Name: "all-dependencies", 188 Usage: "This will resolve all dependencies for all packages, not just those directly used.", 189 }, 190 cli.BoolFlag{ 191 Name: "update-vendored, u", 192 Usage: "Update vendored packages (without local VCS repo). Warning, changes will be lost.", 193 }, 194 cli.BoolFlag{ 195 Name: "cache", 196 Usage: "When downloading dependencies attempt to cache them.", 197 }, 198 cli.BoolFlag{ 199 Name: "cache-gopath", 200 Usage: "When downloading dependencies attempt to put them in the GOPATH, too.", 201 }, 202 cli.BoolFlag{ 203 Name: "use-gopath", 204 Usage: "Copy dependencies from the GOPATH if they exist there.", 205 }, 206 cli.BoolFlag{ 207 Name: "resolve-current", 208 Usage: "Resolve dependencies for only the current system rather than all build modes.", 209 }, 210 }, 211 Action: func(c *cli.Context) { 212 if len(c.Args()) < 1 { 213 fmt.Println("Oops! Package name is required.") 214 os.Exit(1) 215 } 216 217 if c.Bool("resolve-current") { 218 util.ResolveCurrent = true 219 msg.Warn("Only resolving dependencies for the current OS/Arch") 220 } 221 222 inst := &repo.Installer{ 223 Force: c.Bool("force"), 224 UseCache: c.Bool("cache"), 225 UseGopath: c.Bool("use-gopath"), 226 UseCacheGopath: c.Bool("cache-gopath"), 227 UpdateVendored: c.Bool("update-vendored"), 228 ResolveAllFiles: c.Bool("all-dependencies"), 229 } 230 packages := []string(c.Args()) 231 insecure := c.Bool("insecure") 232 action.Get(packages, inst, insecure, c.Bool("no-recursive")) 233 }, 234 }, 235 { 236 Name: "remove", 237 ShortName: "rm", 238 Usage: "Remove a package from the glide.yaml file, and regenerate the lock file.", 239 Description: `This takes one or more package names, and removes references from the glide.yaml file. 240 This will rebuild the glide lock file with the following constraints: 241 242 - Dependencies are re-negotiated. Any that are no longer used are left out of the lock. 243 - Minor version re-nogotiation is performed on remaining dependencies. 244 - No updates are peformed. You may want to run 'glide up' to accomplish that. 245 `, 246 Flags: []cli.Flag{ 247 cli.BoolFlag{ 248 Name: "delete,d", 249 Usage: "Also delete from vendor/ any packages that are no longer used.", 250 }, 251 }, 252 Action: func(c *cli.Context) { 253 if len(c.Args()) < 1 { 254 fmt.Println("Oops! At least one package name is required.") 255 os.Exit(1) 256 } 257 258 if c.Bool("delete") { 259 // FIXME: Implement this in the installer. 260 fmt.Println("Delete is not currently implemented.") 261 } 262 263 inst := &repo.Installer{ 264 Force: c.Bool("force"), 265 } 266 packages := []string(c.Args()) 267 action.Remove(packages, inst) 268 }, 269 }, 270 { 271 Name: "import", 272 Usage: "Import files from other dependency management systems.", 273 Subcommands: []cli.Command{ 274 { 275 Name: "godep", 276 Usage: "Import Godep's Godeps.json files and display the would-be yaml file", 277 Flags: []cli.Flag{ 278 cli.StringFlag{ 279 Name: "file, f", 280 Usage: "Save all of the discovered dependencies to a Glide YAML file.", 281 }, 282 }, 283 Action: func(c *cli.Context) { 284 action.ImportGodep(c.String("file")) 285 }, 286 }, 287 { 288 Name: "gpm", 289 Usage: "Import GPM's Godeps and Godeps-Git files and display the would-be yaml file", 290 Flags: []cli.Flag{ 291 cli.StringFlag{ 292 Name: "file, f", 293 Usage: "Save all of the discovered dependencies to a Glide YAML file.", 294 }, 295 }, 296 Action: func(c *cli.Context) { 297 action.ImportGPM(c.String("file")) 298 }, 299 }, 300 { 301 Name: "gb", 302 Usage: "Import gb's manifest file and display the would-be yaml file", 303 Flags: []cli.Flag{ 304 cli.StringFlag{ 305 Name: "file, f", 306 Usage: "Save all of the discovered dependencies to a Glide YAML file.", 307 }, 308 }, 309 Action: func(c *cli.Context) { 310 action.ImportGB(c.String("file")) 311 }, 312 }, 313 }, 314 }, 315 { 316 Name: "name", 317 Usage: "Print the name of this project.", 318 Description: `Read the glide.yaml file and print the name given on the 'package' line.`, 319 Action: func(c *cli.Context) { 320 action.Name() 321 }, 322 }, 323 { 324 Name: "novendor", 325 ShortName: "nv", 326 Usage: "List all non-vendor paths in a directory.", 327 Description: `Given a directory, list all the relevant Go paths that are not vendored. 328 329 Example: 330 331 $ go test $(glide novendor) 332 `, 333 Flags: []cli.Flag{ 334 cli.StringFlag{ 335 Name: "dir,d", 336 Usage: "Specify a directory to run novendor against.", 337 Value: ".", 338 }, 339 cli.BoolFlag{ 340 Name: "no-subdir,x", 341 Usage: "Specify this to prevent nv from append '/...' to all directories.", 342 }, 343 }, 344 Action: func(c *cli.Context) { 345 action.NoVendor(c.String("dir"), true, !c.Bool("no-subdir")) 346 }, 347 }, 348 { 349 Name: "rebuild", 350 Usage: "Rebuild ('go build') the dependencies", 351 Description: `This rebuilds the packages' '.a' files. On some systems 352 this can improve performance on subsequent 'go run' and 'go build' calls.`, 353 Action: func(c *cli.Context) { 354 action.Rebuild() 355 }, 356 }, 357 { 358 Name: "install", 359 ShortName: "i", 360 Usage: "Install a project's dependencies", 361 Description: `This uses the native VCS of each packages to install 362 the appropriate version. There are two ways a projects dependencies can 363 be installed. When there is a glide.yaml file defining the dependencies but 364 no lock file (glide.lock) the dependencies are installed using the "update" 365 command and a glide.lock file is generated pinning all dependencies. If a 366 glide.lock file is already present the dependencies are installed or updated 367 from the lock file.`, 368 Flags: []cli.Flag{ 369 cli.BoolFlag{ 370 Name: "delete", 371 Usage: "Delete vendor packages not specified in config.", 372 }, 373 cli.BoolFlag{ 374 Name: "force", 375 Usage: "If there was a change in the repo or VCS switch to new one. Warning: changes will be lost.", 376 }, 377 cli.BoolFlag{ 378 Name: "update-vendored, u", 379 Usage: "Update vendored packages (without local VCS repo). Warning: this may destroy local modifications to vendor/.", 380 }, 381 cli.StringFlag{ 382 Name: "file, f", 383 Usage: "Save all of the discovered dependencies to a Glide YAML file. (DEPRECATED: This has no impact.)", 384 }, 385 cli.BoolFlag{ 386 Name: "cache", 387 Usage: "When downloading dependencies attempt to cache them.", 388 }, 389 cli.BoolFlag{ 390 Name: "cache-gopath", 391 Usage: "When downloading dependencies attempt to put them in the GOPATH, too.", 392 }, 393 cli.BoolFlag{ 394 Name: "use-gopath", 395 Usage: "Copy dependencies from the GOPATH if they exist there.", 396 }, 397 }, 398 Action: func(c *cli.Context) { 399 installer := &repo.Installer{ 400 DeleteUnused: c.Bool("deleteOptIn"), 401 UpdateVendored: c.Bool("update-vendored"), 402 Force: c.Bool("force"), 403 UseCache: c.Bool("cache"), 404 UseCacheGopath: c.Bool("cache-gopath"), 405 UseGopath: c.Bool("use-gopath"), 406 Home: gpath.Home(), 407 } 408 409 action.Install(installer) 410 }, 411 }, 412 { 413 Name: "update", 414 ShortName: "up", 415 Usage: "Update a project's dependencies", 416 Description: `This uses the native VCS of each package to try to 417 pull the most applicable updates. Packages with fixed refs (Versions or 418 tags) will not be updated. Packages with no ref or with a branch ref will 419 be updated as expected. 420 421 If a dependency has a glide.yaml file, update will read that file and 422 update those dependencies accordingly. Those dependencies are maintained in 423 a the top level 'vendor/' directory. 'vendor/foo/bar' will have its 424 dependencies stored in 'vendor/'. This behavior can be disabled with 425 '--no-recursive'. When this behavior is skipped a glide.lock file is not 426 generated because the full dependency tree cannot be known. 427 428 Glide will also import Godep, GB, and GPM files as it finds them in dependencies. 429 It will create a glide.yaml file from the Godeps data, and then update. This 430 has no effect if '--no-recursive' is set. 431 432 If the '--update-vendored' flag (aliased to '-u') is present vendored 433 dependencies, stored in your projects VCS repository, will be updated. This 434 works by removing the old package, checking out an the repo and setting the 435 version, and removing the VCS directory. 436 437 By default, packages that are discovered are considered transient, and are 438 not stored in the glide.yaml file. The --file=NAME.yaml flag allows you 439 to save the discovered dependencies to a YAML file. 440 `, 441 Flags: []cli.Flag{ 442 cli.BoolFlag{ 443 Name: "delete", 444 Usage: "Delete vendor packages not specified in config.", 445 }, 446 cli.BoolFlag{ 447 Name: "no-recursive, quick", 448 Usage: "Disable updating dependencies' dependencies. Only update things in glide.yaml.", 449 }, 450 cli.BoolFlag{ 451 Name: "force", 452 Usage: "If there was a change in the repo or VCS switch to new one. Warning, changes will be lost.", 453 }, 454 cli.BoolFlag{ 455 Name: "all-dependencies", 456 Usage: "This will resolve all dependencies for all packages, not just those directly used.", 457 }, 458 cli.BoolFlag{ 459 Name: "update-vendored, u", 460 Usage: "Update vendored packages (without local VCS repo). Warning, changes will be lost.", 461 }, 462 cli.StringFlag{ 463 Name: "file, f", 464 Usage: "Save all of the discovered dependencies to a Glide YAML file.", 465 }, 466 cli.BoolFlag{ 467 Name: "cache", 468 Usage: "When downloading dependencies attempt to cache them.", 469 }, 470 cli.BoolFlag{ 471 Name: "cache-gopath", 472 Usage: "When downloading dependencies attempt to put them in the GOPATH, too.", 473 }, 474 cli.BoolFlag{ 475 Name: "use-gopath", 476 Usage: "Copy dependencies from the GOPATH if they exist there.", 477 }, 478 cli.BoolFlag{ 479 Name: "resolve-current", 480 Usage: "Resolve dependencies for only the current system rather than all build modes.", 481 }, 482 }, 483 Action: func(c *cli.Context) { 484 485 if c.Bool("resolve-current") { 486 util.ResolveCurrent = true 487 msg.Warn("Only resolving dependencies for the current OS/Arch") 488 } 489 490 installer := &repo.Installer{ 491 DeleteUnused: c.Bool("deleteOptIn"), 492 UpdateVendored: c.Bool("update-vendored"), 493 ResolveAllFiles: c.Bool("all-dependencies"), 494 Force: c.Bool("force"), 495 UseCache: c.Bool("cache"), 496 UseCacheGopath: c.Bool("cache-gopath"), 497 UseGopath: c.Bool("use-gopath"), 498 Home: gpath.Home(), 499 } 500 501 action.Update(installer, c.Bool("no-recursive")) 502 }, 503 }, 504 { 505 Name: "tree", 506 Usage: "Tree prints the dependencies of this project as a tree.", 507 Description: `This scans a project's source files and builds a tree 508 representation of the import graph. 509 510 It ignores testdata/ and directories that begin with . or _. Packages in 511 vendor/ are only included if they are referenced by the main project or 512 one of its dependencies.`, 513 Action: func(c *cli.Context) { 514 action.Tree(".", false) 515 }, 516 }, 517 { 518 Name: "list", 519 Usage: "List prints all dependencies that the present code references.", 520 Description: `List scans your code and lists all of the packages that are used. 521 522 It does not use the glide.yaml. Instead, it inspects the code to determine what packages are 523 imported. 524 525 Directories that begin with . or _ are ignored, as are testdata directories. Packages in 526 vendor are only included if they are used by the project. 527 `, 528 Action: func(c *cli.Context) { 529 action.List(".", true) 530 }, 531 }, 532 { 533 Name: "about", 534 Usage: "Learn about Glide", 535 Action: func(c *cli.Context) { 536 action.About() 537 }, 538 }, 539 } 540 } 541 542 func defaultGlideDir() string { 543 c, err := user.Current() 544 if err != nil { 545 return "" 546 } 547 return filepath.Join(c.HomeDir, ".glide") 548 } 549 550 // startup sets up the base environment. 551 // 552 // It does not assume the presence of a Glide.yaml file or vendor/ directory, 553 // so it can be used by any Glide command. 554 func startup(c *cli.Context) error { 555 action.Debug(c.Bool("debug")) 556 action.NoColor(c.Bool("no-color")) 557 action.Quiet(c.Bool("quiet")) 558 action.Init(c.String("yaml"), c.String("home")) 559 action.EnsureGoVendor() 560 return nil 561 } 562 563 // Get the path to the glide.yaml file. 564 // 565 // This returns the name of the path, even if the file does not exist. The value 566 // may be set by the user, or it may be the default. 567 func glidefile(c *cli.Context) string { 568 path := c.String("file") 569 if path == "" { 570 // For now, we construct a basic assumption. In the future, we could 571 // traverse backward to see if a glide.yaml exists in a parent. 572 path = "./glide.yaml" 573 } 574 a, err := filepath.Abs(path) 575 if err != nil { 576 // Underlying fs didn't provide working dir. 577 return path 578 } 579 return a 580 }