github.com/goguardian/glide@v0.0.0-20160311175917-84255172e124/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.NewInstaller()
   223  				inst.Force = c.Bool("force")
   224  				inst.UseCache = c.Bool("cache")
   225  				inst.UseGopath = c.Bool("use-gopath")
   226  				inst.UseCacheGopath = c.Bool("cache-gopath")
   227  				inst.UpdateVendored = c.Bool("update-vendored")
   228  				inst.ResolveAllFiles = c.Bool("all-dependencies")
   229  				packages := []string(c.Args())
   230  				insecure := c.Bool("insecure")
   231  				action.Get(packages, inst, insecure, c.Bool("no-recursive"))
   232  			},
   233  		},
   234  		{
   235  			Name:      "remove",
   236  			ShortName: "rm",
   237  			Usage:     "Remove a package from the glide.yaml file, and regenerate the lock file.",
   238  			Description: `This takes one or more package names, and removes references from the glide.yaml file.
   239  	This will rebuild the glide lock file with the following constraints:
   240  
   241  	- Dependencies are re-negotiated. Any that are no longer used are left out of the lock.
   242  	- Minor version re-nogotiation is performed on remaining dependencies.
   243  	- No updates are peformed. You may want to run 'glide up' to accomplish that.
   244  `,
   245  			Flags: []cli.Flag{
   246  				cli.BoolFlag{
   247  					Name:  "delete,d",
   248  					Usage: "Also delete from vendor/ any packages that are no longer used.",
   249  				},
   250  			},
   251  			Action: func(c *cli.Context) {
   252  				if len(c.Args()) < 1 {
   253  					fmt.Println("Oops! At least one package name is required.")
   254  					os.Exit(1)
   255  				}
   256  
   257  				if c.Bool("delete") {
   258  					// FIXME: Implement this in the installer.
   259  					fmt.Println("Delete is not currently implemented.")
   260  				}
   261  				inst := repo.NewInstaller()
   262  				inst.Force = c.Bool("force")
   263  				packages := []string(c.Args())
   264  				action.Remove(packages, inst)
   265  			},
   266  		},
   267  		{
   268  			Name:  "import",
   269  			Usage: "Import files from other dependency management systems.",
   270  			Subcommands: []cli.Command{
   271  				{
   272  					Name:  "godep",
   273  					Usage: "Import Godep's Godeps.json files and display the would-be yaml file",
   274  					Flags: []cli.Flag{
   275  						cli.StringFlag{
   276  							Name:  "file, f",
   277  							Usage: "Save all of the discovered dependencies to a Glide YAML file.",
   278  						},
   279  					},
   280  					Action: func(c *cli.Context) {
   281  						action.ImportGodep(c.String("file"))
   282  					},
   283  				},
   284  				{
   285  					Name:  "gpm",
   286  					Usage: "Import GPM's Godeps and Godeps-Git files and display the would-be yaml file",
   287  					Flags: []cli.Flag{
   288  						cli.StringFlag{
   289  							Name:  "file, f",
   290  							Usage: "Save all of the discovered dependencies to a Glide YAML file.",
   291  						},
   292  					},
   293  					Action: func(c *cli.Context) {
   294  						action.ImportGPM(c.String("file"))
   295  					},
   296  				},
   297  				{
   298  					Name:  "gb",
   299  					Usage: "Import gb's manifest file and display the would-be yaml file",
   300  					Flags: []cli.Flag{
   301  						cli.StringFlag{
   302  							Name:  "file, f",
   303  							Usage: "Save all of the discovered dependencies to a Glide YAML file.",
   304  						},
   305  					},
   306  					Action: func(c *cli.Context) {
   307  						action.ImportGB(c.String("file"))
   308  					},
   309  				},
   310  				{
   311  					Name:  "gom",
   312  					Usage: "Import Gomfile and display the would-be yaml file",
   313  					Flags: []cli.Flag{
   314  						cli.StringFlag{
   315  							Name:  "file, f",
   316  							Usage: "Save all of the discovered dependencies to a Glide YAML file.",
   317  						},
   318  					},
   319  					Action: func(c *cli.Context) {
   320  						action.ImportGom(c.String("file"))
   321  					},
   322  				},
   323  			},
   324  		},
   325  		{
   326  			Name:        "name",
   327  			Usage:       "Print the name of this project.",
   328  			Description: `Read the glide.yaml file and print the name given on the 'package' line.`,
   329  			Action: func(c *cli.Context) {
   330  				action.Name()
   331  			},
   332  		},
   333  		{
   334  			Name:      "novendor",
   335  			ShortName: "nv",
   336  			Usage:     "List all non-vendor paths in a directory.",
   337  			Description: `Given a directory, list all the relevant Go paths that are not vendored.
   338  
   339  Example:
   340  
   341  			$ go test $(glide novendor)
   342  `,
   343  			Flags: []cli.Flag{
   344  				cli.StringFlag{
   345  					Name:  "dir,d",
   346  					Usage: "Specify a directory to run novendor against.",
   347  					Value: ".",
   348  				},
   349  				cli.BoolFlag{
   350  					Name:  "no-subdir,x",
   351  					Usage: "Specify this to prevent nv from append '/...' to all directories.",
   352  				},
   353  			},
   354  			Action: func(c *cli.Context) {
   355  				action.NoVendor(c.String("dir"), true, !c.Bool("no-subdir"))
   356  			},
   357  		},
   358  		{
   359  			Name:  "rebuild",
   360  			Usage: "Rebuild ('go build') the dependencies",
   361  			Description: `This rebuilds the packages' '.a' files. On some systems
   362  	this can improve performance on subsequent 'go run' and 'go build' calls.`,
   363  			Action: func(c *cli.Context) {
   364  				action.Rebuild()
   365  			},
   366  		},
   367  		{
   368  			Name:      "install",
   369  			ShortName: "i",
   370  			Usage:     "Install a project's dependencies",
   371  			Description: `This uses the native VCS of each packages to install
   372  		the appropriate version. There are two ways a projects dependencies can
   373  	be installed. When there is a glide.yaml file defining the dependencies but
   374  	no lock file (glide.lock) the dependencies are installed using the "update"
   375  	command and a glide.lock file is generated pinning all dependencies. If a
   376  	glide.lock file is already present the dependencies are installed or updated
   377  	from the lock file.`,
   378  			Flags: []cli.Flag{
   379  				cli.BoolFlag{
   380  					Name:  "delete",
   381  					Usage: "Delete vendor packages not specified in config.",
   382  				},
   383  				cli.BoolFlag{
   384  					Name:  "force",
   385  					Usage: "If there was a change in the repo or VCS switch to new one. Warning: changes will be lost.",
   386  				},
   387  				cli.BoolFlag{
   388  					Name:  "update-vendored, u",
   389  					Usage: "Update vendored packages (without local VCS repo). Warning: this may destroy local modifications to vendor/.",
   390  				},
   391  				cli.StringFlag{
   392  					Name:  "file, f",
   393  					Usage: "Save all of the discovered dependencies to a Glide YAML file. (DEPRECATED: This has no impact.)",
   394  				},
   395  				cli.BoolFlag{
   396  					Name:  "cache",
   397  					Usage: "When downloading dependencies attempt to cache them.",
   398  				},
   399  				cli.BoolFlag{
   400  					Name:  "cache-gopath",
   401  					Usage: "When downloading dependencies attempt to put them in the GOPATH, too.",
   402  				},
   403  				cli.BoolFlag{
   404  					Name:  "use-gopath",
   405  					Usage: "Copy dependencies from the GOPATH if they exist there.",
   406  				},
   407  			},
   408  			Action: func(c *cli.Context) {
   409  				installer := repo.NewInstaller()
   410  				installer.Force = c.Bool("force")
   411  				installer.UseCache = c.Bool("cache")
   412  				installer.UseGopath = c.Bool("use-gopath")
   413  				installer.UseCacheGopath = c.Bool("cache-gopath")
   414  				installer.UpdateVendored = c.Bool("update-vendored")
   415  				installer.Home = gpath.Home()
   416  				installer.DeleteUnused = c.Bool("deleteOptIn")
   417  
   418  				action.Install(installer)
   419  			},
   420  		},
   421  		{
   422  			Name:      "update",
   423  			ShortName: "up",
   424  			Usage:     "Update a project's dependencies",
   425  			Description: `This uses the native VCS of each package to try to
   426  	pull the most applicable updates. Packages with fixed refs (Versions or
   427  	tags) will not be updated. Packages with no ref or with a branch ref will
   428  	be updated as expected.
   429  
   430  	If a dependency has a glide.yaml file, update will read that file and
   431  	update those dependencies accordingly. Those dependencies are maintained in
   432  	a the top level 'vendor/' directory. 'vendor/foo/bar' will have its
   433  	dependencies stored in 'vendor/'. This behavior can be disabled with
   434  	'--no-recursive'. When this behavior is skipped a glide.lock file is not
   435  	generated because the full dependency tree cannot be known.
   436  
   437  	Glide will also import Godep, GB, and GPM files as it finds them in dependencies.
   438  	It will create a glide.yaml file from the Godeps data, and then update. This
   439  	has no effect if '--no-recursive' is set.
   440  
   441  	If the '--update-vendored' flag (aliased to '-u') is present vendored
   442  	dependencies, stored in your projects VCS repository, will be updated. This
   443  	works by removing the old package, checking out an the repo and setting the
   444  	version, and removing the VCS directory.
   445  
   446  	By default, packages that are discovered are considered transient, and are
   447  	not stored in the glide.yaml file. The --file=NAME.yaml flag allows you
   448  	to save the discovered dependencies to a YAML file.
   449  	`,
   450  			Flags: []cli.Flag{
   451  				cli.BoolFlag{
   452  					Name:  "delete",
   453  					Usage: "Delete vendor packages not specified in config.",
   454  				},
   455  				cli.BoolFlag{
   456  					Name:  "no-recursive, quick",
   457  					Usage: "Disable updating dependencies' dependencies. Only update things in glide.yaml.",
   458  				},
   459  				cli.BoolFlag{
   460  					Name:  "force",
   461  					Usage: "If there was a change in the repo or VCS switch to new one. Warning, changes will be lost.",
   462  				},
   463  				cli.BoolFlag{
   464  					Name:  "all-dependencies",
   465  					Usage: "This will resolve all dependencies for all packages, not just those directly used.",
   466  				},
   467  				cli.BoolFlag{
   468  					Name:  "update-vendored, u",
   469  					Usage: "Update vendored packages (without local VCS repo). Warning, changes will be lost.",
   470  				},
   471  				cli.StringFlag{
   472  					Name:  "file, f",
   473  					Usage: "Save all of the discovered dependencies to a Glide YAML file.",
   474  				},
   475  				cli.BoolFlag{
   476  					Name:  "cache",
   477  					Usage: "When downloading dependencies attempt to cache them.",
   478  				},
   479  				cli.BoolFlag{
   480  					Name:  "cache-gopath",
   481  					Usage: "When downloading dependencies attempt to put them in the GOPATH, too.",
   482  				},
   483  				cli.BoolFlag{
   484  					Name:  "use-gopath",
   485  					Usage: "Copy dependencies from the GOPATH if they exist there.",
   486  				},
   487  				cli.BoolFlag{
   488  					Name:  "resolve-current",
   489  					Usage: "Resolve dependencies for only the current system rather than all build modes.",
   490  				},
   491  			},
   492  			Action: func(c *cli.Context) {
   493  
   494  				if c.Bool("resolve-current") {
   495  					util.ResolveCurrent = true
   496  					msg.Warn("Only resolving dependencies for the current OS/Arch")
   497  				}
   498  
   499  				installer := repo.NewInstaller()
   500  				installer.Force = c.Bool("force")
   501  				installer.UseCache = c.Bool("cache")
   502  				installer.UseGopath = c.Bool("use-gopath")
   503  				installer.UseCacheGopath = c.Bool("cache-gopath")
   504  				installer.UpdateVendored = c.Bool("update-vendored")
   505  				installer.ResolveAllFiles = c.Bool("all-dependencies")
   506  				installer.Home = gpath.Home()
   507  				installer.DeleteUnused = c.Bool("deleteOptIn")
   508  
   509  				action.Update(installer, c.Bool("no-recursive"))
   510  			},
   511  		},
   512  		{
   513  			Name:  "tree",
   514  			Usage: "Tree prints the dependencies of this project as a tree.",
   515  			Description: `This scans a project's source files and builds a tree
   516  	representation of the import graph.
   517  
   518  	It ignores testdata/ and directories that begin with . or _. Packages in
   519  	vendor/ are only included if they are referenced by the main project or
   520  	one of its dependencies.`,
   521  			Action: func(c *cli.Context) {
   522  				action.Tree(".", false)
   523  			},
   524  		},
   525  		{
   526  			Name:  "list",
   527  			Usage: "List prints all dependencies that the present code references.",
   528  			Description: `List scans your code and lists all of the packages that are used.
   529  
   530  			It does not use the glide.yaml. Instead, it inspects the code to determine what packages are
   531  			imported.
   532  
   533  			Directories that begin with . or _ are ignored, as are testdata directories. Packages in
   534  			vendor are only included if they are used by the project.
   535  			`,
   536  			Action: func(c *cli.Context) {
   537  				action.List(".", true)
   538  			},
   539  		},
   540  		{
   541  			Name:  "about",
   542  			Usage: "Learn about Glide",
   543  			Action: func(c *cli.Context) {
   544  				action.About()
   545  			},
   546  		},
   547  	}
   548  }
   549  
   550  func defaultGlideDir() string {
   551  	c, err := user.Current()
   552  	if err != nil {
   553  		return ""
   554  	}
   555  	return filepath.Join(c.HomeDir, ".glide")
   556  }
   557  
   558  // startup sets up the base environment.
   559  //
   560  // It does not assume the presence of a Glide.yaml file or vendor/ directory,
   561  // so it can be used by any Glide command.
   562  func startup(c *cli.Context) error {
   563  	action.Debug(c.Bool("debug"))
   564  	action.NoColor(c.Bool("no-color"))
   565  	action.Quiet(c.Bool("quiet"))
   566  	action.Init(c.String("yaml"), c.String("home"))
   567  	action.EnsureGoVendor()
   568  	return nil
   569  }
   570  
   571  // Get the path to the glide.yaml file.
   572  //
   573  // This returns the name of the path, even if the file does not exist. The value
   574  // may be set by the user, or it may be the default.
   575  func glidefile(c *cli.Context) string {
   576  	path := c.String("file")
   577  	if path == "" {
   578  		// For now, we construct a basic assumption. In the future, we could
   579  		// traverse backward to see if a glide.yaml exists in a parent.
   580  		path = "./glide.yaml"
   581  	}
   582  	a, err := filepath.Abs(path)
   583  	if err != nil {
   584  		// Underlying fs didn't provide working dir.
   585  		return path
   586  	}
   587  	return a
   588  }