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  }